Skip to content

Scansv2#13

Merged
pranavcodeant merged 11 commits intomainfrom
scansv2
Apr 22, 2026
Merged

Scansv2#13
pranavcodeant merged 11 commits intomainfrom
scansv2

Conversation

@pranavcodeant
Copy link
Copy Markdown
Collaborator

@pranavcodeant pranavcodeant commented Apr 17, 2026

CodeAnt-AI Description

Add scan browsing, filtering, and export options in the CLI and interactive view

What Changed

  • Added new scan commands to list organizations, repositories, scan history, scan summaries, full findings, and dismissed alerts
  • Added filtering for scan results by severity, file path, check name, dismissed status, and page size, with support for selecting a specific scan or branch
  • Added export formats for scan results: JSON, SARIF, CSV, Markdown, and terminal table, plus writing output to a file
  • Added an interactive scan center to step through organization, repository, scan, and result type selection, with clearer result navigation and back actions
  • Added coverage for the new scan commands, filters, formats, and error handling

Impact

✅ Faster access to scan history
✅ Easier review of security and secrets findings
✅ Smoother export of scan results to files

🔄 Retrigger CodeAnt AI Review

Details

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 17, 2026

CodeAnt AI is running Incremental review

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Apr 17, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 17, 2026

🏁 CodeAnt Quality Gate Results

Commit: 03261a1a
Scan Time: 2026-04-22 08:48:55 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated

View Full Results

Comment thread src/scans/listRepos.js Outdated
Comment thread src/scans/fetchAdvancedScanResults.js Outdated
Comment thread src/scans/fetchDismissedAlerts.js Outdated
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 17, 2026

CodeAnt AI Incremental review completed.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 20, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Apr 20, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 20, 2026

Sequence Diagram

This diagram shows how the new scans center validates the CLI connection, discovers repositories, and loads scan history and results (including advanced findings and dismissed alerts) for a selected repository and commit.

sequenceDiagram
    participant User
    participant ScansCenter
    participant CodeAntAPI

    User->>ScansCenter: Open scans center
    ScansCenter->>CodeAntAPI: Validate connection
    CodeAntAPI-->>ScansCenter: Return organizations and user email
    ScansCenter->>CodeAntAPI: List repositories for selected organization
    CodeAntAPI-->>ScansCenter: Return repository list

    User->>ScansCenter: Select repository and commit
    ScansCenter->>CodeAntAPI: Get scan history for repository
    CodeAntAPI-->>ScansCenter: Return scan runs
    ScansCenter->>CodeAntAPI: Fetch scan and advanced results for repo and commit
    ScansCenter->>CodeAntAPI: Fetch dismissed alerts for repo
    CodeAntAPI-->>ScansCenter: Return issues and dismissed alerts
Loading

Generated by CodeAnt AI

Comment thread src/scans/getScanHistory.js Outdated
Comment thread src/scans/fetchAdvancedScanResults.js Outdated
Comment thread src/scans/fetchAdvancedScanResults.js Outdated
Comment thread src/scans/fetchDismissedAlerts.js Outdated
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 20, 2026

CodeAnt AI finished running the review.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 20, 2026

CodeAnt AI is running Incremental review

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Apr 20, 2026
Comment thread src/components/ScanCenter.js Outdated
Comment thread src/scanCenter/handleSelectRepo.js Outdated
Comment thread src/scanCenter/handleSelectRepo.js Outdated
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 20, 2026

CodeAnt AI Incremental review completed.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 21, 2026

CodeAnt AI is running Incremental review

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Apr 21, 2026
Comment thread src/commands/scans/lib/paginate.js Outdated
Comment thread src/commands/scans/lib/paginate.js Outdated
Comment thread src/commands/scans/lib/resolveScan.js
Comment thread src/commands/scans/lib/dismissMatch.js Outdated
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 21, 2026

CodeAnt AI Incremental review completed.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 22, 2026

CodeAnt AI is running Incremental review

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Apr 22, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 22, 2026

CodeAnt AI Incremental review completed.

- Fix NaN sort comparator in listRepos when pushed_at is missing
- Preserve multi-line file_line_range in fetchAdvancedScanResults
- Add item.check_name to dead-code normalization fallback chain
- Fix confidence_score type error with String() coercion before toLowerCase
- Strip all scan-result suffixes in fetchDismissedAlerts (not just security_issues)
- Add optional chaining on dismissInfo to prevent null crash
- Fall back to input repo param when backend omits it in getScanHistory
- Add dismissed-finding display fallbacks in ScanCenter
- Use full_name || name fallback in handleSelectRepo to avoid undefined API call
- Gate debug stderr logging behind CODEANT_DEBUG_SCAN_HISTORY=1
- Guard paginate offset/limit against NaN with Number.isFinite
- Fix dismissMatch line-number comparison with null check and Number() coercion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pranavcodeant pranavcodeant merged commit c56011a into main Apr 22, 2026
5 checks passed
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 22, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Apr 22, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 22, 2026

Sequence Diagram

This diagram shows how the new scan-center command lets a user pick an organization, repository, scan, and result type, then fetches matching findings from the CodeAnt scan API to display in the terminal.

sequenceDiagram
    participant User
    participant CLI
    participant ScanCenterUI
    participant ScanAPI

    User->>CLI: Run scan-center command
    CLI->>ScanCenterUI: Render interactive scan center
    ScanCenterUI->>ScanAPI: Validate connection and list organizations
    User->>ScanCenterUI: Select organization, repository, and scan
    ScanCenterUI->>ScanAPI: Fetch scan history for selected repo
    User->>ScanCenterUI: Choose result type (findings or dismissed alerts)
    ScanCenterUI->>ScanAPI: Fetch results for repo, commit and type
    ScanAPI-->>ScanCenterUI: Return matching issues
    ScanCenterUI-->>User: Show paginated issues list in terminal
Loading

Generated by CodeAnt AI

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 22, 2026

📊 CodeAnt Coverage Report

Commit: 03261a1a
Scan Time: 2026-04-22 08:48:57 UTC

✅ Status: PASSED

Coverage Metrics

Metric Value Status
New Code Coverage 100.0% (0/0 lines) ✅ PASSED

Details

New Code Coverage: No changed files found in commit

View Coverage Report

Comment on lines +62 to +63
: fetchDismissedAlerts(repo, 'security'),
]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 Architect Review — HIGH

Dismissed-status loading in runResults is hardcoded to analysis_type='security', so dismissed secrets are never considered when filtering or annotating findings; secrets dismissed via the backend can still appear as active findings by default.

Suggestion: When resolving results, fetch dismissed alerts for all relevant analysis types implied by the selected categories (at least security and secrets), merge those lists, and pass the combined dismissal set into applyFilters so both security and secrets findings are correctly recognized as dismissed.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is an **Architect / Logical Review** comment left during a code review. These reviews are first-class, important findings — not optional suggestions. Do NOT dismiss this as a 'big architectural change' just because the title says architect review; most of these can be resolved with a small, localized fix once the intent is understood.

**Path:** src/commands/scans/results.js
**Line:** 62:63
**Comment:**
	*HIGH: Dismissed-status loading in `runResults` is hardcoded to `analysis_type='security'`, so dismissed secrets are never considered when filtering or annotating findings; secrets dismissed via the backend can still appear as active findings by default.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
If a suggested approach is provided above, use it as the authoritative instruction. If no explicit code suggestion is given, you MUST still draft and apply your own minimal, localized fix — do not punt back with 'no suggestion provided, review manually'. Keep the change as small as possible: add a guard clause, gate on a loading state, reorder an await, wrap in a conditional, etc. Do not refactor surrounding code or expand scope beyond the finding.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix

Comment on lines +60 to +61
includeDismissed
? Promise.resolve({ success: true, dismissedAlerts: [] })
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 Architect Review — HIGH

When --include-dismissed is set, runResults skips fetching dismissal metadata and passes an empty dismissedAlerts array to applyFilters, so findings are never annotated with correct dismissed/dismiss_info state and exports (JSON/CSV/SARIF) misrepresent dismissal status.

Suggestion: Always fetch real dismissal metadata and feed it into applyFilters, using --include-dismissed solely to control whether dismissed findings are filtered out or retained, not whether dismissal lookup and annotation run.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is an **Architect / Logical Review** comment left during a code review. These reviews are first-class, important findings — not optional suggestions. Do NOT dismiss this as a 'big architectural change' just because the title says architect review; most of these can be resolved with a small, localized fix once the intent is understood.

**Path:** src/commands/scans/results.js
**Line:** 60:61
**Comment:**
	*HIGH: When `--include-dismissed` is set, `runResults` skips fetching dismissal metadata and passes an empty `dismissedAlerts` array to `applyFilters`, so findings are never annotated with correct `dismissed`/`dismiss_info` state and exports (JSON/CSV/SARIF) misrepresent dismissal status.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
If a suggested approach is provided above, use it as the authoritative instruction. If no explicit code suggestion is given, you MUST still draft and apply your own minimal, localized fix — do not punt back with 'no suggestion provided, review manually'. Keep the change as small as possible: add a guard clause, gate on a loading state, reorder an await, wrap in a conditional, etc. Do not refactor surrounding code or expand scope beyond the finding.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix

Comment on lines +5 to +9
const s = String(val);
if (s.includes(',') || s.includes('"') || s.includes('\n')) {
return '"' + s.replace(/"/g, '""') + '"';
}
return s;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: CSV output is vulnerable to spreadsheet formula injection because values beginning with =, +, -, or @ are emitted unchanged. If a finding field contains attacker-controlled text and the CSV is opened in Excel/Sheets, formulas can execute. Sanitize formula-leading cells (for example, prefix with a single quote) before writing. [security]

Severity Level: Critical 🚨
- ❌ Exported CSV can trigger Excel/Sheets formula execution.
- ⚠️ Security analysts opening CSV risk local compromise.
Steps of Reproduction ✅
1. Run the CLI and invoke the scans results command with CSV output, for example:

   `codeant scans results --repo owner/repo --format csv --output findings.csv`

   (command defined in `src/commands/scans/index.js:59-80`, which calls `runResults()`
   with `format: opts.format` at `src/commands/scans/index.js:89-104`).

2. Inside `runResults()` in `src/commands/scans/results.js:17-33`, the `format` option
(default `'json'`, user‑settable to `'csv'`) is read, and the formatter is resolved as
`FORMATTERS[format]` at `src/commands/scans/results.js:41`. `FORMATTERS` comes from
`src/commands/scans/formatters/index.js:6-12`, where the `csv` formatter imported from
`./csv.js` is registered.

3. Findings are collected and normalized via `normalizeIssue()` in
`src/commands/scans/lib/normalize.js:43-112`, which builds fields like `file_path`,
`check_name`, and `message` from raw scan issues (e.g., `message` is derived from
`issue.message || issue.issue_text || issue.description || checkName` at
`normalize.js:50-52`). These values can contain arbitrary text from the scanned repository
or tools.

4. When the CSV formatter is chosen, `formatter.render(envelope)` in
`src/commands/scans/results.js:153-155` calls the `render()` implementation in
`src/commands/scans/formatters/csv.js:12-22`. That function iterates `envelope.findings`
and for each finding `f` emits `HEADERS.map((h) => csvCell(f[h]))` at `csv.js:19-20`.
`csvCell()` at `csv.js:3-9` converts the value to a string (`const s = String(val);` at
line 5) and only quotes it if it contains a comma, quote, or newline (`if (s.includes(',')
|| s.includes('"') || s.includes('\n'))` at line 6). It does not treat values starting
with `=`, `+`, `-`, or `@` specially, so a field such as `message` or `file_path`
beginning with `=HYPERLINK("http://attacker","click")` is written to the CSV unchanged.
When `findings.csv` is opened in Excel or Google Sheets, that cell is interpreted as a
formula, demonstrating a spreadsheet formula injection vulnerability.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/commands/scans/formatters/csv.js
**Line:** 5:9
**Comment:**
	*Security: CSV output is vulnerable to spreadsheet formula injection because values beginning with `=`, `+`, `-`, or `@` are emitted unchanged. If a finding field contains attacker-controlled text and the CSV is opened in Excel/Sheets, formulas can execute. Sanitize formula-leading cells (for example, prefix with a single quote) before writing.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

.requiredOption('--repo <repo>', 'Repository (owner/repo)')
.option('--branch <name>', 'Filter by branch name')
.option('--since <iso>', 'Show scans since ISO date')
.option('--limit <n>', 'Max results (default: 20)', parseInt, 20)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Using parseInt directly as Commander's option parser is unsafe because Commander passes (value, previousValue) and parseInt interprets the second argument as radix. With default 20, values are parsed in base-20, so user-provided limits are misparsed. Wrap parsing in a single-argument callback with explicit radix 10. [type error]

Severity Level: Major ⚠️
- ⚠️ `codeant scans history` can return many more scans than requested.
- ⚠️ Pagination scripts using `--limit` see inconsistent page sizes.
Steps of Reproduction ✅
1. Invoke the CLI entrypoint `codeant` (or `node src/index.js`), which sets up Commander
in `src/index.js:3-4` and registers scan subcommands via `registerScansCommands(program, {
runCmd })` at `src/index.js:129-130`.

2. Run the history command with an explicit limit, e.g. `codeant scans history --repo
org/repo --limit 100`. This hits the `history` subcommand definition in
`src/commands/scans/index.js:33-42`, where the `--limit` option is defined as
`.option('--limit <n>', 'Max results (default: 20)', parseInt, 20)` at line 39.

3. Commander (v12.1.0, from `package.json:41-47`) calls the custom parser as
`parseInt(value, previousValue)`. With a default of `20`, the first invocation becomes
`parseInt('100', 20)`, so the radix is 20, not 10. JavaScript `parseInt('100', 20)`
returns decimal `400`, not `100`, meaning `opts.limit` is misparsed.

4. The parsed `opts.limit` is passed into `runHistory({ repo: opts.repo, branch:
opts.branch, since: opts.since, limit: opts.limit })` at
`src/commands/scans/index.js:40-41`. In `src/commands/scans/history.js:6-40`, `runHistory`
slices results with `if (limit > 0) history = history.slice(0, limit);`. With the example
above, a user-requested `--limit 100` produces `limit === 400`, so up to 400 scans are
returned instead of 100 whenever that many exist, demonstrating the radix bug in real
execution.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/commands/scans/index.js
**Line:** 39:39
**Comment:**
	*Type Error: Using `parseInt` directly as Commander's option parser is unsafe because Commander passes `(value, previousValue)` and `parseInt` interprets the second argument as radix. With default `20`, values are parsed in base-20, so user-provided limits are misparsed. Wrap parsing in a single-argument callback with explicit radix 10.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

.option('--format <fmt>', 'Output format: json|sarif|csv|md|table (default: json)', 'json')
.option('--output <path>', 'Write output to file instead of stdout')
.option('--fields <list>', 'Project findings to subset of fields (comma-separated)')
.option('--limit <n>', 'Max findings per page (default: 100)', parseInt, 100)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: This has the same parser bug but worse: default 100 becomes the radix for parseInt, which is invalid, so parsed values become NaN and downstream pagination falls back to default behavior. As a result, user-supplied --limit is effectively ignored. [type error]

Severity Level: Major ⚠️
- ⚠️ `codeant scans results` ignores user-specified findings page size.
- ⚠️ Automation cannot tune scan result batch sizes via `--limit`.
Steps of Reproduction ✅
1. Invoke the CLI entrypoint `codeant` (or `node src/index.js`), which registers scan
commands via `registerScansCommands(program, { runCmd })` at `src/index.js:129-130`.

2. Run the results command with a custom page size, e.g. `codeant scans results --repo
org/repo --limit 10`. This hits the `results` subcommand defined in
`src/commands/scans/index.js:59-82`, where `--limit` is configured as `.option('--limit
<n>', 'Max findings per page (default: 100)', parseInt, 100)` at line 77.

3. Commander (v12.1.0 from `package.json:41-47`) invokes the option parser as
`parseInt(value, previousValue)`. With default `100`, the first parse is `parseInt('10',
100)`. Because JavaScript `parseInt` requires a radix between 2 and 36, a radix of 100 is
invalid and `parseInt('10', 100)` returns `NaN`. Thus `opts.limit` becomes `NaN` for any
user-supplied `--limit`.

4. The CLI passes this parsed value into `runResults` via `runResults({ ..., limit:
opts.limit, offset: opts.offset, ... })` at `src/commands/scans/index.js:89-105`. In
`src/commands/scans/results.js:17-33`, `limit` is destructured directly from `opts` (so it
remains `NaN`) and passed to `paginate(sorted, { limit, offset })` at
`src/commands/scans/results.js:111`. `paginate` (in
`src/commands/scans/lib/paginate.js:7-11`) computes `safeLimit = Number.isFinite(limit) &&
limit > 0 ? Math.trunc(limit) : 100`, so with `limit === NaN` it always falls back to
`100`. The command therefore always returns pages of 100 findings, completely ignoring any
user-specified `--limit`, confirming the bug in a real execution path.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/commands/scans/index.js
**Line:** 77:77
**Comment:**
	*Type Error: This has the same parser bug but worse: default `100` becomes the radix for `parseInt`, which is invalid, so parsed values become `NaN` and downstream pagination falls back to default behavior. As a result, user-supplied `--limit` is effectively ignored.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

if (dfp && fp && !fp.endsWith(dfp) && !dfp.endsWith(fp) && fp !== dfp) continue;

// test_id / type must match check_id when present
if (d.test_id && finding.check_id && d.test_id !== finding.check_id) continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: This condition skips test_id validation whenever the finding has an empty check_id, which can falsely match unrelated findings by path/line alone and mark them dismissed. If a dismissed record has a test_id, matching should fail when the finding does not provide an equivalent identifier. [logic error]

Severity Level: Major ⚠️
- ❌ Non-security findings hidden in `codeant scans results` output.
- ⚠️ Developers may miss docstring or style issues entirely.
Steps of Reproduction ✅
1. Execute `codeant scans results --repo <owner/repo>` so that the `results` subcommand in
`src/commands/scans/index.js:10-26` calls `runResults()` in
`src/commands/scans/results.js:17-33`, which by default requests all categories (`sast`,
`docstring`, `complex_functions`, etc.) via `parseTypes()` in
`src/commands/scans/lib/categories.js:36-50`.

2. `runResults()` fetches dismissed alerts with `fetchDismissedAlerts(repo, 'security')`
at `src/commands/scans/results.js:56-63`. In `src/scans/fetchDismissedAlerts.js:31-47`,
each entry pushed into `dismissedAlerts` includes `file_path`, `line_number` (derived from
`part1`), and `test_id: part2`. For security alerts whose key component `part1` begins
with digits (for example `"42: foo()"`), `parseInt(part1, 10)` produces a real line number
that can match findings at that file and line.

3. In parallel, `runResults()` normalizes raw issues across all categories into
`NormalizedFinding` objects using `normalizeIssue()` in
`src/commands/scans/lib/normalize.js:4-13,53-62` after they have been flattened by
`fetchScanResults()` in `src/scans/fetchScanResults.js:81-95` for code results and
`fetchAdvancedScanResults()` in `src/scans/fetchAdvancedScanResults.js:145-195` for
advanced results. Some categories (e.g., `docstring` or other analyzers that only set
`issue_text`/`message`) yield findings where `check_id` is the empty string because none
of `issue.check_id`, `issue.test_id`, `issue.rule_id`, `issue.vulnerability_id`, or
`issue.cve_id` are present, while `file_path` and `line_number` can legitimately coincide
with a dismissed security alert.

4. When `applyFilters()` in `src/commands/scans/lib/filters.js:19-71` runs, it calls
`findDismissMatch(f, dismissedAlerts)` for each finding. For a non-security finding at the
same `file_path` and `line_number` as a dismissed security alert, `findDismissMatch()` in
`src/commands/scans/lib/dismissMatch.js:13-25` sees matching path and line. Because the
finding has an empty `check_id`, the guard `if (d.test_id && finding.check_id && d.test_id
!== finding.check_id) continue;` at `dismissMatch.js:20` does not trigger (the middle
operand is falsy), so the function returns that dismissed entry at `dismissMatch.js:25`.
`applyFilters()` then marks the non-security finding as `dismissed` and, when
`includeDismissed` is false (the default), filters it out at `filters.js:57-59`,
effectively hiding an unrelated finding solely due to path/line collision instead of
matching on the intended identifier.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/commands/scans/lib/dismissMatch.js
**Line:** 20:20
**Comment:**
	*Logic Error: This condition skips `test_id` validation whenever the finding has an empty `check_id`, which can falsely match unrelated findings by path/line alone and mark them dismissed. If a dismissed record has a `test_id`, matching should fail when the finding does not provide an equivalent identifier.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

const result = await validateConnection();
if (!result.success) {
const err = new Error(result.error || 'Failed to validate connection');
err.exitCode = result.error?.toLowerCase().includes('network') ? 4 : 1;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The exit-code classification assumes result.error is a string and calls .toLowerCase().includes(...) on it, which can throw if the backend returns a non-string error payload. Coerce to string (or guard by type) before string operations to prevent crashing in the error path. [type error]

Severity Level: Major ⚠️
-`codeant scans orgs` crashes on malformed backend error payloads.
- ⚠️ Exit code classification for network errors becomes unreliable.
Steps of Reproduction ✅
1. Register and run the CLI so that `registerScansCommands` in
`src/commands/scans/index.js:16-24` hooks up the `codeant scans orgs` subcommand and calls
`runCmd(() => runOrgs())` for that command.

2. Invoke the CLI with `codeant scans orgs`, which triggers `runOrgs` in
`src/commands/scans/orgs.js:7-14`; this calls `validateConnection()` from
`src/scans/connectionHandler.js:19-51`.

3. During `validateConnection`, the helper calls `fetchApi('/extension/scans2/validate',
'POST', { extension: 'cli' })` (`src/scans/connectionHandler.js:21-23`); when the CodeAnt
backend responds with `{ status: 'error', message: <non-string value> }` (e.g. a
structured object), the function returns `{ success: false, error: response.message ||
'Invalid or expired connection string' }`, so `error` is that non-string `message` value.

4. Back in `runOrgs`, the `!result.success` branch at `src/commands/scans/orgs.js:9-12`
executes; line 11 evaluates `result.error?.toLowerCase().includes('network')`, but since
`result.error` is not a string, `result.error.toLowerCase` is undefined and Node throws a
`TypeError: result.error.toLowerCase is not a function`, causing `codeant scans orgs` to
crash with a stack trace instead of exiting cleanly with exit code 4 or 1.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/commands/scans/orgs.js
**Line:** 11:11
**Comment:**
	*Type Error: The exit-code classification assumes `result.error` is a string and calls `.toLowerCase().includes(...)` on it, which can throw if the backend returns a non-string error payload. Coerce to string (or guard by type) before string operations to prevent crashing in the error path.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +41 to +43
const sevSet = severity && severity.length > 0
? new Set(severity.map((s) => normalizeSeverity(s)))
: null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Severity filter values are normalized with normalizeSeverity, which maps unknown inputs to unknown; this means typos in --severity are silently converted into a real filter bucket and can return unintended findings. Validate severity tokens explicitly and error on invalid values instead of coercing them. [logic error]

Severity Level: Major ⚠️
- ⚠️ Severity filter accepts typos, returning unintended 'unknown' results.
- ⚠️ CLI users may misinterpret filtered scan findings.
Steps of Reproduction ✅
1. Run the CLI entrypoint `codeant`, which registers scan subcommands via
`registerScansCommands()` in `src/index.js:21`, adding `codeant scans results` with a
`--severity <list>` option as defined in `src/commands/scans/index.js:58-71`.

2. Execute `codeant scans results --repo owner/repo --severity critical,hgh`; Commander
passes `opts.severity === "critical,hgh"` into the `.action` handler at
`src/commands/scans/index.js:82-99`, which forwards it unchanged to `runResults({
severity: opts.severity, ... })`.

3. Inside `runResults` in `src/commands/scans/results.js:94-99`, `severityList` is
computed as `["critical","hgh"]` and passed into `applyFilters(allFindings, { severity:
severityList, ... })`, so both the valid and mistyped severities reach the filter logic.

4. In `applyFilters` at `src/commands/scans/lib/filters.js:40-43`, `sevSet` is built as
`new Set(severity.map((s) => normalizeSeverity(s)))`; `normalizeSeverity("critical")`
returns `"critical"` while `normalizeSeverity("hgh")` falls through the `SEV_MAP` and
returns `"unknown"` (per `src/commands/scans/lib/normalize.js:14-16), so `sevSet` becomes
`{"critical","unknown"}` and the severity filter at `filters.js:60-62` matches findings
with severities `"critical"` and `"unknown"` instead of the user-intended `"critical"` and
`"high"`, silently converting the typo into a real filter bucket rather than rejecting it.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/commands/scans/lib/filters.js
**Line:** 41:43
**Comment:**
	*Logic Error: Severity filter values are normalized with `normalizeSeverity`, which maps unknown inputs to `unknown`; this means typos in `--severity` are silently converted into a real filter bucket and can return unintended findings. Validate severity tokens explicitly and error on invalid values instead of coercing them.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 22, 2026

CodeAnt AI finished running the review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants