Fix security-scanner and duplicate-detector false positives (revised: addresses #63 review)#65
Conversation
Diagnosed via an external run against AIROI where all 10 reported security issues and the duplicate POST/GET finding were false positives, each traced to a specific regex/allowlist gap in the analyzer embedded in index.html.
…on fixes Task-by-task plan executing the approved design spec, each fix verified against the real analyzer (via card/lib/analyzer.js) before being written down, so every "run the test, expect it to fail/pass" step is grounded in actual measured behavior rather than assumption.
…nd real exec calls
… literal-safety, not just the first Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…g the other rules and the documented behavior Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ated architecture-diagram grouping issue The previous fixture (tests/fixtures/security-precision-world/test/preview.test.tsx) was correct in intent but its placement — a second file added to an already-populated fixture subdirectory — triggered a pre-existing, unrelated fragility in the architecture-diagram block-grouping logic (out of scope for this branch). Moved to a root-level markdown fixture, which exercises the same isNonProductionPath code path via its file extension instead of its directory, without perturbing directory topology elsewhere in the repo.
…t the first .match() without /g only inspected the first query/execute/raw call in a file, so a safe first call hid a vulnerable second one. Iterate all db calls with a global regex and flag if any argument list interpolates a SQL keyword via template literal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on rule
The require() and from-import alternatives in the Command Execution regex
only matched the bare 'child_process' specifier, so
import { exec } from "node:child_process"; exec(userInput); produced no
finding. Both alternatives now accept an optional node: prefix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ection
The prior regression test's server/logger.ts fixture satisfies the
Command Execution rule via its `from` import alone, so it couldn't
independently prove the `require` alternative's node: prefix handling.
Add an in-memory synthetic-file test (buildAnalysisData with a single
fabricated file, no fixture-tree file added) isolating a bare
require("node:child_process") call with no import and no dot-notation
exec() call.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ction paths The codeGroups loop in Parser.findDuplicates had picked up the same isNonProductionPath(fn.file) guard as the name-based fnByName loop, silently suppressing type:"code" structural duplicate findings for tests/fixtures/tooling/docs paths. Only the name-based loop guard was intentional; remove it from the structural loop to match the PR's stated scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
commit 9254993 introduced severity:'info' for backend Debug Statements findings but never wired it into the places that assume only high/medium/low exist: the severity sort comparator produced NaN for info (unstable ordering), the security tab totals row had no Info badge, .security-item had no info variant, and getSeverityColor treated info identically to low. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@abk1969 is attempting to deploy a commit to the braedonsaunders' projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Hello, |
braedonsaunders
left a comment
There was a problem hiding this comment.
Thanks for revising this and covering the four blockers from #63. I independently reran the branch suite (52/52 pass), and the SQL multi-call, node:child_process, structural-duplicate, and top-level info severity fixes are present.
I found two remaining merge-blocking security regressions:
-
Bare
Function(...)construction is no longer detected. JavaScript'sFunction("...")andnew Function("...")are equivalent dynamic-code execution paths, but the new\bnew\s+Function\s*\(check only catches the latter.maincurrently catches both. Please use an anchored form that handles optionalnew(for example,\b(?:new\s+)?Function\s*\() and add regression tests proving thatFunction(...)andnew Function(...)are flagged while identifiers such asgetFunction(...)are not. -
The blanket
isNonProductionPathsuppression hides real security findings in executable repository infrastructure..github/,.claude/,scripts/,tools/, andtooling/can contain CI workflows, hooks, deploy scripts, and other executed code. In particular, hardcoded-secret detection should not be disabled solely because of the file path. Please narrow exclusions per rule (tests/fixtures/docs where appropriate), preserve secret detection in executable infrastructure, and add coverage for a real secret under.github/or a script/hook path.
One smaller completeness issue should also be included in the revision: info findings have their own list color and badge, but the security drill-down still falls through to the blue/low-severity styling. Please give info a consistent drill-down treatment.
The failing Vercel status appears to be fork authorization rather than an application or test failure; it is not the reason for this requested-changes review.
…tion()
Function("...") and new Function("...") are equivalent dynamic-code
execution paths; the anchored optional-new form catches both while \b
still rejects identifiers such as getFunction(...).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nfrastructure Split isNonProductionPath into per-concern predicates (isDocumentationPath, isDevToolingPath, isSecretScanExemptPath). The Hardcoded Secret rule now exempts only tests/fixtures (stub credentials) and docs (examples); secrets in .github/, .claude/, scripts/, tools/, tooling/ are flagged again. Other rules keep their existing full non-production exclusion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g through to low/blue Security drill-down header now uses the same neutral gray family as .security-item.info and getSeverityColor. UI-only change outside the vm-testable analyzer region (same treatment as the info badge fix). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n; track R-wave plan Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the thorough re-review — both regressions and the drill-down gap are fixed and pushed (
Also in 4f723a5: README and the design spec updated so the documented exemption semantics match the code (the README previously claimed secrets were excluded in tooling paths). Understood on the Vercel status being fork authorization, thanks for flagging. 🤖 Generated with Claude Code |
braedonsaunders
left a comment
There was a problem hiding this comment.
Re-verified the latest revision (4f723a5) independently — all three items from the previous review are correctly addressed:
- Bare
Function(...)detection: the rule now uses\b(?:new\s+)?Function\s*\(, which flags bothFunction(src)andnew Function(src)while still rejecting identifiers likegetFunction(name)(the\bcannot fall inside a word). Regression test confirmed present and passing. - Per-rule path exclusions:
Hardcoded Secretnow usesisSecretScanExemptPath(tests/fixtures/docs only), so.github/,.claude/,scripts/,tools/, andtooling/are scanned again; XSS/command/shell rules keep the broaderisNonProductionPath. New test covers a credential under.github/workflows/and.claude/hooks/being flagged while docs stay exempt. infodrill-down styling: the drill-down header now uses the same neutral gray family (rgba(92,92,102,…)) as the list item and badge, andgetSeverityColorreturnsvar(--t3)forinfo.
Branch suite: 52/52 passing (the "54" count included two manual CLI scripts, codeflow-repo-smoke.mjs and verify-brain-vault.mjs, which aren't part of the automated suite — no tests are missing).
One note: a test-merge with current main initially failed tests/architecture-diagram.test.mjs (2 tests). That was not a defect in this PR — the block-candidate sort on main used priority[a.kind]||12, and shell has priority 0 (falsy), so shell blocks like index.html sorted last and were sliced off once this PR's new fixture files pushed the repo past the 64-block cap. Fixed on main in 6319f44; the merged result now passes the full combined suite 58/58. Merging.
Revised version of #63, closed after the requested-changes review found four merge-blocking scanner regressions. This PR carries the original branch plus five commits addressing every finding, each with regression coverage that reproduces the reviewer's exact repro case. Full suite: 52/52 pass (
node --test; 47 at review time + 5 new regression tests).Review findings → fixes
query/execute/rawcall.match(...)with a global-regex loop over every db calllib/db.tsfixture now has a safedb.query("SELECT 1")before the vulnerable template-literal query (the reported repro); test verified RED pre-fixnode:child_processno longer detected(?:node:)?to both therequireandfromregex alternativesserver/logger.tsfixture exercisesimport { exec } from "node:child_process"+exec(userInput); acbc828 additionally proves therequire("node:child_process")alternative in isolation via an in-memory synthetic-file test (revert-verified RED)isNonProductionPath(fn.file)disabled structuraltype:"code"duplicate detection, contradicting the PR bodycodeGroupsloop only; the documented name-basedfnByNameguard is unchangedtests/+tools/paths asserts atype:'code'finding; RED pre-fixinfoseverity absent from severity sort map and High/Medium/Low UI totals/stylesinfo:3to the sort map, an Info totals badge,.security-item.infostyling, and aninfobranch ingetSeverityColorundefinedrank made the comparator return NaN, leaking scan order)The behavior of flagging a
child_processimport without a call is pre-existing and was left unchanged; the blocking defect was the missednode:specifier form.🤖 Generated with Claude Code