{"id":"CVE-2026-115897","kind":"vulnerability","aliases":["CVE-2026-115897","GHSA-m5mm-p8h7-m29v"],"related":[],"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.","summary":"serialize-javascript fails to escape a bare closing script tag at the end of a serialized function's source text before version 7.1.3. An attacker who controls a function's toString output can break out of a script element and inject HTML such as an img tag that runs JavaScript in the page.","severity":"low","cvss":2.1,"epss":0.00394,"exploited":false,"exploited_at":null,"products":[{"name":"serialize-javascript","fixed":["7.1.3"],"affected":"< 7.1.3","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-79","npm","package","serialize-javascript","server-rendered-html","web"],"not_affected_if":["data-only-serialization","no-function-values"],"how_to_check":"- Run `npm ls serialize-javascript` in each project to find the installed version.\n- Check lockfiles (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`) for `serialize-javascript` below `7.1.3`.\n- Check whether the app serializes function values into HTML with this package and whether any function's toString can be influenced by user input.","fix":"- Upgrade `serialize-javascript` to `7.1.3` or later.\n- If upgrading is not possible, stop serializing function values whose source text or toString can be influenced by an attacker.","status":"open","published_at":"2026-10-23T22:09:52+00:00","first_seen_at":"2026-10-23T22:15:56+00:00","triaged_at":"2026-10-25T04:50:01+00:00","updated_at":"2026-10-25T12:38:49+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-25T04:50:01Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `serialize-javascript` to `7.1.3` or later.\n- If upgrading is not possible, stop serializing function values whose source text or toString can be influenced by an attacker.","reach":"internet","title":"serialize-javascript: XSS when a function body ends in an unescaped script tag","impact":"medium","summary":"serialize-javascript fails to escape a bare closing script tag at the end of a serialized function's source text before version 7.1.3. An attacker who controls a function's toString output can break out of a script element and inject HTML such as an img tag that runs JavaScript in the page.","category":"package","nicknames":[],"who_cares":["npm","serialize-javascript","server-rendered-html","web"],"confidence":0.8,"needs_auth":"unknown","worst_case":"Injected markup runs script in victims' browsers on the page","analyst_note":"","how_to_check":"- Run `npm ls serialize-javascript` in each project to find the installed version.\n- Check lockfiles (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`) for `serialize-javascript` below `7.1.3`.\n- Check whether the app serializes function values into HTML with this package and whether any function's toString can be influenced by user input.","needs_analyst":false,"not_affected_if":["data-only-serialization","no-function-values"],"who_is_affected":"Applications that embed output from serialize-javascript inside a script element in a page and serialize function values. Exposure also requires that an attacker can control the source text of a serialized function, in practice through a spoofed toString. Ordinary function values and non-function values are not affected, so projects that serialize only data can skip it.","user_interaction":"unknown","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000709,"input_tokens":4027,"output_tokens":613,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-115897':21A,29A '-2026':20A,28A '-79':34B '/script':10A,114C '1':126C '7.1.3':68B 'alert':125C 'attack':70B 'bare':52B 'bodi':18A 'break':79B 'close':53B 'control':72B 'cross':4A 'cross-sit':3A 'cve':19A,27A 'cwe':33B 'element':84B 'emb':111C 'embed':127C 'end':13A,58B 'escap':50B,103C 'fail':48B 'function':17A,62B,74B,116C 'g':115C,117C 'ghsa':23A 'ghsa-m5mm-p8h7-m29v':22A 'html':43B,87B 'img':91B,121C 'impact':99C 'inject':86B 'insid':112C 'javascript':2A,32A,39B,47B,95B,102C 'm29v':26A 'm5mm':24A 'npm':35B 'onerror':124C 'output':77B,105C 'p8h7':25A 'packag':36B 'page':98B 'render':42B 'return':119C 'run':94B 'safe':109C 'script':6A,54B,83B 'serial':1A,16A,38B,46B,61B,101C 'serialize':31A 'serialize-javascript':30A,37B,45B,100C 'server':41B 'server-rendered-html':40B 'site':5A 'sourc':64B 'src':122C 'tag':55B,92B 'text':65B 'tostr':76B 'unescap':9A 'version':67B 'via':8A 'web':44B 'x':118C,120C,123C 'xss':7A","nicknames":[],"mitigations":[],"who_affected":"Applications that embed output from serialize-javascript inside a script element in a page and serialize function values. Exposure also requires that an attacker can control the source text of a serialized function, in practice through a spoofed toString. Ordinary function values and non-function values are not affected, so projects that serialize only data can skip it.","tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-23T22:09:52+00:00","kev":null,"cvss_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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-m5mm-p8h7-m29v","url":"https://github.com/advisories/GHSA-m5mm-p8h7-m29v","severity":"low","published_at":"2026-10-23T22:09:52+00:00","modified_at":"2026-10-23T22:09:53+00:00","first_seen_at":"2026-10-23T22:15:56+00:00","fetched_at":"2026-10-23T22:15:56+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-115897","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-115897","severity":null,"published_at":"2026-10-23T22:16:59+00:00","modified_at":"2026-10-23T22:16:59+00:00","first_seen_at":"2026-10-23T22:29:04+00:00","fetched_at":"2026-10-23T23:44:51+00:00","note":null},{"source":"epss","external_id":"CVE-2026-115897","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-115897","severity":null,"published_at":null,"modified_at":null,"first_seen_at":"2026-10-25T12:38:49+00:00","fetched_at":"2026-10-25T12:38:49+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}