diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35048103..9e067f6b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# REDAXO consent_manager - Changelog
+## Version 5.3.3 - 29.01.2026
+
+- **Fix:** JSON Parsing Fehler im Frontend behoben (`double-escaping` von HTML-Attributen entfernt), was zu Fehlern beim Laden der Cookie-Gruppen führte (`safeJSONParse failed`).
+
+- **Fix:** Fehler beim Laden von Framework-Templates behoben (`Call to undefined method rex_fragment::subparse()`).
+- **Security:** XSS-Schwachstelle in `consent_manager_outputjs` behoben (Input-Sanitizing für `cid` und `v` Parameter).
+- **Security:** Schutz vor Host-Header Injection im Frontend-Output.
+- **Fix:** JavaScript Syntax-Fehler durch verbessertes Template-Escaping behoben (`json_encode` statt string replace).
+- **Fix:** Google Consent Mode v2 Script auf ES5 Syntax aktualisiert (SyntaxError Fix für ältere Umgebungen).
+
## Version 5.3.0 - 28.01.2026
**🚀 Release-Highlights:**
diff --git a/Namespace-Guide.md b/Namespace-Guide.md
deleted file mode 100644
index c0190b33..00000000
--- a/Namespace-Guide.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Namespace-Guide für consent_manager 5.0
-
-Ab Version 5.0 verwendet der Consent Manager den Namespace `FriendsOfRedaxo\ConsentManager`.
-
-## Übersicht der Klassen-Änderungen
-
-| Alter Klassenname | Neuer Klassenname |
-|-------------------|-------------------|
-| `consent_manager_frontend` | `FriendsOfRedaxo\ConsentManager\Frontend` |
-| `consent_manager_util` | `FriendsOfRedaxo\ConsentManager\Utility` |
-| `consent_manager_config` | `FriendsOfRedaxo\ConsentManager\Config` |
-| `consent_manager_cache` | `FriendsOfRedaxo\ConsentManager\Cache` |
-| `consent_manager_clang` | `FriendsOfRedaxo\ConsentManager\CLang` |
-| `consent_manager_inline` | `FriendsOfRedaxo\ConsentManager\InlineConsent` |
-| `consent_manager_theme` | `FriendsOfRedaxo\ConsentManager\Theme` |
-| `consent_manager_google_consent_mode` | `FriendsOfRedaxo\ConsentManager\GoogleConsentMode` |
-| `consent_manager_json_setup` | `FriendsOfRedaxo\ConsentManager\JsonSetup` |
-| `consent_manager_oembed_parser` | `FriendsOfRedaxo\ConsentManager\OEmbedParser` |
-| `consent_manager_thumbnail_cache` | `FriendsOfRedaxo\ConsentManager\ThumbnailCache` |
-| `consent_manager_rex_form` | `FriendsOfRedaxo\ConsentManager\RexFormSupport` |
-| `consent_manager_rex_list` | `FriendsOfRedaxo\ConsentManager\RexListSupport` |
-| `rex_api_consent_manager` | `FriendsOfRedaxo\ConsentManager\Api\ConsentManager` |
-| `rex_api_consent_manager_inline_log` | `FriendsOfRedaxo\ConsentManager\Api\InlineLog` |
-| `rex_cronjob_log_delete` | `FriendsOfRedaxo\ConsentManager\Cronjob\LogDelete` |
-| `rex_cronjob_consent_manager_thumbnail_cleanup` | `FriendsOfRedaxo\ConsentManager\Cronjob\ThumbnailCleanup` |
-| `rex_consent_manager_thumbnail_mediamanager` | `FriendsOfRedaxo\ConsentManager\ThumbnailMediaManager` |
-| `rex_consent_manager_command_log_delete` | `FriendsOfRedaxo\ConsentManager\Command\LogDelete` |
-
-## Migration deines Codes
-
-### Vorher (alt)
-
-```php
-$frontend = new consent_manager_frontend(0);
-$frontend->showBox();
-
-consent_manager_util::hostname();
-```
-
-### Nachher (neu)
-
-```php
-use FriendsOfRedaxo\ConsentManager\Frontend;
-use FriendsOfRedaxo\ConsentManager\Utility;
-
-$frontend = new Frontend(0);
-$frontend->showBox();
-
-Utility::hostname();
-```
-
-### Alternative mit vollqualifiziertem Namen
-
-```php
-$frontend = new \FriendsOfRedaxo\ConsentManager\Frontend(0);
-\FriendsOfRedaxo\ConsentManager\Utility::hostname();
-```
-
-## Übergangszeit
-
-Die alten Klassennamen funktionieren weiterhin, sind aber als `@deprecated` markiert. Du bekommst also keine Fehler, solltest aber deinen Code bei Gelegenheit anpassen.
-
-Die Deprecated-Klassen findest du in `lib/deprecated/` – sie leiten einfach auf die neuen Namespace-Klassen weiter.
-
-## doConsent() Funktion
-
-Die Shorthand-Funktion `doConsent()` bleibt unverändert verfügbar:
-
-```php
-// Das funktioniert weiterhin
-echo doConsent('youtube', '');
-```
-
-Alternativ mit der neuen Klasse:
-
-```php
-use FriendsOfRedaxo\ConsentManager\InlineConsent;
-
-echo InlineConsent::doConsent('youtube', '');
-```
-
-## Fragmente
-
-Die Fragmente wurden in ein Unterverzeichnis verschoben:
-
-| Vorher | Nachher |
-|--------|---------|
-| `consent_manager_box.php` | `ConsentManager/box.php` |
-| `consent_manager_consent_box.php` | `ConsentManager/consent_box.php` |
-
-```php
-// Neu
-$fragment = new rex_fragment();
-$fragment->setVar('domain', $domain);
-echo $fragment->parse('ConsentManager/box.php');
-```
-
-## Fragen?
-
-Bei Fragen oder Problemen: [GitHub Issues](https://github.com/FriendsOfREDAXO/consent_manager/issues) oder [REDAXO Slack](https://redaxo.org/slack/)
diff --git a/assets/consent_cookie_helper.js b/assets/consent_cookie_helper.js
new file mode 100644
index 00000000..e69de29b
diff --git a/fragments/ConsentManager/box.php b/fragments/ConsentManager/box.php
index 29b4811b..940126b2 100644
--- a/fragments/ConsentManager/box.php
+++ b/fragments/ConsentManager/box.php
@@ -22,7 +22,7 @@
// Check for CSS Framework Mode
$cssFrameworkMode = rex_addon::get('consent_manager')->getConfig('css_framework_mode');
if ($cssFrameworkMode) {
- echo $this->subparse('ConsentManager/box_' . $cssFrameworkMode . '.php');
+ echo $this->parse('ConsentManager/box_' . $cssFrameworkMode . '.php');
return;
}
diff --git a/lib/Frontend.php b/lib/Frontend.php
index 915835ce..ad5dfb53 100644
--- a/lib/Frontend.php
+++ b/lib/Frontend.php
@@ -259,17 +259,20 @@ public function outputJavascript(): never
/** @phpstan-ignore-next-line */
$boxtemplate = sprogdown($boxtemplate, $clang);
}
- $boxtemplate = str_replace("'", "\\'", $boxtemplate);
$boxtemplate = str_replace("\r", '', $boxtemplate);
$boxtemplate = str_replace("\n", ' ', $boxtemplate);
echo '/* --- Parameters --- */' . PHP_EOL;
+ // Sanitize input parameters to prevent XSS
+ $cacheLogId = preg_replace('/[^a-zA-Z0-9_\-]/', '', rex_request::get('cid', 'string', ''));
+ $version = preg_replace('/[^0-9.]/', '', rex_request::get('v', 'string', ''));
+
$consent_manager_parameters = [
'initially_hidden' => 'true' === rex_request::get('i', 'string', 'false'),
'domain' => Utility::hostname(),
'consentid' => uniqid('', true),
- 'cachelogid' => rex_request::get('cid', 'string', ''),
- 'version' => rex_request::get('v', 'string', ''),
+ 'cachelogid' => $cacheLogId,
+ 'version' => $version,
'fe_controller' => rex_url::frontend(),
'forcereload' => rex_request::get('r', 'int', 0),
'hidebodyscrollbar' => 'true' === rex_request::get('h', 'string', 'false'),
@@ -278,12 +281,9 @@ public function outputJavascript(): never
'cookieSecure' => (bool) $addon->getConfig('cookie_secure', false),
'cookieName' => $addon->getConfig('cookie_name', 'consentmanager'),
];
- echo 'var consent_manager_parameters = ' . json_encode($consent_manager_parameters, JSON_UNESCAPED_SLASHES) . ';' . PHP_EOL . PHP_EOL;
+ echo 'var consent_manager_parameters = ' . json_encode($consent_manager_parameters, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) . ';' . PHP_EOL . PHP_EOL;
echo '/* --- Consent-Manager Box Template lang=' . $clang . ' --- */' . PHP_EOL;
- echo 'var consent_manager_box_template = \'';
- // REXSTAN: meldet «Binary operation "." between array|string and '\';' results in an error.»
- // Das ist definitiv falsch und eine Fehlinterpretation wegen obigem «$boxtemplate = str_replace(...»
- echo $boxtemplate . '\';' . PHP_EOL . PHP_EOL;
+ echo 'var consent_manager_box_template = ' . json_encode($boxtemplate, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) . ';' . PHP_EOL . PHP_EOL;
$lifespan = $addon->getConfig('lifespan', 365);
if ('' === $lifespan) {
@@ -454,11 +454,6 @@ public static function getJS(): string
$boxtemplate = is_string($sprogResult) ? $sprogResult : $boxtemplate;
}
- // Escape for JavaScript
- $boxtemplate = str_replace("'", "\\'", $boxtemplate);
- $boxtemplate = str_replace("\r", '', $boxtemplate);
- $boxtemplate = str_replace("\n", ' ', $boxtemplate);
-
$output = '';
// Parameters
@@ -477,13 +472,11 @@ public static function getJS(): string
'cookieSecure' => (bool) $addon->getConfig('cookie_secure', false),
'cookieName' => $addon->getConfig('cookie_name', 'consentmanager'),
];
- $output .= 'var consent_manager_parameters = ' . json_encode($consent_manager_parameters, JSON_UNESCAPED_SLASHES) . ';' . PHP_EOL . PHP_EOL;
+ $output .= 'var consent_manager_parameters = ' . json_encode($consent_manager_parameters, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) . ';' . PHP_EOL . PHP_EOL;
// Box template
$output .= '/* --- Consent-Manager Box Template lang=' . $clang . ' --- */' . PHP_EOL;
- $output .= 'var consent_manager_box_template = \'';
- // $boxtemplate is guaranteed to be string after above checks
- $output .= $boxtemplate . '\';' . PHP_EOL . PHP_EOL;
+ $output .= 'var consent_manager_box_template = ' . json_encode($boxtemplate, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) . ';' . PHP_EOL . PHP_EOL;
// Cookie expiration
$lifespan = $addon->getConfig('lifespan', 365);
diff --git a/package.yml b/package.yml
index 5704347b..ed7d8034 100644
--- a/package.yml
+++ b/package.yml
@@ -1,5 +1,5 @@
package: consent_manager
-version: "5.3.2"
+version: "5.3.3"
author: "Friends Of REDAXO"
supportpage: https://redaxo.org/support/community/#slack