mirror of
https://github.com/discourse/discourse.git
synced 2024-11-29 07:33:38 +08:00
174a8b431b
Raw paths like `/test/path` are not supported natively in the CSP. This commit prepends the site's base URL to these paths. This allows plugins to add 'local' assets to the CSP without needing to hardcode the site's hostname.
89 lines
1.9 KiB
Ruby
89 lines
1.9 KiB
Ruby
# frozen_string_literal: true
|
|
require 'content_security_policy/default'
|
|
|
|
class ContentSecurityPolicy
|
|
class Builder
|
|
EXTENDABLE_DIRECTIVES = %i[
|
|
base_uri
|
|
frame_ancestors
|
|
manifest_src
|
|
object_src
|
|
script_src
|
|
worker_src
|
|
].freeze
|
|
|
|
# Make extending these directives no-op, until core includes them in default CSP
|
|
TO_BE_EXTENDABLE = %i[
|
|
connect_src
|
|
default_src
|
|
font_src
|
|
form_action
|
|
frame_src
|
|
img_src
|
|
media_src
|
|
prefetch_src
|
|
style_src
|
|
].freeze
|
|
|
|
def initialize(base_url:)
|
|
@directives = Default.new(base_url: base_url).directives
|
|
@base_url = base_url
|
|
end
|
|
|
|
def <<(extension)
|
|
return unless valid_extension?(extension)
|
|
|
|
extension.each { |directive, sources| extend_directive(normalize_directive(directive), sources) }
|
|
end
|
|
|
|
def build
|
|
policy = ActionDispatch::ContentSecurityPolicy.new
|
|
|
|
@directives.each do |directive, sources|
|
|
if sources.is_a?(Array)
|
|
policy.public_send(directive, *sources)
|
|
else
|
|
policy.public_send(directive, sources)
|
|
end
|
|
end
|
|
|
|
policy.build
|
|
end
|
|
|
|
private
|
|
|
|
def normalize_directive(directive)
|
|
directive.to_s.gsub('-', '_').to_sym
|
|
end
|
|
|
|
def normalize_source(source)
|
|
if source.starts_with?("/")
|
|
"#{@base_url}#{source}"
|
|
else
|
|
source
|
|
end
|
|
rescue URI::ParseError
|
|
source
|
|
end
|
|
|
|
def extend_directive(directive, sources)
|
|
return unless extendable?(directive)
|
|
|
|
@directives[directive] ||= []
|
|
|
|
sources = Array(sources).map { |s| normalize_source(s) }
|
|
@directives[directive].concat(sources)
|
|
|
|
@directives[directive].delete(:none) if @directives[directive].count > 1
|
|
end
|
|
|
|
def extendable?(directive)
|
|
EXTENDABLE_DIRECTIVES.include?(directive)
|
|
end
|
|
|
|
def valid_extension?(extension)
|
|
extension.is_a?(Hash)
|
|
end
|
|
end
|
|
end
|