Skip to content

feat: dedicated Semgrep findings view - #336

Open
semgrep-chris wants to merge 3 commits into
semgrep:developfrom
semgrep-chris:chris/findings-tree-view
Open

feat: dedicated Semgrep findings view#336
semgrep-chris wants to merge 3 commits into
semgrep:developfrom
semgrep-chris:chris/findings-tree-view

Conversation

@semgrep-chris

Copy link
Copy Markdown

Adds a dedicated Findings view to the Semgrep sidebar, so findings have a home of their own rather than only appearing mixed into the Problems panel.

▾ 🛡 Code Injection                     9 findings
    ⊗ eval-express                          web.js:5
    ⊗ code-string-concat                    web.js:5
    ⚠ audit.eval-detected                   reports.py:14
▾ 🛡 SQL Injection                      4 findings
    ⊗ sqlalchemy-execute-raw-query          users.py:9
    ⚠ formatted-sql-query                   queries.py:5
▾ 🛡 Cross-Site-Scripting (XSS)         4 findings
    ⚠ direct-response-write                 web.js:5
    ⚠ react-unsanitized-method              web.js:9

Grouped by vulnerability class by default; each row names the rule that caught the finding and where it is, and clicking opens the file at that range. Group By in the view toolbar also offers rule, file and severity. Right-click a finding for Open Rule Documentation / Copy Message.

Why the CLI and not diagnostics

The obvious implementation is to read the LSP diagnostics the language client already holds — that is what fills the Problems panel. I started there, but a Semgrep diagnostic carries only range / severity / source / message / code, with no data payload, so none of the rule's metadata reaches the extension. I verified this directly against semgrep lsp with dataSupport: true; a rule with vulnerability_class, cwe and owasp set produces a diagnostic with none of it. semgrep/workspaceRules is declared in lspExtensions.ts but never answers.

semgrep scan --json carries the whole rule: metadata.vulnerability_class, cwe, shortlink, and the full four-level severity — LSP diagnostics have three levels, so CRITICAL is already flattened into Error by the time the client sees it.

The trade-off is that this is a second scan, separate from the language server's. It therefore runs only on demand: the view shows live diagnostics until someone presses scan, so opening a window never triggers an unexpected workspace scan. The header states which source is in use.

Two things I'd like a careful look at

1. --config merge semantics. The CLI replaces configs where the language server merges. Passing the user's semgrep.scan.configuration alone silently drops the deployment policy:

Invocation Findings With vulnerability class
--config my-rules.yaml 8 0
--config auto --config my-rules.yaml 20 12

So --config auto is added back alongside local configs to match the language server. This divergence is arguably a bug in its own right, independent of this view — anyone with scan.configuration set gets a narrower CLI scan than their LSP results suggest.

Resolving auto requires Semgrep's metrics to be enabled, so there is a fallback to local configs only when it fails; without that, users who have turned metrics off get no findings at all.

2. Severity vocabularies. Rules are authored against both ERROR/WARNING/INFO and CRITICAL/HIGH/MEDIUM/LOW, and both reach the scan output. Handling only the first set silently demotes a HIGH finding to an info icon at the bottom of the tree. HIGH→error and MEDIUM→warning are now mapped.

Test plan

The repo suite passes in full, exit 0:

Repo under test Result
semgrep 98 passing (2m)
juice-shop 112 passing (2m)
semgrep-vscode 10 passing (28s)
semgrep-intellij 8 passing (28s)
Total 228 passing, 0 failing

⚠️ npm test is currently broken on VS Code stable, unrelated to this PR. VS Code 1.131 renamed its main binary from Electron to Code; the pinned @vscode/test-electron@^2.3.8 still looks for Electron, so the runner exits with ENOENT before running a single test. I worked around it locally with a symlink inside .vscode-test/. This will hit CI and every contributor and probably deserves its own issue plus a dependency bump.

Manual verification, in the extension development host against a workspace scanned with the deployment policy:

  • Rows, grouping, ordering and counts match the provider's output driven over real semgrep scan --json results.
  • Group By across all four modes; choice persists per workspace.
  • Click-through opens the right file and range, including rows for rules with no vulnerability class.
  • Severity ordering: critical → error → warning → info, ties broken by finding count.
  • Zero extension-host errors on activation.

No unit tests are included for the new modules. The tree-building and rule-id logic are pure functions over parsed scan output and would test well; I did not add them, and that is a fair thing to ask for before merge.

Performance

Checked against synthetic result sets at the scale of a large monorepo, because the first version froze the extension host:

Findings / unique rules Before After
3000 / 200 220ms 17ms
3000 / 900 1188ms 10ms
8000 / 1500 7675ms 33ms

The rule-id disambiguation was quadratic in unique rule count; it now counts suffix occurrences once per depth. Findings are also memoised per refresh, groups start collapsed above 40 findings, and diagnostic events no longer trigger rebuilds once scan results own the tree.

Notes for the reviewer

  • Written with Claude Code. It needs a real human read before merge, not just a green CI.
  • No changes to the language server, the scan commands, or the existing diagnostic path — this is additive. src/extension.ts gains a registration; package.json gains the view, five commands and a submenu.
  • semgrepBinaryPath was extracted into src/semgrepBinary.ts rather than exported from lsp.ts, so the scanner does not pull in the language client just to resolve a path. It mirrors findSemgrep's precedence, which is duplication worth flagging — the two could drift.

🤖 Generated with Claude Code

semgrep-chris and others added 2 commits August 4, 2026 17:07
Findings currently only reach the user through the Problems panel, via the
LSP diagnostics the language client publishes. That works, but a diagnostic
carries only range/severity/source/message/code — there is no `data` payload —
so a rule's metadata never reaches the extension, and CRITICAL has already
been flattened into Error by the time we see it.

This adds a Findings view backed by `semgrep scan --json` instead, which
carries the whole rule: `metadata.vulnerability_class`, CWE, the registry
shortlink, and the full four-level severity. Findings are grouped by
vulnerability class by default, each row naming the rule that caught it and
where. Rule, File and Severity groupings are also available.

The scan is on demand — the view runs on live diagnostics until the user asks
for one — so opening a window never triggers an unexpected workspace scan.

Two things worth calling out for review:

- The CLI replaces rather than merges `--config`, so passing the user's
  `semgrep.scan.configuration` alone silently drops the deployment policy,
  which the language server does include. `--config auto` is added back
  alongside local configs to match. Falls back to local configs only when
  resolving `auto` fails, which happens when Semgrep's metrics are disabled.
- Rules are authored against two severity vocabularies (ERROR/WARNING/INFO and
  CRITICAL/HIGH/MEDIUM/LOW) and both appear in scan output, so HIGH and MEDIUM
  are mapped rather than falling through to info.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Aug 4, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Also corrects a comment that referred to a `sourceLabel` which does not
exist; the header text is built in `updateHeader`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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