Do not use JSON_UNESCAPED_SLASHES in format_json_encode() since it may be unsafe in HTML context#22913
Conversation
…t may be unsafe in HTML context
|
Functional regressions: No — but there are two behavioral changes worth noting:
This is valid JSON, semantically equivalent, and Google/search engines handle both fine.
Verdict: |
Context
Using
JSON_UNESCAPED_SLASHESwhen embedding JSON inside a<script>tag is unsafe because it allows unescaped</script>sequences to appear in the output. HTML parsers terminate<script>tags before JavaScript parsing, even if the sequence appears inside a string literal. This can lead to XSS if any JSON value is user-controlled.The default escaping in
json_encode()prevents this and should not be disabled in this context. While this does not appear to be an issue in the plugin's default setup, the schema can be altered by filters, and the current JSON encoding settings are a footgun that can lead to security issues if misused.Steps to reproduce:
Note that
https://example.com/?s=</script>is a perfectly valid URL, but</script>in the URL prematurely closes the<script>tag.Summary
This PR can be summarized in the following changelog entry:
Relevant technical choices:
JSON_UNESCAPED_SLASHESis removed fromformat_json_encode(), keeping onlyJSON_UNESCAPED_UNICODE. As a result, forward slashes in URLs and other values will now be escaped as\/in all JSON output produced by this function. This is valid JSON per the spec and semantically equivalent — all JSON parsers and search engines handle it correctly.format_json_encode(), most notably the schema JSON-LD<script>block and theopen_graph_image_metafield stored in the indexables table. Existing DB records retain the old encoding;json_decode()handles both formats transparently.Test instructions
Test instructions for the acceptance test before the PR gets merged
This PR can be acceptance tested by following these steps:
<script type="application/ld+json" class="yoast-schema-graph">block.\/) — this is expected and correct.<script>tag is intact (not split) and no JS errors appear in the browser console.functions.php:<script>block is not broken — the schema renders as a single intact block in the page source.functions.phpafter testing.Relevant test scenarios
Test instructions for QA when the code is in the RC
QA can test this PR by following these steps:
Impact check
This PR affects the following parts of the plugin, which may require extra testing:
<script type="application/ld+json">blocks on the frontend. URLs will contain\/instead of/.open_graph_image_metafield in the indexables table is encoded byformat_json_encode(). Existing records are unaffected; newly written records will use the new encoding.format_json_encode()— this includes admin AJAX responses, Wincher API calls, HelpScout beacon, and notification storage. All consumers parse the JSON withjson_decode()orJSON.parse(), both of which handle\/correctly.Other environments
[shopify-seo], added test instructions for Shopify and attached theShopifylabel to this PR.[yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached theGoogle Docs Add-onlabel to this PR.Documentation
Quality assurance
grunt build:imagesand commited the results, if my PR introduces new images or SVGs.Innovation
innovationlabel.