From 98d6ddcbca0026746f21ccbeb70c93b5eacbace1 Mon Sep 17 00:00:00 2001 From: Bret Comnes Date: Sat, 5 Sep 2026 21:19:36 -0700 Subject: [PATCH 1/2] Remove vulnerable write-package dependency --- bin.js | 10 +++--- lib/helpers/add-package-dependencies.js | 24 +++++++++++++ lib/helpers/add-package-dependencies.test.js | 38 ++++++++++++++++++++ package.json | 3 +- 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 lib/helpers/add-package-dependencies.js create mode 100644 lib/helpers/add-package-dependencies.test.js diff --git a/bin.js b/bin.js index 7fd8d680..37712f23 100755 --- a/bin.js +++ b/bin.js @@ -19,9 +19,9 @@ import { inspect } from 'util' import { createServer } from '@domstack/sync' import { packageDirectory } from 'package-directory' import { readPackage } from 'read-pkg' -import { addPackageDependencies } from 'write-package' import { copyFile } from './lib/helpers/copy-file.js' +import { addPackageDependencies } from './lib/helpers/add-package-dependencies.js' import { DomStack } from './index.js' import { DomStackAggregateError } from './lib/helpers/domstack-aggregate-error.js' import { generateTreeData } from './lib/helpers/generate-tree-data.js' @@ -200,11 +200,9 @@ domstack eject actions: await addPackageDependencies( localPkgJson, { - dependencies: { - 'mine.css': mineVersion, - fragtml: fragtmlVersion, - 'highlight.js': highlightVersion, - }, + 'mine.css': mineVersion, + fragtml: fragtmlVersion, + 'highlight.js': highlightVersion, }) console.log('Done ejecting files!') diff --git a/lib/helpers/add-package-dependencies.js b/lib/helpers/add-package-dependencies.js new file mode 100644 index 00000000..7b9b1b18 --- /dev/null +++ b/lib/helpers/add-package-dependencies.js @@ -0,0 +1,24 @@ +import { readFile, writeFile } from 'node:fs/promises' + +/** + * Add production dependencies without normalizing unrelated package metadata. + * + * @param {string} packagePath + * @param {Record} dependencies + */ +export async function addPackageDependencies (packagePath, dependencies) { + const source = await readFile(packagePath, 'utf8') + const packageData = JSON.parse(source) + const indentation = source.match(/^[\t ]+(?=")/m)?.[0] ?? ' ' + const trailingNewline = source.endsWith('\n') ? '\n' : '' + + packageData.dependencies = { + ...packageData.dependencies, + ...dependencies, + } + + await writeFile( + packagePath, + JSON.stringify(packageData, null, indentation) + trailingNewline + ) +} diff --git a/lib/helpers/add-package-dependencies.test.js b/lib/helpers/add-package-dependencies.test.js new file mode 100644 index 00000000..7fb12c73 --- /dev/null +++ b/lib/helpers/add-package-dependencies.test.js @@ -0,0 +1,38 @@ +import assert from 'node:assert/strict' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import path from 'node:path' +import { test } from 'node:test' +import { addPackageDependencies } from './add-package-dependencies.js' + +test('adds dependencies while preserving unrelated package metadata', async (t) => { + const directory = await mkdtemp(path.join(tmpdir(), 'domstack-package-')) + const packagePath = path.join(directory, 'package.json') + t.after(() => rm(directory, { recursive: true, force: true })) + + await writeFile(packagePath, JSON.stringify({ + name: 'example', + private: true, + scripts: { test: 'node --test' }, + dependencies: { existing: '^1.0.0', replace: '^1.0.0' }, + }, null, '\t') + '\n') + + await addPackageDependencies(packagePath, { + added: '^2.0.0', + replace: '^2.0.0', + }) + + const source = await readFile(packagePath, 'utf8') + assert.ok(source.endsWith('\n')) + assert.match(source, /^\{\n\t"name"/) + assert.deepEqual(JSON.parse(source), { + name: 'example', + private: true, + scripts: { test: 'node --test' }, + dependencies: { + existing: '^1.0.0', + replace: '^2.0.0', + added: '^2.0.0', + }, + }) +}) diff --git a/package.json b/package.json index a3707173..85c2af57 100644 --- a/package.json +++ b/package.json @@ -86,8 +86,7 @@ "pino-pretty": "^13.1.3", "pretty": "^2.0.0", "pretty-tree": "^1.0.0", - "read-pkg": "^10.0.0", - "write-package": "^7.0.1" + "read-pkg": "^10.0.0" }, "devDependencies": { "@playwright/test": "^1.61.1", From 63c2daf19f4af5c25895d00798311d415fc79d57 Mon Sep 17 00:00:00 2001 From: Bret Comnes Date: Sat, 5 Sep 2026 21:58:10 -0700 Subject: [PATCH 2/2] Remove read-pkg from the eject command --- bin.js | 12 ++++++------ lib/helpers/add-package-dependencies.js | 2 +- lib/helpers/add-package-dependencies.test.js | 2 +- package.json | 3 +-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/bin.js b/bin.js index 37712f23..b2d99a01 100755 --- a/bin.js +++ b/bin.js @@ -18,7 +18,6 @@ import tree from 'pretty-tree' import { inspect } from 'util' import { createServer } from '@domstack/sync' import { packageDirectory } from 'package-directory' -import { readPackage } from 'read-pkg' import { copyFile } from './lib/helpers/copy-file.js' import { addPackageDependencies } from './lib/helpers/add-package-dependencies.js' @@ -30,9 +29,10 @@ import { createDomStackLogger } from './lib/logger.js' const __dirname = import.meta.dirname -async function getPkg () { - const pkgPath = resolve(__dirname, './package.json') - const pkg = JSON.parse(await readFile(pkgPath, 'utf8')) +/** @param {string} [pkgPath] */ +async function getPkg (pkgPath = resolve(__dirname, './package.json')) { + const source = await readFile(pkgPath, 'utf8') + const pkg = JSON.parse(source.replace(/^\uFEFF/, '')) return pkg } @@ -152,7 +152,7 @@ async function run () { } const localPkgJson = join(localPkg, 'package.json') - const localPkgJsonContents = await readPackage({ cwd: localPkg }) + const localPkgJsonContents = await getPkg(localPkgJson) const targetIsModule = localPkgJsonContents.type === 'module' const relativeSrc = relative(process.cwd(), src) @@ -162,7 +162,7 @@ async function run () { const targetGlobalStylePath = 'globals/global.css' const targetGlobalClientPath = `globals/global.client.${targetIsModule ? 'js' : 'mjs'}` - const tbPkgContents = await readPackage({ cwd: __dirname }) + const tbPkgContents = await getPkg() const mineVersion = tbPkgContents?.['dependencies']?.['mine.css'] const fragtmlVersion = tbPkgContents?.['dependencies']?.['fragtml'] const highlightVersion = tbPkgContents?.['dependencies']?.['highlight.js'] diff --git a/lib/helpers/add-package-dependencies.js b/lib/helpers/add-package-dependencies.js index 7b9b1b18..c63faef7 100644 --- a/lib/helpers/add-package-dependencies.js +++ b/lib/helpers/add-package-dependencies.js @@ -8,7 +8,7 @@ import { readFile, writeFile } from 'node:fs/promises' */ export async function addPackageDependencies (packagePath, dependencies) { const source = await readFile(packagePath, 'utf8') - const packageData = JSON.parse(source) + const packageData = JSON.parse(source.replace(/^\uFEFF/, '')) const indentation = source.match(/^[\t ]+(?=")/m)?.[0] ?? ' ' const trailingNewline = source.endsWith('\n') ? '\n' : '' diff --git a/lib/helpers/add-package-dependencies.test.js b/lib/helpers/add-package-dependencies.test.js index 7fb12c73..bc41b747 100644 --- a/lib/helpers/add-package-dependencies.test.js +++ b/lib/helpers/add-package-dependencies.test.js @@ -10,7 +10,7 @@ test('adds dependencies while preserving unrelated package metadata', async (t) const packagePath = path.join(directory, 'package.json') t.after(() => rm(directory, { recursive: true, force: true })) - await writeFile(packagePath, JSON.stringify({ + await writeFile(packagePath, '\uFEFF' + JSON.stringify({ name: 'example', private: true, scripts: { test: 'node --test' }, diff --git a/package.json b/package.json index 85c2af57..45b1afb5 100644 --- a/package.json +++ b/package.json @@ -85,8 +85,7 @@ "pino": "^10.3.1", "pino-pretty": "^13.1.3", "pretty": "^2.0.0", - "pretty-tree": "^1.0.0", - "read-pkg": "^10.0.0" + "pretty-tree": "^1.0.0" }, "devDependencies": { "@playwright/test": "^1.61.1",