{"schema":"threatline.record/2","record":{"id":"CVE-2026-88058","aliases":["GHSA-j3r3-mxqp-r2p4"],"summary":"Angular SSR: XSS via Unescaped Processing Instruction (<?...?>) Nodes in Fallback Raw-Content Elements","details":"### Summary\nAn XSS vulnerability exists in `@angular/platform-server` during server-side rendering (SSR) HTML serialization of `ProcessingInstruction` DOM nodes (`<?target data?>`, `nodeType === 7`) when nested inside fallback raw-content elements (`<noscript>`, `<iframe>`, `<noembed>`, `<noframes>`). While processing instruction data escaped `>` to `&gt;`, it did not check for or escape matching closing tags of ancestor fallback elements (e.g., `</noscript>`). When rendered in a browser with scripting enabled, an unescaped closing tag sequence in a processing instruction prematurely closes the fallback raw-content tag and causes subsequent sibling elements to execute as live HTML.\n\n### Technical Description\nIn HTML5 parsing, fallback raw-content elements (`<noscript>`, `<iframe>`, `<noembed>`, `<noframes>`) place the browser's HTML tokenizer into `RAWTEXT` mode. In `RAWTEXT` mode, processing instruction tokens (`<?...?>`) are treated as literal raw text rather than bogus comments, and the parser ignores `>` or `?>`. The only token sequence that terminates the container is an end tag matching the container tag name (`</noscript`, `</iframe`, etc.).\n\nDuring server-side HTML serialization, processing instruction nodes previously only replaced `>` with `&gt;` (preventing bogus comment breakouts in normal HTML data states) but left `<` untouched. Crucially, processing instruction serialization never inspected ancestor fallback raw-content tags. As a result, if a `ProcessingInstruction` node inside `<noscript>` contained `</noscript ` in its data payload, it was emitted unescaped as `<?x </noscript ?>`.\n\n### Impact & Reachability\n* **Reachability**: Processing instruction nodes cannot be authored directly through standard Angular template syntax (which parses `<?...>` into comment nodes in DOM position). Reaching this vulnerability requires application or library code calling `inject(DOCUMENT).createProcessingInstruction(target, data)` or `Renderer2` DOM insertion methods with untrusted user input passed to `data` inside a fallback raw-content container.\n* **Impact**: In applications that programmatically construct processing instruction nodes inside fallback elements during server-side rendering, an attacker controlling the processing instruction data can break out of the container and execute arbitrary JavaScript in victims' browsers.\n\n### Proof of Concept (Minimal Reproduction)\n```ts\nimport { Component, ElementRef, Renderer2, inject, DOCUMENT, AfterViewInit } from '@angular/core';\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  template: `<noscript id=\"host\"></noscript>`\n})\nexport class AppComponent implements AfterViewInit {\n  private r = inject(Renderer2);\n  private el = inject(ElementRef);\n  private doc = inject(DOCUMENT);\n\n  ngAfterViewInit() {\n    const host = this.el.nativeElement.querySelector('#host');\n    \n    // Attacker-controlled input passed as Processing Instruction data\n    const pi = this.doc.createProcessingInstruction('x', '</noscript ');\n    this.r.appendChild(host, pi);\n    \n    // Sibling markup that should remain inert inside <noscript>\n    const img = this.r.createElement('img');\n    this.r.setAttribute(img, 'src', 'x');\n    this.r.setAttribute(img, 'onerror', 'alert(\"SSR_PI_XSS\")');\n    this.r.appendChild(host, img);\n  }\n}\n```\n**Vulnerable SSR Output**:\n```html\n<noscript><?x </noscript ?><img src=\"x\" onerror=\"alert('SSR_PI_XSS')\"></noscript>\n```\n\n### Workarounds\n* Avoid passing untrusted user input into `document.createProcessingInstruction(target, data)` when the node is inserted into `<noscript>`, `<iframe>`, `<noembed>`, or `<noframes>` during server-side rendering.\n* Manually sanitize or replace `<` with `&lt;` in any untrusted data passed to processing instruction nodes on the server.","categories":["Frameworks"],"product":"@angular/platform-server","vendor":"angular","ecosystem":"npm","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:40:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":">= 22.0.0, < 22.1.4"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:40:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":">= 21.0.0, < 21.2.22"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:40:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":">= 20.0.0, < 20.3.30"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:40:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":"<= 19.2.25"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-88058","source_modified_at":"2026-09-11T15:14:37.967Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"angular","vendor":"angular","versions":[{"version":"<= 19.2.25","status":"affected"},{"version":">= 20.0.0, < 20.3.30","status":"affected"},{"version":">= 21.0.0, < 21.2.22","status":"affected"},{"version":">= 22.0.0, < 22.1.4","status":"affected"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:45:04.134330777Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"ranges":[{"type":"SEMVER","events":[{"introduced":"22.0.0"},{"fixed":"22.1.4"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:45:04.134330777Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"ranges":[{"type":"SEMVER","events":[{"introduced":"21.0.0"},{"fixed":"21.2.22"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:45:04.134330777Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"ranges":[{"type":"SEMVER","events":[{"introduced":"20.0.0"},{"fixed":"20.3.30"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:45:04.134330777Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"19.2.25"}]}],"versions":[]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:40:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"version":"22.1.4"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:40:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"version":"21.2.22"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:40:09Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"version":"20.3.30"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:45:04.134330777Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"version":"22.1.4"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:45:04.134330777Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"version":"21.2.22"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","source_modified_at":"2026-09-28T20:45:04.134330777Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"version":"20.3.30"}],"cvss":8.6,"severity":"High","kev":{"listed":false},"epss":{"probability":0.00281,"percentile":0.19555,"score_date":"2026-10-23"},"published_at":"2026-09-10T18:56:03.369Z","modified_at":"2026-09-28T20:45:04.134330777Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-09-28T20:40:09Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-88058","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-09-11T15:14:37.967Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-j3r3-mxqp-r2p4","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-09-28T20:45:04.134330777Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-88058","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/angular/angular/security/advisories/GHSA-j3r3-mxqp-r2p4","tags":["exploit"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-88058","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/angular/angular/issues/70146","tags":["exploit"]},{"source":"CVE List","url":"https://github.com/angular/angular/commit/73d8bbd27cb46495426d4132975a1355b47ad915","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/commit/89b20568dfaee1ec8e0b3bcf1872acdddd2f4fef","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/commit/ba3bc47b20b3d12f5eb141ec9c651373ae4d15e8","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/domino/commit/04f987dc08ff3736b427f50941adf1722458528f","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/releases/tag/v20.3.30","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/releases/tag/v21.2.22","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/releases/tag/v22.1.4","tags":["x_refsource_MISC"]},{"url":"https://github.com/advisories/GHSA-j3r3-mxqp-r2p4","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"poc","automatable":"no","technical_impact":"total","assessed_at":"2026-09-11T15:14:28.505071Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"product","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-09-28T20:45:04.134330777Z","content_hash":"e3d50b66b2469f67feea140fcb7b205306692162ec9875f391ce105a650eaf39"}}
