mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-15 16:40:45 +02:00
Adapted CSP to new server dict structure
This commit is contained in:
parent
051e4accd6
commit
5a0684fa2d
@ -23,7 +23,7 @@ class FilterModule(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_csp_whitelist(applications, application_id, directive):
|
def get_csp_whitelist(applications, application_id, directive):
|
||||||
app = applications.get(application_id, {})
|
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):
|
if isinstance(wl, list):
|
||||||
return wl
|
return wl
|
||||||
if wl:
|
if wl:
|
||||||
@ -37,7 +37,7 @@ class FilterModule(object):
|
|||||||
e.g., "'unsafe-eval'", "'unsafe-inline'", etc.
|
e.g., "'unsafe-eval'", "'unsafe-inline'", etc.
|
||||||
"""
|
"""
|
||||||
app = applications.get(application_id, {})
|
app = applications.get(application_id, {})
|
||||||
flags = app.get('csp', {}).get('flags', {}).get(directive, {})
|
flags = app.get('server',{}).get('csp', {}).get('flags', {}).get(directive, {})
|
||||||
tokens = []
|
tokens = []
|
||||||
|
|
||||||
for flag_name, enabled in flags.items():
|
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.
|
Return inline script/style snippets to hash for a given CSP directive.
|
||||||
"""
|
"""
|
||||||
app = applications.get(application_id, {})
|
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):
|
if isinstance(snippets, list):
|
||||||
return snippets
|
return snippets
|
||||||
if snippets:
|
if snippets:
|
||||||
|
@ -13,7 +13,8 @@ def append_csp_hash(applications, application_id, code_one_liner):
|
|||||||
|
|
||||||
apps = copy.deepcopy(applications)
|
apps = copy.deepcopy(applications)
|
||||||
app = apps[application_id]
|
app = apps[application_id]
|
||||||
csp = app.setdefault('csp', {})
|
server = app.setdefault('server', {})
|
||||||
|
csp = server.setdefault('csp', {})
|
||||||
hashes = csp.setdefault('hashes', {})
|
hashes = csp.setdefault('hashes', {})
|
||||||
|
|
||||||
existing = hashes.get('script-src-elem', [])
|
existing = hashes.get('script-src-elem', [])
|
||||||
|
@ -62,7 +62,7 @@ class TestCspConfigurationConsistency(unittest.TestCase):
|
|||||||
errors.append(f"{role_path.name}: YAML parse error: {e}")
|
errors.append(f"{role_path.name}: YAML parse error: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
csp = cfg.get('csp')
|
csp = cfg.get('server',{}).get('csp')
|
||||||
if csp is None:
|
if csp is None:
|
||||||
continue # nothing to check
|
continue # nothing to check
|
||||||
|
|
||||||
|
@ -22,28 +22,30 @@ class TestCspFilters(unittest.TestCase):
|
|||||||
'oauth2': True,
|
'oauth2': True,
|
||||||
'matomo': True,
|
'matomo': True,
|
||||||
},
|
},
|
||||||
'csp': {
|
'server':{
|
||||||
'whitelist': {
|
'csp': {
|
||||||
'script-src-elem': ['https://cdn.example.com'],
|
'whitelist': {
|
||||||
'connect-src': 'https://api.example.com',
|
'script-src-elem': ['https://cdn.example.com'],
|
||||||
},
|
'connect-src': 'https://api.example.com',
|
||||||
'flags': {
|
|
||||||
'script-src': {
|
|
||||||
'unsafe-eval': True,
|
|
||||||
'unsafe-inline': False,
|
|
||||||
},
|
},
|
||||||
'style-src': {
|
'flags': {
|
||||||
'unsafe-inline': True,
|
'script-src': {
|
||||||
|
'unsafe-eval': True,
|
||||||
|
'unsafe-inline': False,
|
||||||
|
},
|
||||||
|
'style-src': {
|
||||||
|
'unsafe-inline': True,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
'hashes': {
|
||||||
'hashes': {
|
'script-src': [
|
||||||
'script-src': [
|
"console.log('hello');",
|
||||||
"console.log('hello');",
|
],
|
||||||
],
|
'style-src': [
|
||||||
'style-src': [
|
"body { background: #fff; }",
|
||||||
"body { background: #fff; }",
|
]
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'app2': {}
|
'app2': {}
|
||||||
@ -114,7 +116,7 @@ class TestCspFilters(unittest.TestCase):
|
|||||||
|
|
||||||
def test_get_csp_inline_content_string(self):
|
def test_get_csp_inline_content_string(self):
|
||||||
# simulate single string instead of list
|
# 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')
|
snippets = self.filter.get_csp_inline_content(self.apps, 'app1', 'style-src')
|
||||||
self.assertEqual(snippets, ["body { color: red; }"])
|
self.assertEqual(snippets, ["body { color: red; }"])
|
||||||
|
|
||||||
|
@ -8,9 +8,11 @@ class TestCspHashes(unittest.TestCase):
|
|||||||
# Sample applications dict for testing
|
# Sample applications dict for testing
|
||||||
self.applications = {
|
self.applications = {
|
||||||
'app1': {
|
'app1': {
|
||||||
'csp': {
|
'server':{
|
||||||
'hashes': {
|
'csp': {
|
||||||
'script-src-elem': ["existing-hash"]
|
'hashes': {
|
||||||
|
'script-src-elem': ["existing-hash"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,25 +22,25 @@ class TestCspHashes(unittest.TestCase):
|
|||||||
def test_appends_new_hash(self):
|
def test_appends_new_hash(self):
|
||||||
result = append_csp_hash(self.applications, 'app1', self.code)
|
result = append_csp_hash(self.applications, 'app1', self.code)
|
||||||
# Original remains unchanged
|
# 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
|
# New result should contain both existing and new
|
||||||
self.assertIn('existing-hash', 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']['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):
|
def test_does_not_duplicate_existing_hash(self):
|
||||||
# Append an existing hash
|
# Append an existing hash
|
||||||
result = append_csp_hash(self.applications, 'app1', 'existing-hash')
|
result = append_csp_hash(self.applications, 'app1', 'existing-hash')
|
||||||
# Should still only have one instance
|
# 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)
|
self.assertEqual(hashes.count('existing-hash'), 1)
|
||||||
|
|
||||||
def test_creates_missing_csp_structure(self):
|
def test_creates_missing_csp_structure(self):
|
||||||
# Remove csp and hashes keys
|
# Remove csp and hashes keys
|
||||||
apps = {'app2': {}}
|
apps = {'app2': {}}
|
||||||
result = append_csp_hash(apps, 'app2', self.code)
|
result = append_csp_hash(apps, 'app2', self.code)
|
||||||
self.assertIn('csp', result['app2'])
|
self.assertIn('csp', result['app2']['server'])
|
||||||
self.assertIn('hashes', result['app2']['csp'])
|
self.assertIn('hashes', result['app2']['server']['csp'])
|
||||||
self.assertIn(self.code, result['app2']['csp']['hashes']['script-src-elem'])
|
self.assertIn(self.code, result['app2']['server']['csp']['hashes']['script-src-elem'])
|
||||||
|
|
||||||
def test_non_dict_applications_raises(self):
|
def test_non_dict_applications_raises(self):
|
||||||
with self.assertRaises(AnsibleFilterError):
|
with self.assertRaises(AnsibleFilterError):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user