Skip to content

fix(web): normalize PDF attachment filenames safely - #175

Draft
vincentkoc wants to merge 1 commit into
mainfrom
fix/clickclack-pdfjs-filename-normalization
Draft

fix(web): normalize PDF attachment filenames safely#175
vincentkoc wants to merge 1 commit into
mainfrom
fix/clickclack-pdfjs-filename-normalization

Conversation

@vincentkoc

@vincentkoc vincentkoc commented Aug 21, 2026

Copy link
Copy Markdown
Member
Additional instructions

MUST: Keep Allow edits from maintainers enabled for this PR so maintainers
can help update the branch when needed.

What Problem This Solves

Fixes a double-escaping path in the bundled PDF worker reported by CodeQL alert 8. The path came from pdfjs-dist@6.2.108, including its current upstream source.

Why This Change Was Made

There is no newer pdfjs-dist release carrying a fix. A pinned pnpm patch replaces the dependency's ordered replacement chain with one explicit filename-normalization scan, preserving the upstream contract while removing the vulnerable pattern. Both modern and legacy worker builds are patched, and checked-in web assets are regenerated from that dependency.

User Impact

PDF attachment filenames keep the same slash-normalization behavior without passing through a double-escaping replacement chain.

Evidence

  • Linux node v26.7.0 with pnpm 11.20.0
  • pnpm install --frozen-lockfile
  • TypeScript formatting and lint
  • FakeCo AWS tests
  • Root and workspace typechecks
  • Two consecutive pnpm build runs with byte-identical embedded web assets
  • node --check apps/api/internal/webassets/dist/_app/immutable/assets/pdf.worker.D3ahraH8.mjs
  • Full multi-stage docker build after copying the pnpm patch into the dependency-install stage
  • Docs site build
  • Exhaustive equivalence check over 9,841 input strings composed of slash, backslash, and ordinary characters through length 8
  • Production/dependency artifact LOC: +92/-16, including Docker patch availability, the pinned dependency patch, lock metadata, and 99%-similar generated asset renames. The positive delta records the dependency contract and removes the security finding without suppressing it.

Coordination

PR #169's web source changes remain separate and retain their contributor ownership. This PR regenerates assets from current main; it does not copy or supersede that source work.

@clawsweeper

clawsweeper Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@clawsweeper clawsweeper Bot added P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Aug 21, 2026
@clawsweeper

clawsweeper Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed August 21, 2026, 11:23 AM ET / 15:23 UTC.

ClawSweeper review

What this changes

The PR applies a locked pdfjs-dist patch to normalize PDF attachment filenames in one scan, updates Docker dependency installation, and regenerates embedded web assets.

Merge readiness

⚠️ Ready for maintainer review - 2 items remain

Keep open: current main still has the reported replacement chain, while this member-authored draft needs explicit maintainer review after Playwright E2E completes.

Priority: P1
Reviewed head: 546754c38f30c25d8e441cf91181a5930f27bf6a
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) The patch is focused, source-equivalent across the tested filename alphabet, and all completed checks pass; Playwright E2E remains in progress.
Proof confidence 🌊 off-meta tidepool Not applicable: The author is a repository member, so the external-contributor proof gate does not apply; the PR body nonetheless reports build, Docker, and equivalence validation.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: The author is a repository member, so the external-contributor proof gate does not apply; the PR body nonetheless reports build, Docker, and equivalence validation.
Evidence reviewed 5 items Current main remains affected: Current main’s embedded worker still uses the ordered three-replacement filename normalization chain; the branch replaces it with one scan.
Patch and install integration: The branch patches modern and legacy workers, records the pnpm patch hash, and copies the patch directory before Docker’s frozen install.
Worker is deployed: The artifact viewer imports pdfjs-dist’s worker by URL, and the generated application chunk resolves that URL to the regenerated worker asset.
Findings None None.
Security None None.

Live Verification

Command: pnpm build:web

Result: PASS (completed)

runner@runnervm76f27:/tmp/clawsweeper-live-proof-175-vFcLxw/target$ pnpm build:web
$ pnpm --filter @clickclack/web build && node scripts/normalize-web-dist.mjs apps/web/dist
$ vite build
pnpm build:web
vite v8.2.1 building ssr environment for production...
transforming...✓ 294 modules transformed.
rendering chunks...
vite v8.2.1 building client environment for production...
transforming...[plugin rolldown:vite-resolve] Module "stream" has been externalized for browser compatibility, imported by "/tmp/clawsweeper-live-proof-175-vFcL
xw/target/node_modules/.pnpm/sax@1.6.1/node_modules/sax/lib/sax.js". See https://vite.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibili
ty for more details.
✓ 310 modules transformed.
rendering chunks...
computing gzip size...
.svelte-kit/output/client/_app/version.json                                                                    0.01 kB │ gzip:   0.03 kB
.svelte-kit/output/client/_app/immutable/assets/geist-mono-symbols2-wght-normal.CO5SzqOn.woff2                 5.81 kB
.svelte-kit/output/client/_app/immutable/assets/geist-mono-cyrillic-ext-wght-normal.X_5orZeX.woff2             6.17 kB
.svelte-kit/output/client/_app/immutable/assets/geist-cyrillic-ext-wght-normal.DjL33-gN.woff2                  7.42 kB
.svelte-kit/output/client/_app/immutable/assets/geist-mono-vietnamese-wght-normal.DadHysG0.woff2               7.69 kB
.svelte-kit/output/client/_app/immutable/assets/geist-vietnamese-wght-normal.6IgcOCM7.woff2                    8.00 kB
.svelte-kit/output/client/_app/immutable/assets/bricolage-grotesque-vietnamese-wght-normal.BUzh504Q.woff2      8.60 kB
.svelte-kit/output/client/_app/immutable/assets/geist-mono-cyrillic-wght-normal.DiZS0aHC.woff2                12.94 kB
.svelte-kit/output/client/_app/immutable/assets/geist-mono-latin-ext-wght-normal.Bwz-egvJ.woff2               14.69 kB
.svelte-kit/output/client/_app/immutable/assets/geist-cyrillic-wght-normal.BEAKL7Jp.woff2                     15.08 kB
.svelte-kit/output/client/_app/immutable/assets/geist-latin-ext-wght-normal.DC-KSUi6.woff2                    16.51 kB
.svelte-kit/output/client/_app/immutable/assets/bricolage-grotesque-latin-ext-wght-normal.CcLUaPy7.woff2      18.66 kB
.svelte-kit/output/client/.vite/manifest.json                                                                 20.44 kB │ gzip:   2.27 kB
.svelte-kit/output/client/_app/immutable/assets/geist-mono-latin-wght-normal.XN7g48iV.woff2                   23.12 kB
.svelte-kit/output/client/_app/immutable/assets/geist-latin-wght-normal.BgDaEnEv.woff2                        29.40 kB
.svelte-kit/output/client/_app/immutable/workers/office.worker-BdyMEpKK.js                                    39.73 kB
.svelte-kit/output/client/_app/immutable/assets/bricolage-grotesque-latin-wght-normal.DLoelf7F.woff2          41.34 kB
.svelte-kit/output/client/_app/immutable/workers/highlight.worker-DMiSXOFf.js                                916.51 kB
.svelte-kit/output/client/_app/immutable/assets/pdf.worker.D3ahraH8.mjs                                    2,223.35 kB
.svelte-kit/output/client/_app/immutable/assets/ThreadPanel.DFXp9CyT.css                                       0.07 kB │ gzip:   0.08 kB
.svelte-kit/output/client/_app/immutable/assets/13.ByCz7Du1.css                                                2.79 kB │ gzip:   0.94 kB
.svelte-kit/output/client/_app/immutable/assets/QuoteBlock.DGJuMNIX.css                                        3.05 kB │ gzip:   1.01 kB
.svelte-kit/output/client/_app/immutable/assets/12.hzXFZBW8.css                                                3.64 kB │ gzip:   1.13 kB
.svelte-kit/output/client/_app/immutable/assets/ChatApp.D0d8bGTy.css                                           3.85 kB │ gzip:   1.04 kB
.svelte-kit/output/client/_app/immutable/assets/ProductSite.4q0lYJor.css                                      15.73 kB │ gzip:   3.69 kB
.svelte-kit/output/client/_app/immutable/assets/0.C0ts6lYR.css     
… output truncated …

Assertions:

  • PASS expect_output: built in

How this fits together

ClickClack’s artifact viewer gives the browser a pdfjs-dist worker to parse PDF attachments. The web build copies that worker into Go-embedded web assets, so the dependency patch must work in both local and Docker installs.

flowchart LR
A[PDF attachment] --> B[Artifact viewer]
B --> C[PDF worker]
C --> D[Filename normalization]
D --> E[Web build]
E --> F[Embedded web assets]
F --> G[Go web server]
Loading

Decision needed

Question Recommendation
Should this member-authored draft security remediation be approved and marked ready once the in-progress Playwright E2E check completes? Approve after checks: Confirm the CodeQL remediation and mark the PR ready when Playwright E2E finishes successfully.

Why: Repository policy keeps member-authored PRs open for explicit maintainer handling, and this changes a bundled third-party PDF worker used for attachment previews.

Before merge

  • Complete next step (P2) - Explicit maintainer approval is required for this member-authored draft after the remaining E2E check completes.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Changed surface 13 files affected; 92 added, 16 removed One locked dependency patch is accompanied by Docker install support and regenerated embedded assets.
Production vs test delta production +92/-16, tests +0 The production delta is bounded to patch metadata, worker output, and asset-reference renames.

Technical review

Best possible solution:

Land the narrow locked dependency patch once a maintainer confirms the alert remediation and the remaining Playwright E2E check completes, retaining regenerated worker assets as the deployable output.

Do we have a high-confidence way to reproduce the issue?

No live exploit reproduction was supplied; current main contains the reported ordered replacement chain, so the path is source-reproducible but was not independently replayed.

Is this the best way to solve the issue?

Yes. A version-locked pnpm patch plus regenerated worker assets is the narrowest maintainable remedy while no newer upstream release is identified in the PR.

AGENTS.md: found, but no applicable review policy affected this item.

Codex review notes: model internal, reasoning high; reviewed against e617cb5f1842.

Labels

Label justifications:

  • P1: This remediates a reported code-scanning issue in the PDF worker that processes attachment filenames.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The author is a repository member, so the external-contributor proof gate does not apply; the PR body nonetheless reports build, Docker, and equivalence validation.

Evidence

What I checked:

Likely related people:

  • Vincent Koc: Vincent authored the two immediately preceding current-main commits and this focused PDF dependency repair. (role: recent current-main contributor and patch author; confidence: medium; commits: e617cb5f1842, 9810127c748d, 546754c38f30; files: patches/pdfjs-dist@6.2.108.patch, apps/api/internal/webassets/dist/_app/immutable/assets/pdf.worker.D3ahraH8.mjs)
  • Peter Steinberger: Blame attributes the current main pdfjs-dist manifest line to Peter’s v0.3.1 release commit. (role: dependency and release provenance contributor; confidence: medium; commits: 18acea79465c; files: apps/web/package.json)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Let Playwright E2E complete, then have a maintainer confirm the alert closure and mark the draft ready.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (1 earlier review cycle)
  • reviewed 2026-08-21T11:13:23.950Z sha ab830ae :: needs maintainer review before merge. :: none

@vincentkoc
vincentkoc force-pushed the fix/clickclack-pdfjs-filename-normalization branch 2 times, most recently from 54da493 to e64f5a2 Compare August 21, 2026 15:03
@vincentkoc
vincentkoc force-pushed the fix/clickclack-pdfjs-filename-normalization branch from e64f5a2 to 546754c Compare August 21, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant