Skip to content

Security hardening and dead-code pruning for public release - #59

Merged
scscodes merged 1 commit into
mainfrom
claude/public-release-security-review-nn08at
Jun 10, 2026
Merged

Security hardening and dead-code pruning for public release#59
scscodes merged 1 commit into
mainfrom
claude/public-release-security-review-nn08at

Conversation

@scscodes

Copy link
Copy Markdown
Owner

Pre-public-release security review and cleanup pass. Net −1,138 lines; all 339 tests pass, tsc clean, bundle builds, npm audit down from 3 critical / 2 high to 2 moderate (both transitive inside npm's own bundle, dev-only; production deps were already clean).

Security fixes

Severity Finding Fix
High hygiene.cleanup was palette/menu-exposed and deleted files with no confirmation (the confirm dialog only lives in the deleteFile wrapper); any extension could invoke it programmatically Handler now defaults to dryRun: true — deletion requires explicit dryRun: false, which only the modal-confirmed Delete File flow passes. Removed from palette and tree context menu
Med Deleting a symlink deleted its target (path-guard realpaths the candidate) deleteFile guards the parent directory and lstats/unlinks the entry itself; directories refused
Med process.cwd() fallbacks let the scanner/analytics crawl an arbitrary cwd with no workspace open (incl. the hourly background scan) Handlers return WORKSPACE_NOT_FOUND; background timer only arms with a real workspace folder
Med infrastructure/workspace.ts exported an unguarded resolveWorkspacePath shadowing the security one (dead module) Deleted
Med meridian.security.logging.sensitive was advertised but never enforced Run-log error messages now pass through the redaction sanitizer (injected into CommandRouter, covered by a new test)
Low Latent git argument-injection on unvalidated refs in unused provider methods (diff, getCommitRange, getMergeBase, …) Methods removed entirely (zero call sites)
Low Webview CSP over-breadth (img-src https:, host-source script-src); session-briefing webview not covered by the security test Nonce-only script-src, img-src https: dropped, explicit connect-src 'none' everywhere; all three webviews pinned by webview-security.test.ts with new nonce/unsafe-eval assertions
Low Unescaped enum/numeric webview sinks (defense-in-depth); LM payload serialized before egress check Escaped/coerced; egress policy check moved before serialization

No exploitable XSS was found — all attacker-influenced strings (commit messages, authors, branch names, paths, AI prose) were already escaped or set via textContent.

Correctness & efficiency

  • Large-file scan now uses fs.stat metadata instead of reading every workspace file into memory hourly (also fixes >512 MB files being silently skipped due to ERR_STRING_TOO_LONG)
  • Root-anchored ignore patterns (/dist) were silently ineffective in the scan path (absolute vs relative mismatch) — paths are now matched workspace-relative
  • Workspace walker prunes .git/node_modules during traversal and pre-compiles glob regexes; Set-based dedupe replaces O(n²) includes
  • Impact-analysis cache key now includes functionName (analyzing function B after A in the same file returned A's cached results)
  • pruneCandiatespruneCandidates typo fixed in the exported report schema (last chance before the schema is public)
  • Explorer "Delete File" hidden on folders; broken no-op palette entries hidden

Dead code removed (~1,200 lines)

Speculative scaffolding for domains that don't exist: workflow/agent/cache telemetry events + InMemoryTelemetrySink, permission/rate-limit middleware, streamProse, CHAT/WORKFLOW/AGENT/SKILL error-code namespaces, the 240-line telemetry metadata table, GIT_DEFAULTS/CHAT_SETTINGS/LOG_SETTINGS constants, retry-config plumbing, and 8 unused GitProvider methods.

Tooling

  • vitest 1.x → 4.x (clears the critical/high dev-dep audit findings; two test-mock fixes for vitest 4's CJS-interop and constructor-mock changes)
  • New SECURITY.md documenting network posture (no egress except policy-gated git ops + optional VS Code LM API), local-only telemetry, and filesystem boundaries

Known items deliberately not changed (noted for review)

  • Deletion is permanent (fs.unlink), not trash — the modal says "cannot be undone", which is accurate; switching to vscode.workspace.fs.delete({useTrash}) would need vscode plumbed into the infra layer
  • Hourly background scan still runs a synchronous ts.createProgram (bounded by 5-min TTL cache); making it a worker is a larger change
  • pull(branch) validates but ignores its branch argument (runs plain git pull); behavior-preserving
  • Webview context-menu JS is triplicated across the three bundles (acknowledged in code comments)

https://claude.ai/code/session_01NTdZdJhedr99fwxZrpdGka


Generated by Claude Code

Security fixes:
- hygiene.cleanup now defaults to dry-run; deletion requires explicit
  dryRun=false (only the modal-confirmed Delete File flow opts in), and
  the command is no longer exposed in the palette or tree context menu
- deleteFile operates on the directory entry itself (lstat + parent
  guard), so deleting a symlink no longer removes its target; directory
  deletion is refused
- scan/analytics handlers fail with WORKSPACE_NOT_FOUND instead of
  falling back to process.cwd(); background scan timer no longer binds
  to cwd when no workspace folder is open
- removed unguarded resolveWorkspacePath duplicate (infrastructure/
  workspace.ts, dead module shadowing the security path-guard)
- security.logging.sensitive is now enforced: run-log error messages
  pass through the redaction sanitizer (injected into CommandRouter)
- LM egress policy check now runs before payload serialization
- webview CSPs tightened to nonce-only script-src, img-src https:
  removed, explicit connect-src 'none' everywhere; session-briefing
  webview added to the pinned security test
- escaped remaining payload-derived webview sinks (risk/category enums,
  legend labels) and coerced numeric fields; esc() helpers aligned

Correctness/efficiency:
- large-file scan uses fs.stat metadata instead of reading every file
  into memory (also fixes >512MB files being silently skipped)
- ignore patterns now match workspace-relative paths, fixing silently
  ignored root-anchored .gitignore/.meridianignore entries
- workspace walker prunes .git/node_modules during traversal and
  pre-compiles glob regexes; Set-based dedupe in scan buckets
- impact-analysis cache key includes functionName (stale-result bug
  when analyzing two functions in the same file)
- dead-code analyzer workspace containment uses a separator-terminated
  prefix; fixed duplicate condition in impact-visitor path heuristic
- prune categories are validated against the known enum
- fixed pruneCandiates -> pruneCandidates typo in the report schema

Dead code removed (~1,200 lines):
- unused GitProvider methods (diff/getCommitRange/getMergeBase/stage/
  reset/getChanges/getDiff/getUncommittedDiff) - also closes a latent
  git argument-injection surface on unvalidated refs
- speculative telemetry (workflow/agent/cache events, InMemoryTelemetry
  sink), permission/rate-limit middleware, streamProse, CHAT/WORKFLOW/
  AGENT/SKILL error-code namespaces, GIT_DEFAULTS/CHAT_SETTINGS/
  LOG_SETTINGS constants, retry-config scaffolding

Tooling:
- vitest 1.x -> 4.x (clears 3 critical / 2 high dev-dependency audit
  findings; prod deps were already clean)
- added SECURITY.md documenting network posture, telemetry, and
  filesystem boundaries

https://claude.ai/code/session_01NTdZdJhedr99fwxZrpdGka
@scscodes
scscodes marked this pull request as ready for review June 10, 2026 10:49
@scscodes
scscodes merged commit 99b2460 into main Jun 10, 2026
1 check passed
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 2.13.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@scscodes
scscodes deleted the claude/public-release-security-review-nn08at branch July 3, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants