From a3c032db7027b91d9ff1a2543d251759935c7d90 Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Thu, 21 May 2026 08:02:48 -0400 Subject: [PATCH 1/2] Updated Audit Deps command --- ...4-17-commands-audit-deps-stale-key-deps.md | 45 ------------------- .claude/commands/audit-deps.md | 14 +++--- 2 files changed, 9 insertions(+), 50 deletions(-) delete mode 100644 .claude/TODO/2026-04-17-commands-audit-deps-stale-key-deps.md diff --git a/.claude/TODO/2026-04-17-commands-audit-deps-stale-key-deps.md b/.claude/TODO/2026-04-17-commands-audit-deps-stale-key-deps.md deleted file mode 100644 index cea5ba1..0000000 --- a/.claude/TODO/2026-04-17-commands-audit-deps-stale-key-deps.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: audit-deps slash command lists stale "Key Dependencies" (next-auth, drizzle, AWS) not used in this project -severity: medium -tags: [drift, doc] -area: commands -files: [.claude/commands/audit-deps.md] -discovered: 2026-04-17 -discovered_by: commands-drift-check -status: open ---- - -## Problem -`.claude/commands/audit-deps.md` ends with a "Key Dependencies to Always Check" section that lists packages which are not part of this project. Running `/audit-deps` as written steers reviewers toward packages that do not exist in `package.json`, missing the real attack surface. - -Listed but not present in this repo: -- `next-auth` — project uses `better-auth` (see `package.json` line with `"better-auth": "^1.5.5"` and CLAUDE.md "Auth: Better Auth") -- `drizzle-orm` / `drizzle-kit` — no ORM is used; MP is accessed via REST through `MPHelper` -- `jsonwebtoken` / `bcryptjs` — not listed in `package.json` -- "Any AWS SDK packages" — no `@aws-sdk/*` in dependencies - -## Evidence -- `.claude/commands/audit-deps.md:48-54` — current "Key Dependencies to Always Check" list -- `package.json:20-88` — actual dependency graph (next 16, react 19, better-auth, docxtemplater, grapesjs, openai, zod v4; vitest 4, eslint 9, typescript 6) - -## Proposed fix -Rewrite the "Key Dependencies to Always Check" section to match what this codebase actually ships. Suggested replacement: - -```markdown -## Key Dependencies to Always Check -- next / eslint-config-next (framework security) -- react / react-dom (core framework) -- better-auth (auth/session) -- zod (validation — v4 API differs from v3) -- @grapesjs/react / grapesjs / grapesjs-mjml / mjml (template editor) -- docx / docxtemplater / docxtemplater-image-module-free / pizzip (Word merge) -- openai (LLM client, if used by tools) -- @react-pdf/renderer (PDF output) -- vitest / @vitest/coverage-v8 / @vitejs/plugin-react (test runner) -- typescript (toolchain) -``` - -While editing, also consider adding a note that `npm audit` on Next.js 16 / React 19 / Vitest 4 may flag peer-dep churn — call that out instead of having the reviewer chase phantom CVEs in packages that aren't installed. - -## Impact if not fixed -Medium. Reviewers running `/audit-deps` spend time evaluating packages that don't exist and may miss real CVEs in the packages that actually ship (Next.js 16, better-auth, grapesjs, openai, docx). Also lowers trust in the command library — the first drift a user notices tends to color their view of the rest. diff --git a/.claude/commands/audit-deps.md b/.claude/commands/audit-deps.md index dfd5d5e..b7a96b2 100644 --- a/.claude/commands/audit-deps.md +++ b/.claude/commands/audit-deps.md @@ -46,9 +46,13 @@ If the user requests, execute the recommended action plan: - Report final status ## Key Dependencies to Always Check -- next / next-auth (framework security) +- next / eslint-config-next (framework security) - react / react-dom (core framework) -- jsonwebtoken / bcryptjs (auth/crypto) -- drizzle-orm / drizzle-kit (database) -- Any AWS SDK packages -- Any packages with known historical vulnerabilities +- better-auth (auth/session) +- zod (validation — v4 API differs from v3) +- @grapesjs/react / grapesjs / grapesjs-mjml / mjml (template editor) +- docx / docxtemplater / docxtemplater-image-module-free / pizzip (Word merge) +- openai (LLM client, if used by tools) +- @react-pdf/renderer (PDF output) +- vitest / @vitest/coverage-v8 / @vitejs/plugin-react (test runner) +- typescript (toolchain) From a9b3b05d0e72fa198f3856ee954d2e0cbd6a0085 Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Thu, 21 May 2026 08:15:13 -0400 Subject: [PATCH 2/2] fix(deps): replace abandoned docxtemplater-image-module-free with docxtemplater-image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the critical xmldom advisory (GHSA-crh6-fp67-6883, CVSS 9.8) by replacing the abandoned docxtemplater-image-module-free@1.1.1 — which pinned the deprecated xmldom@0.1.x — with maintained docxtemplater-image@0.1.2, which uses @xmldom/xmldom@^0.9.7 (the same line as core docxtemplater@3.68). npm audit: 4 vulns (1 critical, 3 moderate) -> 2 moderate. The remaining 2 are postcss-in-next, unrelated and unfixable until Next 16.3 stable. API is identical: same getImage/getSize/centered options and {%token} placeholder syntax. No behavioral change to the Address Labels mail-merge feature; the upload-your-own-template flow continues to work unchanged. Verification: - npm run test:run: 45 files, 667 tests pass - npm run lint: clean - npm run build: clean Manual end-to-end browser test recommended before release (sample template upload -> merge -> open in Word -> verify barcode image rendering). Closes the xmldom-critical-vulnerability TODO. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...026-05-21-xmldom-critical-vulnerability.md | 64 ------------------- .claude/TODO/INDEX.md | 15 ++--- .../references/components/address-labels.md | 4 +- package-lock.json | 26 +++----- package.json | 2 +- src/components/address-labels/actions.test.ts | 2 +- src/components/address-labels/actions.ts | 2 +- .../address-labels/sample-template.ts | 2 +- ...ule-free.d.ts => docxtemplater-image.d.ts} | 2 +- 9 files changed, 24 insertions(+), 95 deletions(-) delete mode 100644 .claude/TODO/2026-05-21-xmldom-critical-vulnerability.md rename src/types/{docxtemplater-image-module-free.d.ts => docxtemplater-image.d.ts} (87%) diff --git a/.claude/TODO/2026-05-21-xmldom-critical-vulnerability.md b/.claude/TODO/2026-05-21-xmldom-critical-vulnerability.md deleted file mode 100644 index cbf7a2b..0000000 --- a/.claude/TODO/2026-05-21-xmldom-critical-vulnerability.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Critical xmldom vulnerability via docxtemplater-image-module-free -severity: critical -tags: [security] -area: components -files: [package.json, src/components/address-labels/] -discovered: 2026-05-21 -discovered_by: install-testing -status: open ---- - -## Problem - -`npm install` on a fresh clone pulls a **critical-severity** transitive dependency: `xmldom@*`. The package was deprecated by its maintainer years ago and carries a GHSA advisory with **"No fix available"** — the maintainer has abandoned it. `npm audit` flags this every time a customer installs the repo. - -The path is: - -``` -mpnext-tools -└── docxtemplater-image-module-free@1.1.1 - └── xmldom@* <-- critical, abandoned, no fix -``` - -`docxtemplater-image-module-free` is used by the Address Labels mail-merge feature to embed images in generated `.docx` files. The maintained replacement for `xmldom` is `@xmldom/xmldom`, but the image module itself has not been updated to use it. - -This is a real security concern: customers running `npm install` against this repo install a critical-severity, unmaintained XML parser. The audit warning will scare off security-conscious adopters and may show up in their own SBOM / vuln dashboards. - -CRITICAL_FLAG - -## Evidence - -- `package.json:47` — `"docxtemplater-image-module-free": "^1.1.1"` -- `npm audit` — reports `xmldom *` as critical, "No fix available" -- xmldom on npm: https://www.npmjs.com/package/xmldom (marked deprecated) -- @xmldom/xmldom on npm: https://www.npmjs.com/package/@xmldom/xmldom (maintained fork) - -## Proposed fix - -Two viable options — prefer (1) for speed, (2) for long-term hygiene: - -**Option 1 — Force the maintained fork via `package.json` overrides** (quick, ~5 min): - -```json -"overrides": { - "docxtemplater-image-module-free": { - "xmldom": "npm:@xmldom/xmldom@^0.9.0" - } -} -``` - -Then re-run `npm install` and `npm audit` to confirm the critical clears. The two packages have compatible APIs for the surface `docxtemplater-image-module-free` uses. - -**Option 2 — Replace the image module with a maintained alternative**: - -- Evaluate `docxtemplater-image-module` (commercial), `easy-template-x`, or a community-maintained fork -- Code change in `src/components/address-labels/` mail-merge surface -- Higher effort but removes the abandoned dependency entirely - -## Impact if not fixed - -- Every `npm install` surfaces a CRITICAL audit warning to customers -- Security-conscious adopters may block the repo from internal use -- The xmldom package can never be patched upstream — vulnerabilities discovered after deprecation will accumulate -- Affects every consumer of MPNext-Tools that uses the Address Labels mail-merge feature (server-side only, but still bundled) diff --git a/.claude/TODO/INDEX.md b/.claude/TODO/INDEX.md index e81ffd6..8881b44 100644 --- a/.claude/TODO/INDEX.md +++ b/.claude/TODO/INDEX.md @@ -8,6 +8,7 @@ last_updated: 2026-05-21 + # TODO Index @@ -19,16 +20,14 @@ All open TODOs dropped during the context-engineering review (2026-04-17) and an - **medium**: doc drift, missing test, refactor with real cost - **low**: nits, minor doc fixes, stylistic improvements -Total: **4 open TODOs**. +Total: **3 open TODOs**. --- ## By severity -### Critical (1) -| Area | Tags | Title | File | -|---|---|---|---| -| components | security | Critical xmldom vulnerability via docxtemplater-image-module-free | [→](2026-05-21-xmldom-critical-vulnerability.md) | +### Critical (0) +_none open_ ### High _none open_ @@ -47,8 +46,8 @@ _none open_ ## By tag -### security (1) -- xmldom-critical-vulnerability — critical +### security (0) +_none open_ ### bug (2) _see severity sections above; tag appears on items involving a functional defect_ @@ -74,7 +73,7 @@ _none open_ | Area | Count | |---|---| -| components | 4 | +| components | 3 | | auth | 0 | | mp-provider | 0 | | services | 0 | diff --git a/.claude/references/components/address-labels.md b/.claude/references/components/address-labels.md index 994b19f..1efaf8a 100644 --- a/.claude/references/components/address-labels.md +++ b/.claude/references/components/address-labels.md @@ -59,7 +59,7 @@ Tool route: `src/app/(web)/tools/addresslabels/page.tsx` → `address-labels.tsx - **POSTNET**: Legacy 5/9/11-digit tall/short bar barcode (no Mailer ID needed). - **Skip reasons** (`SkipReason` in `src/lib/dto/address-label.dto.ts:15`): `no_address`, `no_postal_code`, `opted_out` (`Bulk_Mail_Opt_Out=true`), `no_barcode` (only when `includeMissingBarcodes=false`), `no_household` (household mode only — contact lacks `Household_ID` so dedup cannot be guaranteed). - **Pre-encoded bar states**: `preEncodeBarcodes()` encodes once on the server into `LabelData.barStates` so the PDF/Word renderers are pure layout (see `../utils/barcodes.md`). -- **Mail-merge tab**: uploads a `.docx` with `{Name}`, `{AddressLine1}`, `{AddressLine2}`, `{City}`, `{State}`, `{PostalCode}`, `{%Barcode}` tokens plus `{#addresses}…{/addresses}` loop and `{#isNotLast}{/isNotLast}` conditional — barcodes become BMP images via `docxtemplater-image-module-free`. +- **Mail-merge tab**: uploads a `.docx` with `{Name}`, `{AddressLine1}`, `{AddressLine2}`, `{City}`, `{State}`, `{PostalCode}`, `{%Barcode}` tokens plus `{#addresses}…{/addresses}` loop and `{#isNotLast}{/isNotLast}` conditional — barcodes become BMP images via `docxtemplater-image`. - **LocalStorage persistence**: last-used `LabelConfig` stored under key `address-labels-config` (`src/app/(web)/tools/addresslabels/address-labels.tsx:15`). ## API / Interface @@ -177,7 +177,7 @@ const SERVICE_TYPES = [{id:'040',…}, {id:'300',…}, {id:'044',…}, {id:'700' ### mergeTemplate (.docx mail merge) - Rejects `templateBase64` if `Math.ceil(length * 0.75) > 5 * 1024 * 1024`. - `preEncodeBarcodes()` then builds `addresses[]` rows with `{ Name, AddressLine1, AddressLine2, City, State, PostalCode, Barcode: 'barcode_N', isNotLast }` plus a `Map` of BMP barcodes keyed by `barcode_N`. -- `Docxtemplater` with `docxtemplater-image-module-free` — `getImage` resolves key strings to buffers; `getSize` returns `[200, 25]` for the `Barcode` tag. +- `Docxtemplater` with `docxtemplater-image` — `getImage` resolves key strings to buffers; `getSize` returns `[200, 25]` for the `Barcode` tag. - Render errors containing "tag" are wrapped as `Template error: …`. ### Mail merge tab upload flow diff --git a/package-lock.json b/package-lock.json index ef25168..a2963e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "cmdk": "^1.1.1", "docx": "^9.6.1", "docxtemplater": "^3.68.5", - "docxtemplater-image-module-free": "^1.1.1", + "docxtemplater-image": "^0.1.2", "dotenv": "^17.3.1", "grapesjs": "^0.22.14", "grapesjs-mjml": "^1.0.8", @@ -75,6 +75,9 @@ "tw-animate-css": "^1.4.0", "typescript": "^6.0.3", "vitest": "^4.1.0" + }, + "engines": { + "node": ">=20" } }, "node_modules/@acemir/cssom": { @@ -7205,13 +7208,14 @@ "node": ">=0.10" } }, - "node_modules/docxtemplater-image-module-free": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/docxtemplater-image-module-free/-/docxtemplater-image-module-free-1.1.1.tgz", - "integrity": "sha512-aWOzVQN7ggDYjfoy3pTTNrcrZ7/CJrQcI9cT+hmyHE6nRLR67nt5yPFPe9hm9VWbfYIED2fi+3itOnF0TE/RWQ==", + "node_modules/docxtemplater-image": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/docxtemplater-image/-/docxtemplater-image-0.1.2.tgz", + "integrity": "sha512-4lT0CYxP+h7hdLSNSerFy4ljAHKEi/ERJYOR36kjoEtqfD/wetiWWQnsGvKYH5TY4vfWUZJRXqUQbsm0Zy95lA==", "license": "MIT", "dependencies": { - "xmldom": "^0.1.27" + "@xmldom/xmldom": "^0.9.7", + "docxtemplater": "^3.60.0" } }, "node_modules/dom-accessibility-api": { @@ -14714,16 +14718,6 @@ "dev": true, "license": "MIT" }, - "node_modules/xmldom": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", - "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==", - "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0", - "license": "(LGPL-2.0 or MIT)", - "engines": { - "node": ">=0.1" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 1522a2f..d3759fd 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "cmdk": "^1.1.1", "docx": "^9.6.1", "docxtemplater": "^3.68.5", - "docxtemplater-image-module-free": "^1.1.1", + "docxtemplater-image": "^0.1.2", "dotenv": "^17.3.1", "grapesjs": "^0.22.14", "grapesjs-mjml": "^1.0.8", diff --git a/src/components/address-labels/actions.test.ts b/src/components/address-labels/actions.test.ts index f53077d..550386c 100644 --- a/src/components/address-labels/actions.test.ts +++ b/src/components/address-labels/actions.test.ts @@ -72,7 +72,7 @@ vi.mock('pizzip', () => ({ default: class {}, })); -vi.mock('docxtemplater-image-module-free', () => ({ +vi.mock('docxtemplater-image', () => ({ default: class {}, })); diff --git a/src/components/address-labels/actions.ts b/src/components/address-labels/actions.ts index 6e2c8ef..363a0c8 100644 --- a/src/components/address-labels/actions.ts +++ b/src/components/address-labels/actions.ts @@ -23,7 +23,7 @@ import { preEncodeBarcodes } from '@/lib/barcode-helpers'; import { validateMailerId } from '@/lib/validation'; import Docxtemplater from 'docxtemplater'; import PizZip from 'pizzip'; -import ImageModule from 'docxtemplater-image-module-free'; +import ImageModule from 'docxtemplater-image'; import { imbBarcodeToBmp, postnetBarcodeToBmp } from '@/lib/barcode-image'; async function getSession() { diff --git a/src/components/address-labels/sample-template.ts b/src/components/address-labels/sample-template.ts index 02b967b..dfa1e2d 100644 --- a/src/components/address-labels/sample-template.ts +++ b/src/components/address-labels/sample-template.ts @@ -88,7 +88,7 @@ export async function generateSampleTemplate(): Promise { ], }), - // Barcode image placeholder (% prefix for docxtemplater-image-module-free) + // Barcode image placeholder (% prefix for docxtemplater-image) new Paragraph({ spacing: { after: 200 }, children: [new TextRun({ text: '{%Barcode}', size: 22, font: 'Arial' })], diff --git a/src/types/docxtemplater-image-module-free.d.ts b/src/types/docxtemplater-image.d.ts similarity index 87% rename from src/types/docxtemplater-image-module-free.d.ts rename to src/types/docxtemplater-image.d.ts index a24529c..4909d04 100644 --- a/src/types/docxtemplater-image-module-free.d.ts +++ b/src/types/docxtemplater-image.d.ts @@ -1,4 +1,4 @@ -declare module 'docxtemplater-image-module-free' { +declare module 'docxtemplater-image' { interface ImageModuleOptions { centered?: boolean; getImage: (tagValue: unknown, tagName: string) => Buffer | string;