Skip to content

Replace abandoned exceljs dependency with @e965/xlsx - #6165

Open
jonnalley wants to merge 1 commit into
mainfrom
chore/replace-vulnerable-exceljs
Open

Replace abandoned exceljs dependency with @e965/xlsx#6165
jonnalley wants to merge 1 commit into
mainfrom
chore/replace-vulnerable-exceljs

Conversation

@jonnalley

@jonnalley jonnalley commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

What changed

Replaces the exceljs npm dependency (pinned at 4.4.0) with @e965/xlsx (SheetJS Community Edition, republished to npm). exceljs was Snyk-flagged for several CVEs, and investigation confirmed the project is effectively abandoned: no commit to master since 2024-01-12, no stable release since 2023-10-19 (4.4.0 is still npm's latest), 802 open issues dating back to 2015, and zero upstream response to 4 new CVEs disclosed— none of which have a fixed version.

A codebase audit found exceljs was only ever used write-only with app-trusted data (no .load()/parsing of untrusted input in shipped code), so there was no active exploit path today, but continuing to depend on an abandoned library was judged not worth the risk. @e965/xlsx was chosen over plain xlsx (its npm build is 4+ years behind SheetJS's actual patched releases) and over write-excel-file/read-excel-file (two packages with no shared workbook model — a bigger structural change for no benefit given this app never loads-and-mutates an existing workbook).

  • frontend/package.json / frontend/bun.lock: removed exceljs, added @e965/xlsx pinned to an exact version (0.20.3, no ^/~), matching this repo's existing exact-pin convention.
  • frontend/src/helpers/tableExport.helpers.js: rewrote exportTableToXlsx and exportMultiSheetToXlsx on SheetJS's API (XLSX.utils.aoa_to_sheet / book_new / book_append_sheet / write) instead of ExcelJS's workbook/worksheet object model. Extracted the per-cell currency-numFmt logic (previously duplicated in both functions) into a shared applyCurrencyFormat helper.
  • frontend/src/helpers/tableExport.helpers.test.js: updated the read-back assertions to SheetJS equivalents (XLSX.read, wb.Sheets[name], cell.z instead of cell.numFmt). Also fixed two assertions that assumed ExcelJS's "no format" semantics (undefined/falsy) — SheetJS's default for an unformatted cell is the string "General", so those now assert .not.toBe(CURRENCY_FORMAT) instead of .toBeFalsy().

Issue

N/A — no tracked ticket for this dependency swap.

How to test

  • cd frontend && bun run test --watch=false src/helpers/tableExport.helpers.test.js — all 8 tests pass.
  • cd frontend && bun run test --watch=false — full suite passes (359/360 files, 4308 tests).
  • cd frontend && bun run build — production Vite build succeeds with no bundling issues for @e965/xlsx.
  • Manual verification performed: generated a real .xlsx via the same code path outside the test harness and inspected it at the byte/XML level — file identifies it as genuine "Microsoft Excel 2007+", and styles.xml/sheet1.xml show the currency numFmt correctly applied to the expected cells.
  • To manually verify in the app: docker compose up db data-import backend frontend --build, trigger any table export (e.g. Agreements list, Budget Line List), and confirm the downloaded .xlsx opens correctly with headers, data, and currency-formatted columns.

A11y impact

  • No accessibility-impacting changes in this PR

Storybook

  • No UI component changes in this PR
  • Story added for new component in src/components/UI/
  • Story updated to reflect changed props/states
  • N/A — change is page-specific or non-visual

Screenshots

N/A — dependency/library swap only, no visual or markup change.

Definition of Done Checklist

  • OESA: Code refactored for clarity
  • OESA: Dependency rules followed
  • Automated unit tests updated and passed
  • Automated integration tests updated and passed
  • Automated quality tests updated and passed
  • Automated load tests updated and passed
  • Automated a11y tests updated and passed
  • Automated security tests updated and passed
  • 90%+ Code coverage achieved
  • Form validations updated

Links

N/A

@jonnalley jonnalley changed the title chore: replace abandoned exceljs dependency with @e965/xlsx Replace abandoned exceljs dependency with @e965/xlsx Aug 27, 2026
@jonnalley jonnalley self-assigned this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants