diff --git a/filter_plugins/csp_filters.py b/filter_plugins/csp_filters.py index af9e13e4..ed1c6bb7 100644 --- a/filter_plugins/csp_filters.py +++ b/filter_plugins/csp_filters.py @@ -23,7 +23,7 @@ class FilterModule(object): @staticmethod def get_csp_whitelist(applications, application_id, directive): app = applications.get(application_id, {}) - wl = app.get('csp', {}).get('whitelist', {}).get(directive, []) + wl = app.get('server',{}).get('csp', {}).get('whitelist', {}).get(directive, []) if isinstance(wl, list): return wl if wl: @@ -37,7 +37,7 @@ class FilterModule(object): e.g., "'unsafe-eval'", "'unsafe-inline'", etc. """ app = applications.get(application_id, {}) - flags = app.get('csp', {}).get('flags', {}).get(directive, {}) + flags = app.get('server',{}).get('csp', {}).get('flags', {}).get(directive, {}) tokens = [] for flag_name, enabled in flags.items(): @@ -52,7 +52,7 @@ class FilterModule(object): Return inline script/style snippets to hash for a given CSP directive. """ app = applications.get(application_id, {}) - snippets = app.get('csp', {}).get('hashes', {}).get(directive, []) + snippets = app.get('server',{}).get('csp', {}).get('hashes', {}).get(directive, []) if isinstance(snippets, list): return snippets if snippets: diff --git a/filter_plugins/csp_hashes.py b/filter_plugins/csp_hashes.py index f1bb8491..47f44a68 100644 --- a/filter_plugins/csp_hashes.py +++ b/filter_plugins/csp_hashes.py @@ -13,7 +13,8 @@ def append_csp_hash(applications, application_id, code_one_liner): apps = copy.deepcopy(applications) app = apps[application_id] - csp = app.setdefault('csp', {}) + server = app.setdefault('server', {}) + csp = server.setdefault('csp', {}) hashes = csp.setdefault('hashes', {}) existing = hashes.get('script-src-elem', []) diff --git a/tests/integration/test_csp_configuration_consistency.py b/tests/integration/test_csp_configuration_consistency.py index 6526b401..1cd00594 100644 --- a/tests/integration/test_csp_configuration_consistency.py +++ b/tests/integration/test_csp_configuration_consistency.py @@ -62,7 +62,7 @@ class TestCspConfigurationConsistency(unittest.TestCase): errors.append(f"{role_path.name}: YAML parse error: {e}") continue - csp = cfg.get('csp') + csp = cfg.get('server',{}).get('csp') if csp is None: continue # nothing to check diff --git a/tests/unit/filter_plugins/test_csp_filters.py b/tests/unit/filter_plugins/test_csp_filters.py index 84b43ba6..9d0d6282 100644 --- a/tests/unit/filter_plugins/test_csp_filters.py +++ b/tests/unit/filter_plugins/test_csp_filters.py @@ -22,28 +22,30 @@ class TestCspFilters(unittest.TestCase): 'oauth2': True, 'matomo': True, }, - 'csp': { - 'whitelist': { - 'script-src-elem': ['https://cdn.example.com'], - 'connect-src': 'https://api.example.com', - }, - 'flags': { - 'script-src': { - 'unsafe-eval': True, - 'unsafe-inline': False, + 'server':{ + 'csp': { + 'whitelist': { + 'script-src-elem': ['https://cdn.example.com'], + 'connect-src': 'https://api.example.com', }, - 'style-src': { - 'unsafe-inline': True, + 'flags': { + 'script-src': { + 'unsafe-eval': True, + 'unsafe-inline': False, + }, + 'style-src': { + 'unsafe-inline': True, + }, }, - }, - 'hashes': { - 'script-src': [ - "console.log('hello');", - ], - 'style-src': [ - "body { background: #fff; }", - ] - } + 'hashes': { + 'script-src': [ + "console.log('hello');", + ], + 'style-src': [ + "body { background: #fff; }", + ] + } + } }, }, 'app2': {} @@ -114,7 +116,7 @@ class TestCspFilters(unittest.TestCase): def test_get_csp_inline_content_string(self): # simulate single string instead of list - self.apps['app1']['csp']['hashes']['style-src'] = "body { color: red; }" + self.apps['app1']['server']['csp']['hashes']['style-src'] = "body { color: red; }" snippets = self.filter.get_csp_inline_content(self.apps, 'app1', 'style-src') self.assertEqual(snippets, ["body { color: red; }"]) diff --git a/tests/unit/filter_plugins/test_csp_hashes.py b/tests/unit/filter_plugins/test_csp_hashes.py index e5cde0c8..ffc6ba86 100644 --- a/tests/unit/filter_plugins/test_csp_hashes.py +++ b/tests/unit/filter_plugins/test_csp_hashes.py @@ -8,9 +8,11 @@ class TestCspHashes(unittest.TestCase): # Sample applications dict for testing self.applications = { 'app1': { - 'csp': { - 'hashes': { - 'script-src-elem': ["existing-hash"] + 'server':{ + 'csp': { + 'hashes': { + 'script-src-elem': ["existing-hash"] + } } } } @@ -20,25 +22,25 @@ class TestCspHashes(unittest.TestCase): def test_appends_new_hash(self): result = append_csp_hash(self.applications, 'app1', self.code) # Original remains unchanged - self.assertNotIn(self.code, self.applications['app1']['csp']['hashes']['script-src-elem']) + self.assertNotIn(self.code, self.applications['app1']['server']['csp']['hashes']['script-src-elem']) # New result should contain both existing and new - self.assertIn('existing-hash', result['app1']['csp']['hashes']['script-src-elem']) - self.assertIn(self.code, result['app1']['csp']['hashes']['script-src-elem']) + self.assertIn('existing-hash', result['app1']['server']['csp']['hashes']['script-src-elem']) + self.assertIn(self.code, result['app1']['server']['csp']['hashes']['script-src-elem']) def test_does_not_duplicate_existing_hash(self): # Append an existing hash result = append_csp_hash(self.applications, 'app1', 'existing-hash') # Should still only have one instance - hashes = result['app1']['csp']['hashes']['script-src-elem'] + hashes = result['app1']['server']['csp']['hashes']['script-src-elem'] self.assertEqual(hashes.count('existing-hash'), 1) def test_creates_missing_csp_structure(self): # Remove csp and hashes keys apps = {'app2': {}} result = append_csp_hash(apps, 'app2', self.code) - self.assertIn('csp', result['app2']) - self.assertIn('hashes', result['app2']['csp']) - self.assertIn(self.code, result['app2']['csp']['hashes']['script-src-elem']) + self.assertIn('csp', result['app2']['server']) + self.assertIn('hashes', result['app2']['server']['csp']) + self.assertIn(self.code, result['app2']['server']['csp']['hashes']['script-src-elem']) def test_non_dict_applications_raises(self): with self.assertRaises(AnsibleFilterError):