From 01f009154d2ed9e38f3b241f85666513940601fe Mon Sep 17 00:00:00 2001 From: Jack McCracken Date: Wed, 4 May 2022 15:37:38 -0400 Subject: [PATCH] Add trusted types directive --- .../headers/content_security_policy_config.rb | 2 ++ .../headers/policy_management.rb | 30 ++++++++++++++++++- .../headers/content_security_policy_spec.rb | 14 +++++++++ .../headers/policy_management_spec.rb | 2 ++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/secure_headers/headers/content_security_policy_config.rb b/lib/secure_headers/headers/content_security_policy_config.rb index 3c477161..5d3c7550 100644 --- a/lib/secure_headers/headers/content_security_policy_config.rb +++ b/lib/secure_headers/headers/content_security_policy_config.rb @@ -35,6 +35,7 @@ def initialize(hash) @report_only = nil @report_uri = nil @require_sri_for = nil + @require_trusted_types_for = nil @sandbox = nil @script_nonce = nil @script_src = nil @@ -44,6 +45,7 @@ def initialize(hash) @style_src = nil @style_src_elem = nil @style_src_attr = nil + @trusted_types = nil @worker_src = nil @upgrade_insecure_requests = nil @disable_nonce_backwards_compatibility = nil diff --git a/lib/secure_headers/headers/policy_management.rb b/lib/secure_headers/headers/policy_management.rb index b81f3b0e..99825443 100644 --- a/lib/secure_headers/headers/policy_management.rb +++ b/lib/secure_headers/headers/policy_management.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "set" +require 'byebug' module SecureHeaders module PolicyManagement @@ -98,7 +99,19 @@ def self.included(base) STYLE_SRC_ATTR ].flatten.freeze - ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0).uniq.sort + # Experimental directives - these vary greatly in support + # See MDN for details. + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types + TRUSTED_TYPES = :trusted_types + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for + REQUIRE_TRUSTED_TYPES_FOR = :require_trusted_types_for + + DIRECTIVES_EXPERIMENTAL = [ + TRUSTED_TYPES, + REQUIRE_TRUSTED_TYPES_FOR, + ].flatten.freeze + + ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_EXPERIMENTAL).uniq.sort # Think of default-src and report-uri as the beginning and end respectively, # everything else is in between. @@ -121,6 +134,7 @@ def self.included(base) OBJECT_SRC => :source_list, PLUGIN_TYPES => :media_type_list, REQUIRE_SRI_FOR => :require_sri_for_list, + REQUIRE_TRUSTED_TYPES_FOR => :require_trusted_types_for_list, REPORT_URI => :source_list, PREFETCH_SRC => :source_list, SANDBOX => :sandbox_list, @@ -130,6 +144,7 @@ def self.included(base) STYLE_SRC => :source_list, STYLE_SRC_ELEM => :source_list, STYLE_SRC_ATTR => :source_list, + TRUSTED_TYPES => :source_list, WORKER_SRC => :source_list, UPGRADE_INSECURE_REQUESTS => :boolean, }.freeze @@ -175,6 +190,7 @@ def self.included(base) ].freeze REQUIRE_SRI_FOR_VALUES = Set.new(%w(script style)) + REQUIRE_TRUSTED_TYPES_FOR_VALUES = Set.new(%w(script)) module ClassMethods # Public: generate a header name, value array that is user-agent-aware. @@ -324,6 +340,8 @@ def validate_directive!(directive, value) validate_media_type_expression!(directive, value) when :require_sri_for_list validate_require_sri_source_expression!(directive, value) + when :require_trusted_types_for_list + validate_require_trusted_types_for_source_expression!(directive, value) else raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}") end @@ -368,6 +386,16 @@ def validate_require_sri_source_expression!(directive, require_sri_for_expressio end end + # Private: validates that a require trusted types for expression: + # 1. is an array of strings + # 2. is a subset of ["script"] + def validate_require_trusted_types_for_source_expression!(directive, require_trusted_types_for_expression) + ensure_array_of_strings!(directive, require_trusted_types_for_expression) + unless require_trusted_types_for_expression.to_set.subset?(REQUIRE_TRUSTED_TYPES_FOR_VALUES) + raise ContentSecurityPolicyConfigError.new(%(require-sri for must be a subset of #{REQUIRE_TRUSTED_TYPES_FOR_VALUES.to_a} but was #{require_trusted_types_for_expression})) + end + end + # Private: validates that a source expression: # 1. is an array of strings # 2. does not contain any deprecated, now invalid values (inline, eval, self, none) diff --git a/spec/lib/secure_headers/headers/content_security_policy_spec.rb b/spec/lib/secure_headers/headers/content_security_policy_spec.rb index c080283d..2b2ba4d5 100644 --- a/spec/lib/secure_headers/headers/content_security_policy_spec.rb +++ b/spec/lib/secure_headers/headers/content_security_policy_spec.rb @@ -141,6 +141,15 @@ module SecureHeaders expect(csp.value).to eq("default-src 'self'; require-sri-for script style") end + it "supports require-trusted-types-for directive" do + csp = ContentSecurityPolicy.new({require_trusted_types_for: %(script)}) + expect(csp.value).to eq("require-trusted-types-for script") + end + + it "does not support style for require-trusted-types-for directive" do + expect { ContentSecurityPolicy.new({require_trusted_types_for: %(script style)}) }.to raise_error(ContentSecurityPolicyConfigError) + end + it "includes prefetch-src" do csp = ContentSecurityPolicy.new(default_src: %w('self'), prefetch_src: %w(foo.com)) expect(csp.value).to eq("default-src 'self'; prefetch-src foo.com") @@ -180,6 +189,11 @@ module SecureHeaders csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')}) expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'") end + + it "supports trusted-types directive" do + csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy)}) + expect(csp.value).to eq("trusted-types blahblahpolicy") + end end end end diff --git a/spec/lib/secure_headers/headers/policy_management_spec.rb b/spec/lib/secure_headers/headers/policy_management_spec.rb index 1a9ac4eb..150e0b35 100644 --- a/spec/lib/secure_headers/headers/policy_management_spec.rb +++ b/spec/lib/secure_headers/headers/policy_management_spec.rb @@ -45,6 +45,7 @@ module SecureHeaders plugin_types: %w(application/x-shockwave-flash), prefetch_src: %w(fetch.com), require_sri_for: %w(script style), + require_trusted_types_for: %w(script), script_src: %w('self'), style_src: %w('unsafe-inline'), upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/ @@ -53,6 +54,7 @@ module SecureHeaders script_src_attr: %w(example.com), style_src_elem: %w(example.com), style_src_attr: %w(example.com), + trusted_types: %w(abcpolicy), report_uri: %w(https://example.com/uri-directive), }