fix(security): close file-system-race (TOCTOU) sites in shared lib#25
Merged
Conversation
CodeQL js/file-system-race fired on check-then-use file access in the shared collector/analyzer code that syncs to consumer plugins. Fixes: - New lib/utils/fs-safe.js readFileWithLimit(): opens once and does the is-file/size check + read on the same fd, so a path swap cannot redirect the read. - collectors/codebase.js, enhance/suppression.js, enhance/cross-file-analyzer.js: replace stat/exists/access-then-readFileSync with readFileWithLimit (drops the redundant pre-checks). - patterns/slop-analyzers.js: inline fd-based stat+read using the injected fs (preserves test mocking). - enhance/auto-suppression.js: drop existsSync pre-check; read via fd and write via writeJsonAtomic. - enhance/docs-analyzer.js, enhance/prompt-analyzer.js: back up from the in-memory original snapshot instead of re-reading the file before write. - enhance/fixer.js: write via writeFileAtomic (temp + rename, never follows a symlink) while keeping the explicit assertNotSymlink refusal. Propagates to consumers (deslop, repo-intel, ...) via the lib sync. node --test: 69 pass / 0 fail.
There was a problem hiding this comment.
Code Review
This pull request introduces safe file-reading and atomic-writing mechanisms, specifically adding readFileWithLimit to mitigate TOCTOU (Time-of-Check to Time-of-Use) vulnerabilities and symlink-following attacks across multiple analyzer modules. While these security enhancements are applied successfully in several areas, the review feedback correctly identifies that lib/enhance/docs-analyzer.js and lib/enhance/prompt-analyzer.js still utilize fs.writeFileSync directly for writing files and backups. To fully secure the application against symlink redirection, these files should be updated to use the newly introduced writeFileAtomic utility.
This was referenced May 30, 2026
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.
Summary
Resolves the CodeQL
js/file-system-race(high) alerts in the shared collector/analyzer code. These are check-then-use patterns where a separatefs.stat/fs.existsSync/fs.accessSync/fs.lstaton a path precedes areadFileSync/writeFileSyncon the same path. Because thislib/syncs to consumer plugins (deslop, repo-intel, + 11 others viasync.yml), fixing it here clears the alerts in those repos after the next sync + scan.Changes
lib/utils/fs-safe.jsreadFileWithLimit()- opens the path once and runs the is-file check, size cap, and read against the same fd. A swap of the path between calls cannot redirect the read.collectors/codebase.js,enhance/suppression.js,enhance/cross-file-analyzer.js- replacestat/exists/access-then-readFileSyncwithreadFileWithLimit(drops the redundant pre-check; missing/oversize throws and is handled by the existing catch).patterns/slop-analyzers.js- inline fd-basedfstat+read using the injectedfsso mocked tests keep working.enhance/auto-suppression.js- dropexistsSyncpre-check; read via fd, write viawriteJsonAtomic.enhance/docs-analyzer.js,enhance/prompt-analyzer.js- back up from the in-memory original snapshot instead of re-reading the file immediately before the write (removes the read->write race).enhance/fixer.js- write viawriteFileAtomic(temp + rename; rename never follows a symlink) while keeping the explicitassertNotSymlinkrefusal.Test plan
node --test-> 69 pass / 0 fail / 2 skipped (incl.fixer.test.js)require()cleanly;readFileWithLimitverified for normal read,EFBIG(oversize), andENOENT(missing)