{"id":"GHSA-m5mm-p8h7-m29v","revision":"0dc5ebc6f3d84b6eb46f04f60d64a3b0","records":[{"revision":"0dc5ebc6f3d84b6eb46f04f60d64a3b0","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-m5mm-p8h7-m29v","aliases":["CVE-2026-115754","CVE-2026-115897"],"title":"Serialize JavaScript: Cross-site scripting (XSS) via unescaped </script at the end of a serialized function body","description":"### Impact\n\n`serialize-javascript` escapes its output so it is safe to embed inside a\n`<script>` element. Before 7.1.3 that guarantee did not hold for a **function\nvalue whose source ends in a bare `</script`**.\n\nEach function body is escaped on its own, and a bare `</script` was only\nneutralized when followed — within the same body — by a character the HTML\ntokenizer treats as ending the tag name (TAB, LF, FF, CR, SPACE, `/` or `>`).\nA body ending exactly at `</script` has no following character, so it was\nemitted unescaped. The delimiter is then supplied by the surrounding output\n(for example the newline the `space` option emits after the value), and a\nlater serialized value can supply the closing `>` followed by markup:\n\n```js\nconst serialize = require('serialize-javascript');\nfunction f() {} f.toString = () => 'function f(){ return 1 }//</script';\nfunction g() {} g.toString = () => 'function g(x){ return x } > <img src=x onerror=alert(1)>';\nserialize({ a: { f }, g }, { space: 2 });\n// {\n//   \"a\": {\n//     \"f\": function f(){ return 1 }//</script\n//   },\n//   \"g\": function g(x){ return x } > <img src=x onerror=alert(1)>\n// }\n```\n\nEmbedded in a `<script>` element, the script ends at the injected tag and the\n`<img>` becomes a live element in the page.\n\nExploitation requires an attacker to control the **source text** of a\nserialized function, in practice through a spoofed `toString()`. Real\nfunctions always end in `}`, so ordinary function values are not affected,\nand non-function values (including strings containing `</script`) are\nescaped correctly.\n\n### Patches\n\nFixed in **7.1.3** (#235). The bare `</script` form is now also matched at\nthe end of the function body, so it is escaped regardless of what follows it\nin the output.\n\n### Workarounds\n\nUpgrade to **7.1.3**. If you cannot upgrade, do not serialize function values\nwhose source text (or `toString()`) can be influenced by an attacker.\n\n### Credits\n\nReported by **ha_kr25** via the Yahoo bug bounty program.","state":0,"publishedAt":"2026-10-23T22:09:52+00:00","modifiedAt":"2026-10-24T02:55:55.5077181+00:00","affected":[{"ecosystem":"npm","name":"serialize-javascript","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"7.1.3"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V4","vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"LOW"}],"references":["https://github.com/yahoo/serialize-javascript/security/advisories/GHSA-m5mm-p8h7-m29v","https://github.com/yahoo/serialize-javascript/pull/235","https://github.com/yahoo/serialize-javascript/commit/2864b3c6260a3bfd52d2db3e70d1a079de108a61","https://github.com/yahoo/serialize-javascript","https://github.com/yahoo/serialize-javascript/releases/tag/v7.1.3"],"sourceUrl":"https://github.com/advisories/GHSA-m5mm-p8h7-m29v","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-m5mm-p8h7-m29v","identifiers":["GHSA-m5mm-p8h7-m29v","CVE-2026-115754","CVE-2026-115897"]},"retained":false,"historicalAffected":[]}],"coverage":[]}