Medium severity6.4GHSA Advisory· Published May 21, 2025· Updated Apr 15, 2026
CVE-2025-48203
CVE-2025-48203
Description
The cs_seo extension through 9.2.0 for TYPO3 allows XSS.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
clickstorm/cs-seoPackagist | >= 9.0.0, < 9.3.0 | 9.3.0 |
clickstorm/cs-seoPackagist | >= 8.0.0, < 8.4.0 | 8.4.0 |
clickstorm/cs-seoPackagist | >= 7.0.0, < 7.5.0 | 7.5.0 |
clickstorm/cs-seoPackagist | >= 6.3.0, < 6.8.0 | 6.8.0 |
Affected products
1- Range: >= 6.3.0, < 6.8.0
Patches
11cf6c4082110[SECUTRIY] escape JSON-LD output to prevent XSS #2025032510000016
4 files changed · +73 −15
Classes/Evaluation/TCA/JsonLdEvaluator.php+41 −8 modified@@ -2,24 +2,57 @@ namespace Clickstorm\CsSeo\Evaluation\TCA; +use TYPO3\CMS\Extbase\Utility\LocalizationUtility; + class JsonLdEvaluator extends AbstractEvaluator { + private static bool $alreadyValidated = false; + /** * Server-side validation/evaluation on saving the record * */ public function evaluateFieldValue(string $value, string $is_in, bool &$set): string { - if ($value && !isset($_REQUEST['tx_csseo_json_ld_eval_done'])) { - $value = trim(preg_replace('#<script(.*?)>|</script>#is', '', $value)); - if ($value && json_decode($value, true) === null) { - $this->addFlashMessage( - 'LLL:EXT:cs_seo/Resources/Private/Language/locallang_db.xlf:evaluation.tca.json_ld.invalid_json' - ); - } + if (self::$alreadyValidated || empty($value)) { + return $value; + } + + self::$alreadyValidated = true; + + // Remove surrounding <script> tag if present + $value = $this->stripScriptWrapper($value); + + $decoded = json_decode($value, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + $this->addFlashMessage( + 'LLL:EXT:cs_seo/Resources/Private/Language/locallang_db.xlf:evaluation.tca.json_ld.invalid_json' + ); + + $invalidComment = LocalizationUtility::translate( + 'error.invalid_json_comment', + 'cs_seo' + ) ?? '/* INVALID JSON */'; + + return $invalidComment . "\n" . $value; } - $_REQUEST['tx_csseo_json_ld_eval_done'] = true; + return $value; + } + + /** + * Remove <script type="application/ld+json"> wrapper from pasted code + */ + protected function stripScriptWrapper(string $value): string + { + // Normalize whitespace and remove script wrapper + $value = trim($value); + + // Match and extract the contents inside <script type="application/ld+json">...</script> + if (preg_match('#<script[^>]*type=["\']application/ld\+json["\'][^>]*>(.*?)</script>#is', $value, $matches)) { + return trim($matches[1]); + } return $value; }
Classes/UserFunc/StructuredData.php+23 −5 modified@@ -55,18 +55,36 @@ public function getJsonLdOfPageOrRecord(string $content, array $conf): string // overwrite json ld with record metadata if ($metaData) { - $jsonLd = $metaData['json_ld'] ?? null; + $jsonLd = $metaData['json_ld'] ?? ''; } - return $jsonLd ? $this->wrapWithLd($jsonLd) : ''; + // if empty return nothing + if (empty($jsonLd)) { + return ''; + } + + // Try to decode the JSON string to ensure it's valid + $tempJson = json_decode($jsonLd, true); + + // Check if decoding was successful + if (json_last_error() === JSON_ERROR_NONE) { + + // Re-encode the array to sanitize any unexpected content + $safeJson = json_encode($tempJson, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + return $this->wrapWithLd($safeJson); + } + + return '<!-- Invalid JSON-LD -->'; } /** * Wraps $content with Json declaration */ protected function wrapWithLd(string $content): string { - return '<script type="application/ld+json">' . $content . '</script>'; + // Escape special characters to prevent breaking out of the script tag + return '<script type="application/ld+json">' . htmlspecialchars($content, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</script>'; } public function getSiteSearch(string $content, array $conf): string @@ -93,7 +111,7 @@ public function getSiteSearch(string $content, array $conf): string ], ]; - return $this->wrapWithLd(json_encode($siteSearch)); + return $this->wrapWithLd(json_encode($siteSearch, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } /** @@ -165,6 +183,6 @@ public function getBreadcrumb(string $conf, array $content): string 'itemListElement' => $breadcrumbItems, ]; - return $this->wrapWithLd(json_encode($structuredBreadcrumb)); + return $this->wrapWithLd(json_encode($structuredBreadcrumb, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } }
Resources/Private/Language/de.locallang.xlf+5 −1 modified@@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> - <file source-language="en" target-language="de" datatype="plaintext" original="EXT:cs_seo/Resources/Private/Language/locallang.xlf" date="2024-05-17T07:49:36Z" product-name="cs_seo"> + <file source-language="en" target-language="de" datatype="plaintext" original="EXT:cs_seo/Resources/Private/Language/locallang.xlf" date="2025-04-15T10:00:55Z" product-name="cs_seo"> <header/> <body> <trans-unit id="date.format" resname="date.format"> @@ -19,6 +19,10 @@ <source>The TypoScript of the current page could not be loaded.</source> <target>Das TypoScript dieser Seite konnte nicht geladen werden.</target> </trans-unit> + <trans-unit id="error.invalid_json_comment" resname="error.invalid_json_comment"> + <source>INVALID JSON!</source> + <target>UNGÜLTIGES JSON!</target> + </trans-unit> <trans-unit id="evaluation.description" resname="evaluation.description"> <source>Meta Description</source> <target>Seitenbeschreibung</target>
Resources/Private/Language/locallang.xlf+4 −1 modified@@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> - <file source-language="en" datatype="plaintext" original="EXT:cs_seo/Resources/Private/Language/locallang.xlf" date="2024-05-17T07:49:36Z" product-name="cs_seo"> + <file source-language="en" datatype="plaintext" original="EXT:cs_seo/Resources/Private/Language/locallang.xlf" date="2025-04-15T10:00:55Z" product-name="cs_seo"> <header/> <body> <trans-unit id="date.format" resname="date.format"> @@ -15,6 +15,9 @@ <trans-unit id="error.no_tsfe" resname="error.no_tsfe"> <source>The TypoScript of the current page could not be loaded.</source> </trans-unit> + <trans-unit id="error.invalid_json_comment" resname="error.invalid_json_comment"> + <source>INVALID JSON!</source> + </trans-unit> <trans-unit id="evaluation.description" resname="evaluation.description"> <source>Meta Description</source> </trans-unit>
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-6p8w-pc35-mqv8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-48203ghsaADVISORY
- github.com/FriendsOfPHP/security-advisories/blob/master/clickstorm/cs-seo/CVE-2025-48203.yamlghsaWEB
- github.com/clickstorm/cs_seo/commit/1cf6c40821102b1f1508fe4e76825569340c8f90ghsaWEB
- typo3.org/security/advisory/typo3-ext-sa-2025-005nvdWEB
News mentions
0No linked articles in our index yet.