Skip to content

Commit 4b07344

Browse files
committed
Add report-to CSP directive
1 parent bac6dae commit 4b07344

File tree

5 files changed

+20
-8
lines changed

5 files changed

+20
-8
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@ SecureHeaders::Configuration.default do |config|
8181
style_src_attr: %w('unsafe-inline'),
8282
worker_src: %w('self'),
8383
upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
84-
report_uri: %w(https://report-uri.io/example-csp)
84+
report_uri: %w(https://report-uri.io/example-csp),
85+
report_to: %w(example-csp)
8586
}
8687
# This is available only from 3.5.0; use the `report_only: true` setting for 3.4.1 and below.
8788
config.csp_report_only = config.csp.merge({
8889
img_src: %w(somewhereelse.com),
89-
report_uri: %w(https://report-uri.io/example-csp-report-only)
90+
report_uri: %w(https://report-uri.io/example-csp-report-only),
91+
report_to: %w(example-csp-report-only)
9092
})
9193
end
9294
```

lib/secure_headers/headers/content_security_policy.rb

+4-3
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def value
4646
private
4747

4848
# Private: converts the config object into a string representing a policy.
49-
# Places default-src at the first directive and report-uri as the last. All
50-
# others are presented in alphabetical order.
49+
# Places default-src at the first directive, report-uri second to last and report-to as the
50+
# last. All others are presented in alphabetical order.
5151
#
5252
# Returns a content security policy header value.
5353
def build_value
@@ -130,7 +130,7 @@ def minify_source_list(directive, source_list)
130130
source_list = populate_nonces(directive, source_list)
131131
source_list = reject_all_values_if_none(source_list)
132132

133-
unless directive == REPORT_URI || @preserve_schemes
133+
unless [REPORT_URI, REPORT_TO].include?(directive) || @preserve_schemes
134134
source_list = strip_source_schemes(source_list)
135135
end
136136
source_list.uniq
@@ -185,6 +185,7 @@ def directives
185185
DEFAULT_SRC,
186186
BODY_DIRECTIVES,
187187
REPORT_URI,
188+
REPORT_TO
188189
].flatten
189190
end
190191

lib/secure_headers/headers/policy_management.rb

+6-3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def self.included(base)
8181
SCRIPT_SRC_ATTR = :script_src_attr
8282
STYLE_SRC_ELEM = :style_src_elem
8383
STYLE_SRC_ATTR = :style_src_attr
84+
REPORT_TO = :report_to
8485

8586
DIRECTIVES_3_0 = [
8687
DIRECTIVES_2_0,
@@ -93,7 +94,8 @@ def self.included(base)
9394
SCRIPT_SRC_ELEM,
9495
SCRIPT_SRC_ATTR,
9596
STYLE_SRC_ELEM,
96-
STYLE_SRC_ATTR
97+
STYLE_SRC_ATTR,
98+
REPORT_TO
9799
].flatten.freeze
98100

99101
# Experimental directives - these vary greatly in support
@@ -110,9 +112,9 @@ def self.included(base)
110112

111113
ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_EXPERIMENTAL).uniq.sort
112114

113-
# Think of default-src and report-uri as the beginning and end respectively,
115+
# Think of default-src as the beginning, report-uri and report-to as the end,
114116
# everything else is in between.
115-
BODY_DIRECTIVES = ALL_DIRECTIVES - [DEFAULT_SRC, REPORT_URI]
117+
BODY_DIRECTIVES = ALL_DIRECTIVES - [DEFAULT_SRC, REPORT_URI, REPORT_TO]
116118

117119
DIRECTIVE_VALUE_TYPES = {
118120
BASE_URI => :source_list,
@@ -131,6 +133,7 @@ def self.included(base)
131133
PLUGIN_TYPES => :media_type_list,
132134
REQUIRE_SRI_FOR => :require_sri_for_list,
133135
REQUIRE_TRUSTED_TYPES_FOR => :require_trusted_types_for_list,
136+
REPORT_TO => :source_list,
134137
REPORT_URI => :source_list,
135138
PREFETCH_SRC => :source_list,
136139
SANDBOX => :sandbox_list,

spec/lib/secure_headers/headers/content_security_policy_spec.rb

+5
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ module SecureHeaders
7171
expect(csp.value).to eq("default-src https:; report-uri https://example.org")
7272
end
7373

74+
it "does not remove schemes from report-to values" do
75+
csp = ContentSecurityPolicy.new(default_src: %w(https:), report_to: %w(https://example.org))
76+
expect(csp.value).to eq("default-src https:; report-to https://example.org")
77+
end
78+
7479
it "does not remove schemes when :preserve_schemes is true" do
7580
csp = ContentSecurityPolicy.new(default_src: %w(https://example.org), preserve_schemes: true)
7681
expect(csp.value).to eq("default-src https://example.org")

spec/lib/secure_headers/headers/policy_management_spec.rb

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ module SecureHeaders
5555
style_src_attr: %w(example.com),
5656
trusted_types: %w(abcpolicy),
5757

58+
report_to: %w(https://example.com/uri-directive),
5859
report_uri: %w(https://example.com/uri-directive),
5960
}
6061

0 commit comments

Comments
 (0)