Security hardening and dead-code pruning for public release - #59
Merged
Conversation
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
marked this pull request as ready for review
June 10, 2026 10:49
|
🎉 This PR is included in version 2.13.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pre-public-release security review and cleanup pass. Net −1,138 lines; all 339 tests pass,
tscclean, bundle builds,npm auditdown from 3 critical / 2 high to 2 moderate (both transitive inside npm's own bundle, dev-only; production deps were already clean).Security fixes
hygiene.cleanupwas palette/menu-exposed and deleted files with no confirmation (the confirm dialog only lives in thedeleteFilewrapper); any extension could invoke it programmaticallydryRun: true— deletion requires explicitdryRun: false, which only the modal-confirmed Delete File flow passes. Removed from palette and tree context menudeleteFileguards the parent directory andlstats/unlinks the entry itself; directories refusedprocess.cwd()fallbacks let the scanner/analytics crawl an arbitrary cwd with no workspace open (incl. the hourly background scan)WORKSPACE_NOT_FOUND; background timer only arms with a real workspace folderinfrastructure/workspace.tsexported an unguardedresolveWorkspacePathshadowing the security one (dead module)meridian.security.logging.sensitivewas advertised but never enforcedCommandRouter, covered by a new test)diff,getCommitRange,getMergeBase, …)img-src https:, host-source script-src); session-briefing webview not covered by the security testscript-src,img-src https:dropped, explicitconnect-src 'none'everywhere; all three webviews pinned bywebview-security.test.tswith new nonce/unsafe-eval assertionsNo 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
fs.statmetadata instead of reading every workspace file into memory hourly (also fixes >512 MB files being silently skipped due toERR_STRING_TOO_LONG)/dist) were silently ineffective in the scan path (absolute vs relative mismatch) — paths are now matched workspace-relative.git/node_modulesduring traversal and pre-compiles glob regexes;Set-based dedupe replaces O(n²)includesfunctionName(analyzing function B after A in the same file returned A's cached results)pruneCandiates→pruneCandidatestypo fixed in the exported report schema (last chance before the schema is public)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/SKILLerror-code namespaces, the 240-line telemetry metadata table,GIT_DEFAULTS/CHAT_SETTINGS/LOG_SETTINGSconstants, retry-config plumbing, and 8 unusedGitProvidermethods.Tooling
SECURITY.mddocumenting network posture (no egress except policy-gated git ops + optional VS Code LM API), local-only telemetry, and filesystem boundariesKnown items deliberately not changed (noted for review)
fs.unlink), not trash — the modal says "cannot be undone", which is accurate; switching tovscode.workspace.fs.delete({useTrash})would need vscode plumbed into the infra layerts.createProgram(bounded by 5-min TTL cache); making it a worker is a larger changepull(branch)validates but ignores its branch argument (runs plaingit pull); behavior-preservinghttps://claude.ai/code/session_01NTdZdJhedr99fwxZrpdGka
Generated by Claude Code