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,10 +139,9 @@ class FilterModule(object):
if matomo_domain:
tokens.append(f"{web_protocol}://{matomo_domain}")
# Allow the loading of js from the cdn
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]
tokens.append(f"{web_protocol}://{domain}")
# Allow fetching from internal CDN as default for all applications
domain = domains.get('web-svc-cdn')[0]
tokens.append(f"{web_protocol}://{domain}")
# ReCaptcha integration: allow loading scripts from Google if feature enabled
if self.is_feature_enabled(applications, 'recaptcha', application_id):

View File

@@ -45,7 +45,7 @@ class TestCspFilters(unittest.TestCase):
"body { background: #fff; }",
]
}
}
}
},
},
'app2': {}
@@ -85,18 +85,16 @@ class TestCspFilters(unittest.TestCase):
header = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https')
# Ensure core directives are present
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(
"script-src-elem 'self' https://matomo.example.org https://cdn.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;",
"connect-src 'self' https://matomo.example.org https://cdn.example.org https://api.example.com;",
header
)
# ends with img-src
@@ -106,8 +104,9 @@ class TestCspFilters(unittest.TestCase):
header = self.filter.build_csp_header(self.apps, 'app2', self.domains)
# default-src only contains 'self'
self.assertIn("default-src 'self';", header)
# no external URLs
self.assertNotIn('http', header)
self.assertIn('https://cdn.example.org', header)
self.assertNotIn('matomo.example.org', header)
self.assertNotIn('www.google.com', header)
# ends with img-src
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; }")
self.assertNotIn(style_hash, header)
def test_build_csp_header_recaptcha_toggle(self):
"""
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']
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(
header,
r"frame-ancestors\s+'self'\s+domain-example\.com;"
@@ -194,9 +192,9 @@ class TestCspFilters(unittest.TestCase):
# Now disable the feature and rebuild
self.apps['app1']['features']['desktop'] = False
header_no = self.filter.build_csp_header(self.apps, 'app1', self.domains, web_protocol='https')
# Should no longer contain the wildcarded sld.tld
self.assertNotIn("*.domain-example.com", header_no)
# Should no longer contain the SLD+TLD
self.assertNotIn("domain-example.com", header_no)
if __name__ == '__main__':
unittest.main()
unittest.main()