From 6b4a414dff23001882d4dd31209b3c7dd60336e5 Mon Sep 17 00:00:00 2001 From: Gabriel Taveira Date: Mon, 7 Sep 2026 09:24:43 -0300 Subject: [PATCH] build(deps): patch xmldom fragment injection --- pnpm-lock.yaml | 21 +++++++------- pnpm-workspace.yaml | 3 +- tools/dependency-security-check.mjs | 44 ++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3bc9ab4..d4eb67a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,8 @@ settings: excludeLinksFromLockfile: false overrides: - '@expo/plist>@xmldom/xmldom': 0.8.13 + '@expo/plist>@xmldom/xmldom': 0.8.15 + plist>@xmldom/xmldom: 0.9.12 '@typescript-eslint/typescript-estree>minimatch': 9.0.7 brace-expansion@<1.1.18: ^1.1.18 brace-expansion@>=4.0.0 <5.0.9: ^5.0.9 @@ -1903,12 +1904,12 @@ packages: '@ungap/structured-clone@1.3.3': resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} - '@xmldom/xmldom@0.8.13': - resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} + '@xmldom/xmldom@0.8.15': + resolution: {integrity: sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==} engines: {node: '>=10.0.0'} - '@xmldom/xmldom@0.9.10': - resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} + '@xmldom/xmldom@0.9.12': + resolution: {integrity: sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==} engines: {node: '>=14.6'} abab@2.0.6: @@ -6871,13 +6872,13 @@ snapshots: '@expo/plist@0.1.3': dependencies: - '@xmldom/xmldom': 0.8.13 + '@xmldom/xmldom': 0.8.15 base64-js: 1.5.1 xmlbuilder: 14.0.0 '@expo/plist@0.8.1': dependencies: - '@xmldom/xmldom': 0.8.13 + '@xmldom/xmldom': 0.8.15 base64-js: 1.5.1 xmlbuilder: 15.1.1 @@ -7859,9 +7860,9 @@ snapshots: '@ungap/structured-clone@1.3.3': {} - '@xmldom/xmldom@0.8.13': {} + '@xmldom/xmldom@0.8.15': {} - '@xmldom/xmldom@0.9.10': {} + '@xmldom/xmldom@0.9.12': {} abab@2.0.6: {} @@ -11118,7 +11119,7 @@ snapshots: plist@3.1.1: dependencies: - '@xmldom/xmldom': 0.9.10 + '@xmldom/xmldom': 0.9.12 base64-js: 1.5.1 xmlbuilder: 15.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 85de6cb..9d74419 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -48,7 +48,8 @@ minimumReleaseAgeExclude: # no other home, so this file carries the old Yarn pins and scoped security # fixes. Yarn's `**/a/b` globs become pnpm's `a>b` selectors. overrides: - "@expo/plist>@xmldom/xmldom": 0.8.13 + "@expo/plist>@xmldom/xmldom": 0.8.15 + "plist>@xmldom/xmldom": 0.9.12 "@typescript-eslint/typescript-estree>minimatch": 9.0.7 brace-expansion@<1.1.18: ^1.1.18 brace-expansion@>=4.0.0 <5.0.9: ^5.0.9 diff --git a/tools/dependency-security-check.mjs b/tools/dependency-security-check.mjs index 2f3b1ff..4384dd8 100644 --- a/tools/dependency-security-check.mjs +++ b/tools/dependency-security-check.mjs @@ -147,6 +147,44 @@ assert.equal(randomCalls, 0); const pnpmStore = join(process.cwd(), "node_modules/.pnpm"); const installedPackages = readdirSync(pnpmStore); +const packageInStore = (name, version) => { + const directory = installedPackages.find((entry) => + entry.startsWith(`${name.replace("/", "+")}@${version}`), + ); + + assert.ok(directory, `${name} ${version} is installed`); + + return join(pnpmStore, directory, "node_modules", name, "package.json"); +}; + +for (const version of ["0.8.15", "0.9.12"]) { + const packageJson = packageInStore("@xmldom/xmldom", version); + const { DOMImplementation, XMLSerializer } = + requireFrom(packageJson)("@xmldom/xmldom"); + const document = new DOMImplementation().createDocument(null, "root", null); + + assert.throws( + () => document.createEntityReference("safe; &x"), + /not a valid xml name/, + ); + + const reference = document.createEntityReference("safe"); + reference.nodeName = "safe; &x"; + const serializer = new XMLSerializer(); + const serialize = + version === "0.8.15" + ? () => + serializer.serializeToString(reference, false, undefined, { + requireWellFormed: true, + }) + : () => + serializer.serializeToString(reference, { + requireWellFormed: true, + }); + + assert.throws(serialize, /not a valid XML Name/); +} + for (const version of ["3.15.1", "4.3.1"]) { const directory = installedPackages.find((entry) => entry.startsWith(`js-yaml@${version}`), @@ -177,6 +215,10 @@ for (const version of ["3.15.1", "4.3.1"]) { } for (const vulnerablePackage of [ + "@xmldom+xmldom@0.8.13", + "@xmldom+xmldom@0.8.14", + "@xmldom+xmldom@0.9.10", + "@xmldom+xmldom@0.9.11", "js-yaml@3.15.0", "js-yaml@4.3.0", "nanoid@3.3.16", @@ -190,5 +232,5 @@ for (const vulnerablePackage of [ } console.log( - "Dependency security checks passed for js-yaml, nanoid and image-size.", + "Dependency security checks passed for @xmldom/xmldom, js-yaml, nanoid and image-size.", );