Skip to content

Commit 0beac7b

Browse files
committed
Disallow bare https: in CSP (ref #810, #325)
1 parent 98d6073 commit 0beac7b

File tree

2 files changed

+19
-9
lines changed

2 files changed

+19
-9
lines changed

checks/tasks/http_headers.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def __init__(self):
6464
self.has_unsafe_eval = False
6565
self.has_unsafe_hashes = False
6666
self.has_http = False
67+
self.has_bare_https = False
6768
self.has_data = False
6869
self.has_base_uri = False
6970
self.has_form_action = False
@@ -78,6 +79,7 @@ def failures(self):
7879
"has_unsafe_inline",
7980
"has_unsafe_eval",
8081
"has_http",
82+
"has_bare_https",
8183
"has_data",
8284
"has_invalid_host",
8385
"has_unsafe_hashes",
@@ -110,6 +112,7 @@ def __str__(self):
110112
f"has_unsafe_eval: {self.has_unsafe_eval}\n"
111113
f"has_unsafe_hashes: {self.has_unsafe_hashes}\n"
112114
f"has_http: {self.has_http}\n"
115+
f"has_bare_https: {self.has_bare_https}\n"
113116
f"has_data: {self.has_data}\n"
114117
f"has_base_uri: {self.has_base_uri}\n"
115118
f"has_form_action: {self.has_form_action}\n"
@@ -125,7 +128,7 @@ def __str__(self):
125128
host_source_regex = re.compile(
126129
r"^(?:(?P<scheme>.+)://)?" r"(?P<host>[^:/']+|\[.+\])" r"(?::(?P<port>\d+|\*))?" r"(?P<path>\/.*)?$"
127130
)
128-
scheme_source_regex = re.compile(r"^(?P<scheme>https?|data|mediastream|blob|filesystem):$")
131+
scheme_source_regex = re.compile(r"^(?P<scheme_source>https?|data|mediastream|blob|filesystem):$")
129132
self_none_regex = re.compile(r"^(?:(?P<self>'self')|(?P<none>'none'))$")
130133
other_source_regex = re.compile(
131134
r"(?:"
@@ -356,8 +359,6 @@ def _check_none_self_similar(self, domain, directive: str):
356359
found_self = True
357360
elif "report_sample" in match.groupdict() and match.group("report_sample"):
358361
expected_sources += 1
359-
elif "scheme" in match.groupdict() and match.group("scheme") and match.string == "https:":
360-
expected_sources += 1
361362
elif "host" in match.groupdict() and match.group("host"):
362363
expected_sources += 1
363364
host = match.group("host").rstrip(".")
@@ -392,9 +393,10 @@ def _check_none_self_similar(self, domain, directive: str):
392393
return False
393394

394395
def _verdict(self, domain):
395-
self.result.has_http = self._check_matched_for_groups(dict(scheme=["http", "*"]))
396+
self.result.has_http = self._check_matched_for_groups(dict(scheme=["http", "*"], scheme_source=["http", "*"]))
397+
self.result.has_bare_https = self._check_matched_for_groups(dict(scheme_source=["https"]))
396398
self.result.has_data = self._check_matched_for_groups(
397-
dict(scheme=["data", "*"]), directives=["object-src", "script-src"]
399+
dict(scheme_source=["data", "*"]), directives=["object-src", "script-src"]
398400
)
399401
self.result.has_invalid_host = self._check_matched_for_groups(dict(host=["*", "127.0.0.1"]))
400402
self.result.has_unsafe_inline = self._check_matched_for_groups(dict(unsafe_inline=[]))

tests/unittests/test_tasks_http_headers.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def test_default_src_1(self):
101101

102102
def test_default_src_2(self):
103103
headers = "form-action 'none'; base-uri 'none'; default-src 'self' https:; frame-ancestors 'self'"
104-
self._is_good(headers)
104+
self._is_bad(headers)
105105

106106
def test_default_src_3(self):
107107
headers = "form-action 'none'; base-uri 'none'; default-src 'self' 'report_sample'; frame-ancestors 'self'"
@@ -164,9 +164,13 @@ def test_http_2(self):
164164
headers = self.base_policy + "frame-ancestors 'self', style-src http:"
165165
self._is_bad(headers)
166166

167+
def test_http_3(self):
168+
headers = self.base_policy + "frame-ancestors 'self', style-src http://*"
169+
self._is_bad(headers)
170+
167171
def test_https_1(self):
168172
headers = self.base_policy + "frame-ancestors 'self', style-src https:"
169-
self._is_good(headers)
173+
self._is_bad(headers)
170174

171175
def test_https_2(self):
172176
headers = self.base_policy + "frame-ancestors 'self', style-src https://whatever.com:443/afsdf"
@@ -176,6 +180,10 @@ def test_https_3(self):
176180
headers = self.base_policy + "frame-ancestors 'self', style-src https://[ipv6:address]:443/afsdf"
177181
self._is_good(headers)
178182

183+
def test_https_4(self):
184+
headers = "form-action 'none'; base-uri 'self' https:; default-src 'self'; frame-ancestors 'self'"
185+
self._is_bad(headers)
186+
179187
def test_star_for_port(self):
180188
headers = self.base_policy + "frame-ancestors 'self', style-src https://[ipv6:address]:*/afsdf"
181189
self._is_good(headers)
@@ -281,7 +289,7 @@ def test_frame_source_domain(self):
281289
self._is_good(headers)
282290

283291
def test_two_headers(self):
284-
headers = self.base_policy + "frame-ancestors https:, frame-ancestors 'none'"
292+
headers = self.base_policy + "frame-ancestors 'self', frame-ancestors 'none'"
285293
self._is_good(headers)
286294

287295
def test_syntax_trusted_types_1(self):
@@ -338,7 +346,7 @@ def test_host_source_3(self):
338346

339347
def test_scheme_source_1(self):
340348
headers = self.base_policy + "frame-ancestors 'none', style-src https:"
341-
self._is_good_and_parsed(headers, "style-src")
349+
self._is_bad_and_parsed(headers, "style-src")
342350

343351
def test_scheme_source_2(self):
344352
headers = self.base_policy + "frame-ancestors 'none', style-src http:"

0 commit comments

Comments
 (0)