Skip to content

Instantly share code, notes, and snippets.

@cheeyeo
Last active September 19, 2015 20:21
Show Gist options
  • Save cheeyeo/fc41b675d3036d477740 to your computer and use it in GitHub Desktop.
Save cheeyeo/fc41b675d3036d477740 to your computer and use it in GitHub Desktop.
CSP
module GitHub
module CSP
# Public: Constants for CSP keywords
NONE = "'none'".freeze
SELF = "'self'".freeze
UNSAFE_INLINE = "'unsafe-inline'".freeze
UNSAFE_EVAL = "'unsafe-eval'".freeze
# Public: Constants for CSP directive names
BASE_URI = "base-uri".freeze
CHILD_SRC = "child-src".freeze
CONNECT_SRC = "connect-src".freeze
DEFAULT_SRC = "default-src".freeze
FONT_SRC = "font-src".freeze
FORM_ACTION = "form-action".freeze
FRAME_ANCESTORS = "frame-ancestors".freeze
FRAME_SRC = "frame-src".freeze
IMG_SRC = "img-src".freeze
MEDIA_SRC = "media-src".freeze
OBJECT_SRC = "object-src".freeze
PLUGIN_TYPES = "plugin-types".freeze
REFERRER = "referrer".freeze
REFLECTED_XSS = "reflected-xss".freeze
REPORT_URI = "report-uri".freeze
SANDBOX = "sandbox".freeze
SCRIPT_SRC = "script-src".freeze
STYLE_SRC = "style-src".freeze
# Internal: Mapping of valid directive names to the type of values.
DIRECTIVE_VALUE_TYPES = {
BASE_URI => :source_list,
CHILD_SRC => :source_list,
CONNECT_SRC => :source_list,
DEFAULT_SRC => :source_list,
FONT_SRC => :source_list,
FORM_ACTION => :source_list,
FRAME_ANCESTORS => :source_list,
FRAME_SRC => :source_list,
IMG_SRC => :source_list,
MEDIA_SRC => :source_list,
OBJECT_SRC => :source_list,
PLUGIN_TYPES => :media_type_list,
REFERRER => :string,
REFLECTED_XSS => :string,
REPORT_URI => :uri,
SANDBOX => :string,
SCRIPT_SRC => :source_list,
STYLE_SRC => :source_list
}.freeze
# Public: Convert CSP policy structure into a String.
#
# policy - Array of directives where [directive_name, directive_value]
# directive_name - String name
# directive_value - String or Array
#
# Returns a String or raises a TypeError if the policy has unknown
# directives.
def self.stringify(policy)
policy.map { |name, value|
unless directive_type = DIRECTIVE_VALUE_TYPES[name]
raise TypeError, "unknown directive name: #{name.inspect}"
end
case directive_type
when :string, :uri
[name, value].compact.join(" ")
when :source_list, :media_type_list
([name] + value).compact.join(" ")
else
raise TypeError, "unknown directive type: #{directive_type.inspect}"
end
}.join("; ")
end
# Internal: Minify CSP policy.
#
# Removes duplicate sources.
# Removes sources that already match an existing wild card.
#
# policy - Array of directives. See `stringify`.
#
# Returns Array policy.
def self.minify(policy)
policy.map do |name, value|
if DIRECTIVE_VALUE_TYPES[name] == :source_list
sources = dedup_source_list(value.compact)
sources = sources.map { |source| strip_source_scheme(source) }
[name, sources]
else
[name, value]
end
end
end
# Internal: Strips scheme from URI source.
#
# Sources without a scheme default to the page's scheme. This saves some
# bytes in our header.
#
# source - String
#
# Returns source String.
def self.strip_source_scheme(source)
source.sub(/^https?:\/\//, "")
end
# Internal: Dedup source list values.
#
# Removes duplicate sources.
# Removes sources that already match an existing wild card.
#
# sources - Array of String sources.
#
# Returns Array of sources.
def self.dedup_source_list(sources)
sources = sources.uniq
wild_sources = sources.select { |source| source =~ /\*/ }
if wild_sources.any?
sources.reject do |source|
!wild_sources.include?(source) &&
wild_sources.any? { |pattern| File.fnmatch(pattern, source) }
end
else
sources
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment