11# frozen_string_literal: true
2+
3+ require "set"
4+
25module SecureHeaders
36 module PolicyManagement
47 def self . included ( base )
@@ -70,13 +73,19 @@ def self.included(base)
7073 # https://w3c.github.io/webappsec/specs/CSP2/
7174 BLOCK_ALL_MIXED_CONTENT = :block_all_mixed_content
7275 MANIFEST_SRC = :manifest_src
76+ NAVIGATE_TO = :navigate_to
77+ PREFETCH_SRC = :prefetch_src
78+ REQUIRE_SRI_FOR = :require_sri_for
7379 UPGRADE_INSECURE_REQUESTS = :upgrade_insecure_requests
7480 WORKER_SRC = :worker_src
7581
7682 DIRECTIVES_3_0 = [
7783 DIRECTIVES_2_0 ,
7884 BLOCK_ALL_MIXED_CONTENT ,
7985 MANIFEST_SRC ,
86+ NAVIGATE_TO ,
87+ PREFETCH_SRC ,
88+ REQUIRE_SRI_FOR ,
8089 WORKER_SRC ,
8190 UPGRADE_INSECURE_REQUESTS
8291 ] . flatten . freeze
@@ -100,14 +109,17 @@ def self.included(base)
100109 IMG_SRC => :source_list ,
101110 MANIFEST_SRC => :source_list ,
102111 MEDIA_SRC => :source_list ,
112+ NAVIGATE_TO => :source_list ,
103113 OBJECT_SRC => :source_list ,
104114 PLUGIN_TYPES => :media_type_list ,
115+ REQUIRE_SRI_FOR => :require_sri_for_list ,
105116 REPORT_URI => :source_list ,
117+ PREFETCH_SRC => :source_list ,
106118 SANDBOX => :sandbox_list ,
107119 SCRIPT_SRC => :source_list ,
108120 STYLE_SRC => :source_list ,
109121 WORKER_SRC => :source_list ,
110- UPGRADE_INSECURE_REQUESTS => :boolean
122+ UPGRADE_INSECURE_REQUESTS => :boolean ,
111123 } . freeze
112124
113125 # These are directives that don't have use a source list, and hence do not
@@ -122,7 +134,8 @@ def self.included(base)
122134 BASE_URI ,
123135 FORM_ACTION ,
124136 FRAME_ANCESTORS ,
125- REPORT_URI
137+ NAVIGATE_TO ,
138+ REPORT_URI ,
126139 ]
127140
128141 FETCH_SOURCES = ALL_DIRECTIVES - NON_FETCH_SOURCES - NON_SOURCE_LIST_SOURCES
@@ -148,6 +161,8 @@ def self.included(base)
148161 :style_nonce
149162 ] . freeze
150163
164+ REQUIRE_SRI_FOR_VALUES = Set . new ( %w( script style ) )
165+
151166 module ClassMethods
152167 # Public: generate a header name, value array that is user-agent-aware.
153168 #
@@ -241,7 +256,8 @@ def merge_policy_additions(original, additions)
241256 def list_directive? ( directive )
242257 source_list? ( directive ) ||
243258 sandbox_list? ( directive ) ||
244- media_type_list? ( directive )
259+ media_type_list? ( directive ) ||
260+ require_sri_for_list? ( directive )
245261 end
246262
247263 # For each directive in additions that does not exist in the original config,
@@ -274,11 +290,17 @@ def media_type_list?(directive)
274290 DIRECTIVE_VALUE_TYPES [ directive ] == :media_type_list
275291 end
276292
293+ def require_sri_for_list? ( directive )
294+ DIRECTIVE_VALUE_TYPES [ directive ] == :require_sri_for_list
295+ end
296+
277297 # Private: Validates that the configuration has a valid type, or that it is a valid
278298 # source expression.
279299 def validate_directive! ( directive , value )
280300 ensure_valid_directive! ( directive )
281301 case ContentSecurityPolicy ::DIRECTIVE_VALUE_TYPES [ directive ]
302+ when :source_list
303+ validate_source_expression! ( directive , value )
282304 when :boolean
283305 unless boolean? ( value )
284306 raise ContentSecurityPolicyConfigError . new ( "#{ directive } must be a boolean. Found #{ value . class } value" )
@@ -287,8 +309,8 @@ def validate_directive!(directive, value)
287309 validate_sandbox_expression! ( directive , value )
288310 when :media_type_list
289311 validate_media_type_expression! ( directive , value )
290- when :source_list
291- validate_source_expression !( directive , value )
312+ when :require_sri_for_list
313+ validate_require_sri_source_expression !( directive , value )
292314 else
293315 raise ContentSecurityPolicyConfigError . new ( "Unknown directive #{ directive } " )
294316 end
@@ -323,6 +345,16 @@ def validate_media_type_expression!(directive, media_type_expression)
323345 end
324346 end
325347
348+ # Private: validates that a require sri for expression:
349+ # 1. is an array of strings
350+ # 2. is a subset of ["string", "style"]
351+ def validate_require_sri_source_expression! ( directive , require_sri_for_expression )
352+ ensure_array_of_strings! ( directive , require_sri_for_expression )
353+ unless require_sri_for_expression . to_set . subset? ( REQUIRE_SRI_FOR_VALUES )
354+ raise ContentSecurityPolicyConfigError . new ( %(require-sri for must be a subset of #{ REQUIRE_SRI_FOR_VALUES . to_a } but was #{ require_sri_for_expression } ) )
355+ end
356+ end
357+
326358 # Private: validates that a source expression:
327359 # 1. is an array of strings
328360 # 2. does not contain any deprecated, now invalid values (inline, eval, self, none)
0 commit comments