Skip to content

Commit 3f5e979

Browse files
committed
fix(icons): replace hand-rolled entity decoder with browser-native parser (CWE-116)
1 parent 87a8fc7 commit 3f5e979

1 file changed

Lines changed: 11 additions & 10 deletions

File tree

  • modules/blox/blox/shared/js/components

modules/blox/blox/shared/js/components/Icon.jsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {h} from "preact";
44
/**
55
* Icon component
66
* Renders an SVG icon from a raw SVG string passed from Hugo.
7-
* Decodes JSON-escaped sequences (\u003c) and HTML entities (<, ", ").
7+
* Defensively decodes any residual JSON unicode escapes and HTML entities.
88
*
99
* SVG sizing strategy:
1010
* - By default, icons size to 1em (matching current font size) — works universally
@@ -16,18 +16,19 @@ import {h} from "preact";
1616
export const Icon = ({svg, attributes}) => {
1717
if (!svg) return null;
1818

19-
// Clean the SVG string: decode HTML entities and TRIM whitespace
19+
// Decode any residual JSON unicode escapes (e.g. \u003c → <) and HTML entities
20+
// (e.g. &lt; → <) that survive the JSON.parse + DOM read pipeline.
21+
// Uses the browser's built-in HTML parser rather than hand-rolled regexes —
22+
// this is inherently safe against double-unescaping (CWE-116).
2023
let decoded = String(svg)
21-
.replace(/\\u003c/gi, "<")
22-
.replace(/\\u003e/gi, ">")
23-
.replace(/&lt;/gi, "<")
24-
.replace(/&gt;/gi, ">")
25-
.replace(/&quot;/gi, '"')
26-
.replace(/&#34;/gi, '"')
27-
.replace(/\\u0026/gi, "&")
28-
.replace(/&amp;/gi, "&")
24+
.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
2925
.trim();
3026

27+
// Let the browser's HTML parser handle all entity decoding in one safe pass
28+
const _textarea = document.createElement("textarea");
29+
_textarea.innerHTML = decoded;
30+
decoded = _textarea.value;
31+
3132
const hasWrapper = /<svg[\s>]/i.test(decoded);
3233

3334
if (hasWrapper) {

0 commit comments

Comments
 (0)