Skip to content

Harden CI permissions and CSV export - #36

Open
CMonnin wants to merge 4 commits into
mainfrom
hardening
Open

CMonnin wants to merge 4 commits into
mainfrom
hardening

Conversation

@CMonnin

@CMonnin CMonnin commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Summary

CI permissions scoped per job; actions pinned; CSV export escaped.

 permissions:
-  contents: read
-  pages: write
-  id-token: write
+  {}                      # workflow default
 jobs:
   build:                  # runs on PRs, executes pnpm install
+    permissions: { contents: read }
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@<sha> # v4
+      with: { persist-credentials: false }
   deploy:
+    permissions: { pages: write, id-token: write }
 escapeCSVField(value)
+  if string starts with = + - @ \t \r
+    prefix "'"            # stop Excel evaluating notes / participant IDs
-  if contains ","
-    wrap in quotes
+  if contains , " \r \n
+    double inner quotes, wrap in quotes

.npmrc quarantine was never enforced — minimum-release-age needs pnpm ≥10.16, block-exotic-subdeps ≥10.26, and packageManager pinned 10.10.0 (which CI uses), so both were silently ignored:

-"packageManager": "pnpm@10.10.0"
+"packageManager": "pnpm@10.34.6"

Lockfile regenerated with the quarantine active, in-range only (no majors):

-vite    6.4.3
+vite    6.4.4    # GHSA-9jrq-w75r-8gcw + others; 6.4.5 held back (<7 days)
-eslint  9.39.4
+eslint  9.39.5
 rollup  4.64.10  # 4.65.1 held back (<7 days)
 + transitive: brace-expansion, js-yaml, postcss, nanoid, source-map-js, postcss-selector-parser

Docs (docs/requirements.txt), clearing the open Dependabot alerts:

-urllib3==2.7.0
+urllib3==2.8.0
-mkdocs-material==9.6.14
+mkdocs-material==9.7.7          # DOM XSS in search suggestions
-pymdown-extensions==10.21.3
+pymdown-extensions==11.0.1      # ReDoS, b64 path traversal

Also: .github/dependabot.yml for github-actions (keeps the SHA pins current), cancel-in-progress only for PRs so main deploys aren't cut off, empty-results guard in arrayToCSV, fixed nested <html> in index.html.

Evidence

  • Before: note said "dog", then =HYPERLINK("x") → "said "dog", then =HYPERLINK("x")" (malformed row); note =HYPERLINK("x") → written raw, executes in Excel
    After:
    "a,b"              -> "a,b"
    said "dog"         -> "said ""dog"""
    line1\nline2       -> "line1\nline2"   (quoted)
    =HYPERLINK("x")    -> "'=HYPERLINK(""x"")"
    -3 (number)        -> -3
    
  • Before: pnpm audit → 30 vulnerabilities (3 low, 9 moderate, 18 high)
    After: pnpm audit → No known vulnerabilities found
  • All 372 packages in the lockfile checked against registry publish times: none younger than 7 days.
  • Docs: pip check clean, mkdocs build --strict passes with the bumped pins.
  • tsc and vite build pass; eslint error count unchanged-or-lower vs main (all remaining are pre-existing).

Merge Danger

Door: two-way

Blast Radius: small

pnpm bump affects every contributor: corepack/pnpm/action-setup will fetch 10.34.6, and installs may now refuse packages under 7 days old (intended). pymdown-extensions is a major bump (10 → 11); docs build cleanly but eyeball the rendered site on RTD. Lockfile churn is otherwise tooling-only except @opendatacapture/runtime-v1, which stays at the already-resolved 1.16.6 (only the range floor moved).

CSV free-text fields beginning with = + - @ now carry a leading ' — downstream scripts parsing notes may see it. Deploy job needs pages/id-token at job level; if the first main run after merge fails to deploy, that's where to look.

🤖 Generated with Claude Code

CMonnin and others added 4 commits October 26, 2026 22:31
- Scope workflow permissions per job; build is read-only
- Pin actions to commit SHAs, add dependabot for github-actions
- Don't persist checkout credentials; don't cancel in-flight main deploys
- Escape CSV fields per RFC 4180 and neutralise formula injection
- Handle empty results in CSV export
- Fix nested <html> in index.html, add no-referrer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clears all 30 pnpm audit advisories (vite 6.4.3 -> 6.4.5, eslint
9.39.4 -> 9.39.5, transitive brace-expansion/js-yaml/postcss/nanoid).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
minimum-release-age needs pnpm >=10.16 and block-exotic-subdeps >=10.26;
the pinned 10.10.0 silently ignored both. Regenerated the lockfile with
the quarantine active, which drops vite to 6.4.4 and rollup to 4.64.10
(6.4.5 / 4.65.1 are under 7 days old). pnpm audit remains clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
urllib3 2.8.0, mkdocs-material 9.7.7 (search XSS),
pymdown-extensions 11.0.1 (ReDoS, b64 path traversal).
mkdocs build --strict passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant