fix(csp): always include internal CDN in script-src/connect-src and update tests accordingly

See ChatGPT conversation: https://chatgpt.com/share/68b492b8-847c-800f-82a9-fb890d4add7f
This commit is contained in:
2025-08-31 20:22:05 +02:00
parent 5f66c1a622
commit aa2eb53776
2 changed files with 20 additions and 23 deletions

View File

@@ -139,8 +139,7 @@ class FilterModule(object):
if matomo_domain: if matomo_domain:
tokens.append(f"{web_protocol}://{matomo_domain}") tokens.append(f"{web_protocol}://{matomo_domain}")
# Allow the loading of js from the cdn # Allow fetching from internal CDN as default for all applications
if self.is_feature_enabled(applications, 'logout', application_id) or self.is_feature_enabled(applications, 'desktop', application_id):
domain = domains.get('web-svc-cdn')[0] domain = domains.get('web-svc-cdn')[0]
tokens.append(f"{web_protocol}://{domain}") tokens.append(f"{web_protocol}://{domain}")

View File

@@ -85,18 +85,16 @@ class TestCspFilters(unittest.TestCase):
header = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https') header = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https')
# Ensure core directives are present # Ensure core directives are present
self.assertIn("default-src 'self';", header) self.assertIn("default-src 'self';", header)
# script-src directive should include unsafe-eval, Matomo domain and CDN (hash may follow) # script-src-elem should include 'self', Matomo, internes CDN und explizite Whitelist-CDN
self.assertIn("script-src-elem 'self'", header)
self.assertIn("https://matomo.example.org", header)
self.assertIn("https://cdn.example.org", header) # internes CDN
self.assertIn("https://cdn.example.com", header) # Whitelist
# script-src directive should include unsafe-eval
self.assertIn("script-src 'self' 'unsafe-eval'", header)
# connect-src directive
self.assertIn( self.assertIn(
"script-src-elem 'self' https://matomo.example.org https://cdn.example.com", "connect-src 'self' https://matomo.example.org https://cdn.example.org https://api.example.com;",
header
)
self.assertIn(
"script-src 'self' 'unsafe-eval'",
header
)
# connect-src directive unchanged (no inline hash)
self.assertIn(
"connect-src 'self' https://matomo.example.org https://api.example.com;",
header header
) )
# ends with img-src # ends with img-src
@@ -106,8 +104,9 @@ class TestCspFilters(unittest.TestCase):
header = self.filter.build_csp_header(self.apps, 'app2', self.domains) header = self.filter.build_csp_header(self.apps, 'app2', self.domains)
# default-src only contains 'self' # default-src only contains 'self'
self.assertIn("default-src 'self';", header) self.assertIn("default-src 'self';", header)
# no external URLs self.assertIn('https://cdn.example.org', header)
self.assertNotIn('http', header) self.assertNotIn('matomo.example.org', header)
self.assertNotIn('www.google.com', header)
# ends with img-src # ends with img-src
self.assertTrue(header.strip().endswith('img-src * data: blob:;')) self.assertTrue(header.strip().endswith('img-src * data: blob:;'))
@@ -154,7 +153,6 @@ class TestCspFilters(unittest.TestCase):
style_hash = self.filter.get_csp_hash("body { background: #fff; }") style_hash = self.filter.get_csp_hash("body { background: #fff; }")
self.assertNotIn(style_hash, header) self.assertNotIn(style_hash, header)
def test_build_csp_header_recaptcha_toggle(self): def test_build_csp_header_recaptcha_toggle(self):
""" """
When the 'recaptcha' feature is enabled, 'https://www.google.com' When the 'recaptcha' feature is enabled, 'https://www.google.com'
@@ -185,7 +183,7 @@ class TestCspFilters(unittest.TestCase):
self.domains['web-app-desktop'] = ['domain-example.com'] self.domains['web-app-desktop'] = ['domain-example.com']
header = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https') header = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https')
# Expect '*.domain-example.com' in the frame-ancestors directive # Expect 'domain-example.com' in the frame-ancestors directive
self.assertRegex( self.assertRegex(
header, header,
r"frame-ancestors\s+'self'\s+domain-example\.com;" r"frame-ancestors\s+'self'\s+domain-example\.com;"
@@ -194,8 +192,8 @@ class TestCspFilters(unittest.TestCase):
# Now disable the feature and rebuild # Now disable the feature and rebuild
self.apps['app1']['features']['desktop'] = False self.apps['app1']['features']['desktop'] = False
header_no = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https') header_no = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https')
# Should no longer contain the wildcarded sld.tld # Should no longer contain the SLD+TLD
self.assertNotIn("*.domain-example.com", header_no) self.assertNotIn("domain-example.com", header_no)
if __name__ == '__main__': if __name__ == '__main__':