Skip to content

Fix security-scanner and duplicate-detector false positives (revised: addresses #63 review)#65

Merged
braedonsaunders merged 22 commits into
braedonsaunders:mainfrom
abk1969:security-scanner-precision
Jul 14, 2026
Merged

Fix security-scanner and duplicate-detector false positives (revised: addresses #63 review)#65
braedonsaunders merged 22 commits into
braedonsaunders:mainfrom
abk1969:security-scanner-precision

Conversation

@abk1969

@abk1969 abk1969 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

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

Finding Fix Regression coverage
SQL template injection checked only in the first query/execute/raw call 939161c — replaced the single .match(...) with a global-regex loop over every db call lib/db.ts fixture now has a safe db.query("SELECT 1") before the vulnerable template-literal query (the reported repro); test verified RED pre-fix
node:child_process no longer detected a5ffa6a — added (?:node:)? to both the require and from regex alternatives server/logger.ts fixture exercises import { exec } from "node:child_process" + exec(userInput); acbc828 additionally proves the require("node:child_process") alternative in isolation via an in-memory synthetic-file test (revert-verified RED)
isNonProductionPath(fn.file) disabled structural type:"code" duplicate detection, contradicting the PR body c50ed30 — removed the guard from the structural codeGroups loop only; the documented name-based fnByName guard is unchanged Synthetic two-file structural duplicate under tests/ + tools/ paths asserts a type:'code' finding; RED pre-fix
info severity absent from severity sort map and High/Medium/Low UI totals/styles 07b754f — added info:3 to the sort map, an Info totals badge, .security-item.info styling, and an info branch in getSeverityColor Synthetic three-file ordering test that deterministically failed pre-fix (the undefined rank made the comparator return NaN, leaking scan order)

The behavior of flagging a child_process import without a call is pre-existing and was left unchanged; the blocking defect was the missed node: specifier form.

🤖 Generated with Claude Code

abk1969 and others added 18 commits July 11, 2026 23:47
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.
… 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>
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

@abk1969 is attempting to deploy a commit to the braedonsaunders' projects Team on Vercel.

A member of the Team first needs to authorize it.

@abk1969

abk1969 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Hello,
Fable helped me to fix PR#63.
Best regards!
Abbas

@braedonsaunders braedonsaunders left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Bare Function(...) construction is no longer detected. JavaScript's Function("...") and new Function("...") are equivalent dynamic-code execution paths, but the new \bnew\s+Function\s*\( check only catches the latter. main currently catches both. Please use an anchored form that handles optional new (for example, \b(?:new\s+)?Function\s*\() and add regression tests proving that Function(...) and new Function(...) are flagged while identifiers such as getFunction(...) are not.

  2. The blanket isNonProductionPath suppression hides real security findings in executable repository infrastructure. .github/, .claude/, scripts/, tools/, and tooling/ 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.

abk1969 and others added 4 commits July 13, 2026 18:51
…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>
@abk1969

abk1969 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough re-review — both regressions and the drill-down gap are fixed and pushed (07b754f..4f723a5). Full suite: 54/54 (47 baseline + 5 from the previous wave + 2 new regression tests). Finding → fix mapping:

Review finding Fix commit Regression coverage
Bare Function(...) no longer detected 6d6ab03 — regex widened to \b(?:new\s+)?Function\s*\( New test asserts Function(src) and new Function(src) are flagged while getFunction(name) is not (verified failing before the fix)
Blanket isNonProductionPath hides secrets in executable infrastructure 0fafa94 — split into per-concern predicates; Hardcoded Secret now uses isSecretScanExemptPath (tests/fixtures/docs only), so .github/, .claude/, scripts/, tools/, tooling/ are scanned again. XSS/shell/command rules keep their existing exclusion New test asserts a credential under .github/workflows/, .claude/hooks/, and scripts/ is flagged while the same line in docs/*.md stays exempt (verified failing before the fix); existing test-stub exemption test still passes
info drill-down falls through to blue/low styling 0ff7a2e — drill-down header gets the same neutral gray family (rgba(92,92,102,…)) as .security-item.info and getSeverityColor UI-only, outside the vm-extracted analyzer region (same treatment as the info badge fix in 07b754f)

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 braedonsaunders left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-verified the latest revision (4f723a5) independently — all three items from the previous review are correctly addressed:

  1. Bare Function(...) detection: the rule now uses \b(?:new\s+)?Function\s*\(, which flags both Function(src) and new Function(src) while still rejecting identifiers like getFunction(name) (the \b cannot fall inside a word). Regression test confirmed present and passing.
  2. Per-rule path exclusions: Hardcoded Secret now uses isSecretScanExemptPath (tests/fixtures/docs only), so .github/, .claude/, scripts/, tools/, and tooling/ are scanned again; XSS/command/shell rules keep the broader isNonProductionPath. New test covers a credential under .github/workflows/ and .claude/hooks/ being flagged while docs stay exempt.
  3. info drill-down styling: the drill-down header now uses the same neutral gray family (rgba(92,92,102,…)) as the list item and badge, and getSeverityColor returns var(--t3) for info.

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.

@braedonsaunders
braedonsaunders merged commit b36869e into braedonsaunders:main Jul 14, 2026
2 of 3 checks passed
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.

2 participants