Skip to content

security(pdf): remediate GHSA-hq66-cqwq-w95j before v1.4 (pdfjs-dist 5.7.284 in affected range) #121

Description

@mrsibe

v1.4 release blocker

pdfjs-dist is pinned in dependencies and is the parsing engine for the app's core input, so a
known-high advisory cannot ship in v1.4. The library is affected; the reachability of the exploit
in KnowNote is a separate question, and the evidence below says it is not currently reachable —
which is why this is a scheduled, deliberate upgrade rather than a fire. It is still a blocker,
because "not reachable" here rests on our own call sites rather than on anything the library
guarantees.

Advisory

GHSA GHSA-hq66-cqwq-w95j
CVE CVE-2026-16633
Severity High — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N
Affected pdfjs-dist >=5.6.83 <6.2.108
Fixed 6.2.108
Installed here 5.7.284 — inside the affected range (package.json declares ^5.4.530; the lockfile resolves to 5.7.284)

Advisory impact statement, verbatim:

If PDF.js is used to load a malicious PDF, and PDF.js is configured with enableScripting set to
true (which is the default value) and no CSP for disallowing script-src, unrestricted
attacker-controlled JavaScript will be executed in the context of the hosting domain.

Official workarounds: "Set enableScripting to false or set a CSP."

Exploit conditions in KnowNote — checked, not assumed

Both PDF paths were inspected. Neither opts into the vulnerable path, and neither instantiates the
component that would exercise it:

Path Entry point Options passed What it uses the document for
main src/main/services/loaders/PdfLoader.ts:68 getDocument({ data, password }) — no enableScripting getMetadata(), page.getTextContent()
renderer src/renderer/src/components/notebook/source/reader/PdfSourceReader.tsx:211 getDocument({ data }) — no options page.render() (canvas) + pdfjs.TextLayer

The advisory's prerequisite is enableScripting: true plus the code that acts on it:

  • enableScripting is consumed by AnnotationLayer (annotation JS actions) and by the viewer's
    PDFScriptingManager, which is what actually executes document JavaScript.
  • KnowNote builds no AnnotationLayer and imports no pdfjs-dist/web/*. In the installed
    package, PDFScriptingManager appears only in web/pdf_viewer.mjs — grep -c PDFScriptingManager
    against the API build build/pdf.mjs is 0.
  • Bare pdfjs-dist resolves to main: build/pdf.mjs, i.e. the API build, for the renderer; the
    main process imports pdfjs-dist/legacy/build/pdf.mjs. The only other import is the parsing
    worker (build/pdf.worker.min.mjs?url).
  • The API build contains no eval() / new Function() scripting primitive: the annotation layer's
    _bindJSAction only binds DOM listeners that dispatch into a sandbox the viewer is expected to
    provide.

Conclusion: the stated exploit conditions are not met on either path today. This must be
re-verified as part of this issue, because the conclusion depends on our call sites: adding an
annotation layer, or the #71 renderer-side PDF spike, would put the vulnerable code back in scope.

Acceptance

  • Exploit conditions re-confirmed on both paths (main PdfLoader, renderer PdfSourceReader) and the finding recorded, so the reasoning survives the next reader
  • enableScripting: false passed explicitly on both getDocument calls — corrected, this was not implementable. enableScripting is declared on AnnotationLayer / AnnotationLayerBuilder / PDFViewer, and is not a DocumentInitParameters member, so TypeScript rejects it on getDocument (TS2353). The advisory's workaround targets the viewer, which this app does not use, so there is no switch to flip at our call sites.
    The mitigation is therefore structural, and is now a CI-enforced invariant rather than a one-time inspection: test/pdfjsScriptingBoundary.test.ts fails if anything imports the viewer build (where PDFScriptingManager lives), constructs the annotation layer or scripting manager, or sets enableScripting: true — with a self-check so it cannot pass by scanning nothing.
  • Evaluate whether a CSP is additionally warranted for the reader, given the reader renders untrusted documents in the renderer process
  • Upgrade to pdfjs-dist >=6.2.108; confirm 6.x is the first fixed line and record the resolved version
  • Reader regression: page render, text layer selection ([Feat] Selection → excerpt to Note, anchored back to its source #73 excerpts depend on it), highlighting by bbox, worker loading, zoom
  • main PdfLoader regression: text extraction and page/structure mapping (test/fixtures/sample.pdf, plus the existing parser tests)
  • Packaged artifact verified on Windows and macOS, not just dev — this dependency is externalized and ships in app.asar, so a dev-only check proves nothing about the shipped build
  • npm audit --omit=dev clean against pdfjs-dist (note: needs --registry=https://registry.npmjs.org; the configured mirror has no audit endpoint)
  • Rollback plan recorded: what breaks if 6.x is rejected, and how the app behaves on the previous version

Prefer not to ship at all

Even if enableScripting: false demonstrably blocks the current exploit path, the intent is that
v1.4 does not ship a version inside a known-high affected range. Untrusted PDFs are the normal
input model for this app, not an edge case, so a known-high in that path should not be deferred to
a later release.

If the 6.x upgrade turns out to be large, this becomes a decision point rather than an automatic
deferral — bring the comparison back here, do not silently push it to v1.5.

Explicitly not in scope

Background

Found while running the dependency audit for #62 (pdfjs-dist is one of the three dependencies in
its scope). Audit command and result:

$ npm audit --omit=dev --registry=https://registry.npmjs.org
# npm audit report
pdfjs-dist  >=5.6.83 <6.2.108
Severity: high
PDF.js: Arbitrary JavaScript execution upon opening a malicious PDF
1 high severity vulnerability

Status

Addressed by #124. The current exploit path is not reachable under the inspected call sites — that is a statement about our code (no AnnotationLayer, no viewer import, and PDFScriptingManager absent from the API build), not a claim that the library is safe, and it is now guarded by a test rather than assumed. The renderer additionally already sets script-src 'self' with no 'unsafe-inline'/'unsafe-eval', so the advisory's "no CSP" prerequisite is not met either.

Upgraded to 6.3.289; the only API breakage was destroy() moving from PDFDocumentProxy to PDFDocumentLoadingTask (three call sites). npm audit --omit=dev now reports 0 vulnerabilities. Reader rendering regression remains manual QA.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdependenciesDependency updates

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions