From 9c65c320f957c6feb10d0b67859bdb7ad02e2b64 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Thu, 15 May 2025 19:36:46 +0200 Subject: [PATCH] Optimized more CSP policies and recaptcha --- Makefile | 4 ++-- filter_plugins/csp_filters.py | 7 +++++++ roles/docker-keycloak/vars/configuration.yml | 10 ++++++---- roles/docker-keycloak/vars/main.yml | 2 +- roles/docker-moodle/vars/configuration.yml | 19 +++++++++--------- roles/docker-peertube/vars/configuration.yml | 2 +- roles/docker-wordpress/vars/configuration.yml | 3 +-- tests/unit/test_csp_filters.py | 20 +++++++++++++++++++ 8 files changed, 48 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 0bbde034..97eefeb7 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ install: build @echo "⚙️ Install complete." test: - @echo "\n🧪 Running Unit Tests..." + @echo "🧪 Running Unit Tests..." python -m unittest discover -s tests/unit - @echo "\n🔬 Running Integration Tests..." + @echo "🔬 Running Integration Tests..." python -m unittest discover -s tests/integration \ No newline at end of file diff --git a/filter_plugins/csp_filters.py b/filter_plugins/csp_filters.py index a6fbeb54..c417155c 100644 --- a/filter_plugins/csp_filters.py +++ b/filter_plugins/csp_filters.py @@ -113,6 +113,13 @@ class FilterModule(object): if matomo_domain: tokens.append(f"{web_protocol}://{matomo_domain}") + # ReCaptcha integration: allow loading scripts from Google if feature enabled + if ( + self.is_feature_enabled(applications, 'recaptcha', application_id) + and directive == 'script-src' + ): + tokens.append('https://www.google.com') + # whitelist tokens += self.get_csp_whitelist(applications, application_id, directive) diff --git a/roles/docker-keycloak/vars/configuration.yml b/roles/docker-keycloak/vars/configuration.yml index 3efb77ae..4d1275bd 100644 --- a/roles/docker-keycloak/vars/configuration.yml +++ b/roles/docker-keycloak/vars/configuration.yml @@ -4,12 +4,14 @@ users: username: "{{users.administrator.username}}" # Administrator Username for Keycloak import_realm: True # If True realm will be imported. If false skip. credentials: -# database_password: # Needs to be defined in inventory file -# administrator_password: # Needs to be defined in inventory file features: matomo: true css: true - portfolio_iframe: true + portfolio_iframe: true ldap: true central_database: true - recaptcha: true \ No newline at end of file + recaptcha: true +csp: + flags: + script-src: + unsafe-inline: true \ No newline at end of file diff --git a/roles/docker-keycloak/vars/main.yml b/roles/docker-keycloak/vars/main.yml index 580a8195..ddc4261c 100644 --- a/roles/docker-keycloak/vars/main.yml +++ b/roles/docker-keycloak/vars/main.yml @@ -1,4 +1,4 @@ -application_id: "keycloak" +application_id: "keycloak" database_type: "postgres" container_name: "{{application_id}}_application" realm: "{{primary_domain}}" # This is the name of the default realm which is used by the applications diff --git a/roles/docker-moodle/vars/configuration.yml b/roles/docker-moodle/vars/configuration.yml index 77295330..ff3c2472 100644 --- a/roles/docker-moodle/vars/configuration.yml +++ b/roles/docker-moodle/vars/configuration.yml @@ -1,23 +1,24 @@ -site_titel: "Global Learning Academy on {{primary_domain}}" +site_titel: "Global Learning Academy on {{primary_domain}}" users: administrator: - username: "{{users.administrator.username}}" - email: "{{users.administrator.email}}" -version: "latest" + username: "{{users.administrator.username}}" + email: "{{users.administrator.email}}" +version: "latest" features: matomo: true - css: true - portfolio_iframe: false + css: false + portfolio_iframe: false central_database: true csp: flags: script-src: - unsafe-inline: true - unsafe-eval: true + unsafe-inline: true + unsafe-eval: true style-src: - unsafe-inline: true + unsafe-inline: true whitelist: font-src: - "data:" - "blob:" + script-src: - "https://cdn.jsdelivr.net" \ No newline at end of file diff --git a/roles/docker-peertube/vars/configuration.yml b/roles/docker-peertube/vars/configuration.yml index a2162116..93bffacf 100644 --- a/roles/docker-peertube/vars/configuration.yml +++ b/roles/docker-peertube/vars/configuration.yml @@ -2,7 +2,7 @@ version: "bookworm" features: matomo: true css: true - portfolio_iframe: false + portfolio_iframe: false central_database: true csp: flags: diff --git a/roles/docker-wordpress/vars/configuration.yml b/roles/docker-wordpress/vars/configuration.yml index c7052dea..7ac92539 100644 --- a/roles/docker-wordpress/vars/configuration.yml +++ b/roles/docker-wordpress/vars/configuration.yml @@ -28,10 +28,9 @@ csp: - "blob:" font-src: - "data:" + - "https://fonts.bunny.net" script-src: - "https://cdn.gtranslate.net" - "{{ domains.wordpress[0] }}" - frame-src: - - "{{ domains.peertube }}" style-src: - "https://fonts.bunny.net" \ No newline at end of file diff --git a/tests/unit/test_csp_filters.py b/tests/unit/test_csp_filters.py index b13fa57a..17cc5eb0 100644 --- a/tests/unit/test_csp_filters.py +++ b/tests/unit/test_csp_filters.py @@ -137,5 +137,25 @@ 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' + must be included in script-src; when disabled, it must not be. + """ + # enabled case + self.apps['app1']['features']['recaptcha'] = True + header_enabled = self.filter.build_csp_header( + self.apps, 'app1', self.domains, web_protocol='https' + ) + self.assertIn("https://www.google.com", header_enabled) + + # disabled case + self.apps['app1']['features']['recaptcha'] = False + header_disabled = self.filter.build_csp_header( + self.apps, 'app1', self.domains, web_protocol='https' + ) + self.assertNotIn("https://www.google.com", header_disabled) + if __name__ == '__main__': unittest.main() \ No newline at end of file