Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## Added

- New "Findings" view in the Semgrep sidebar. Runs `semgrep scan --json` on demand and groups the results by vulnerability class, showing the rule that caught each finding and its location. Group by rule, file or severity instead from the view's Group By menu. Because it reads the CLI's output rather than editor diagnostics, it can show each rule's vulnerability class and distinguishes CRITICAL from ERROR.

## 1.17.0 - 2026-04-10

## Fixed
Expand Down
117 changes: 116 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"commands": [
{
"command": "semgrep.scanWorkspace",
"title": "Semgrep: Scan changed files in workspace"
"title": "Semgrep: Scan changed files in workspace",
"icon": "$(refresh)"
},
{
"command": "semgrep.scanWorkspaceFull",
Expand Down Expand Up @@ -86,6 +87,41 @@
{
"command": "semgrep.restartLanguageServer",
"title": "Semgrep: Restart Language Server"
},
{
"command": "semgrep.findings.scan",
"title": "Semgrep Findings: Scan workspace",
"icon": "$(refresh)"
},
{
"command": "semgrep.findings.groupByClass",
"title": "Semgrep Findings: Group by Vulnerability Class",
"icon": "$(list-tree)"
},
{
"command": "semgrep.findings.groupByFile",
"title": "Semgrep Findings: Group by File",
"icon": "$(list-tree)"
},
{
"command": "semgrep.findings.groupBySeverity",
"title": "Semgrep Findings: Group by Severity",
"icon": "$(list-tree)"
},
{
"command": "semgrep.findings.groupByRule",
"title": "Semgrep Findings: Group by Rule",
"icon": "$(list-tree)"
},
{
"command": "semgrep.findings.openRuleDocs",
"title": "Semgrep Findings: Open Rule Documentation",
"icon": "$(book)"
},
{
"command": "semgrep.findings.copyMessage",
"title": "Semgrep Findings: Copy Message",
"icon": "$(copy)"
}
],
"configuration": {
Expand Down Expand Up @@ -203,6 +239,10 @@
]
},
"viewsWelcome": [
{
"view": "semgrep.view.findings",
"contents": "No Semgrep findings.\n[Scan workspace](command:semgrep.findings.scan)\nA scan reports the vulnerability class for each finding. Saving a file also reports findings here, without the classification."
},
{
"view": "semgrep.view.policy",
"contents": "Using Semgrep with your organization? \n[Sign in](command:semgrep.login)\n or [configure a local policy](command:workbench.action.openSettings?%22semgrep.scan.configuration%22) in the settings",
Expand All @@ -211,6 +251,10 @@
],
"views": {
"semgrep-sidebar": [
{
"id": "semgrep.view.findings",
"name": "Findings"
},
{
"id": "semgrep.view.policy",
"name": "Policy Configuration"
Expand All @@ -226,6 +270,13 @@
}
]
},
"submenus": [
{
"id": "semgrep.findings.groupBy",
"label": "Group By",
"icon": "$(list-tree)"
}
],
"menus": {
"commandPalette": [
{
Expand All @@ -235,6 +286,70 @@
{
"command": "semgrep.logout",
"when": "semgrep.loggedIn"
},
{
"command": "semgrep.findings.openRuleDocs",
"when": "false"
},
{
"command": "semgrep.findings.copyMessage",
"when": "false"
}
],
"view/title": [
{
"command": "semgrep.findings.scan",
"when": "view == semgrep.view.findings",
"group": "navigation@1"
},
{
"submenu": "semgrep.findings.groupBy",
"when": "view == semgrep.view.findings",
"group": "navigation@2"
},
{
"command": "semgrep.scanWorkspaceFull",
"when": "view == semgrep.view.findings",
"group": "1_scan@1"
}
],
"semgrep.findings.groupBy": [
{
"command": "semgrep.findings.groupByClass",
"when": "semgrep.findings.groupBy != class",
"group": "1_group@1"
},
{
"command": "semgrep.findings.groupByRule",
"when": "semgrep.findings.groupBy != rule",
"group": "1_group@2"
},
{
"command": "semgrep.findings.groupByFile",
"when": "semgrep.findings.groupBy != file",
"group": "1_group@3"
},
{
"command": "semgrep.findings.groupBySeverity",
"when": "semgrep.findings.groupBy != severity",
"group": "1_group@4"
}
],
"view/item/context": [
{
"command": "semgrep.findings.openRuleDocs",
"when": "view == semgrep.view.findings && viewItem == semgrep.finding.documented",
"group": "inline@1"
},
{
"command": "semgrep.findings.openRuleDocs",
"when": "view == semgrep.view.findings && viewItem == semgrep.finding.documented",
"group": "1_finding@1"
},
{
"command": "semgrep.findings.copyMessage",
"when": "view == semgrep.view.findings && viewItem =~ /^semgrep.finding/",
"group": "1_finding@2"
}
]
},
Expand Down
13 changes: 13 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { activateLsp, deactivateLsp, restartLsp } from "./lsp";
import { createStatusBar } from "./statusBar";
import { initTelemetry, stopTelemetry } from "./telemetry/telemetry";
import { deregisterExistingOtel, withSpan } from "./utilities/tracing";
import {
SemgrepFindingsProvider,
registerFindingsCommands,
} from "./views/findings";
import { SemgrepPolicyViewProvider } from "./views/policy";
import { SemgrepHelpProvider } from "./views/support";
import { SemgrepSearchWebviewProvider } from "./views/webview";
Expand Down Expand Up @@ -52,6 +56,15 @@ async function afterClientStart(context: ExtensionContext, env: Environment) {
);
env.provider = provider;

// Dedicated findings tree. It reads the diagnostics the language client
// already publishes, so it needs no extra wiring to the LSP.
const findingsProvider = new SemgrepFindingsProvider(env);
findingsProvider.register();
context.subscriptions.push(
findingsProvider,
...registerFindingsCommands(findingsProvider),
);

context.subscriptions.push(
vscode.window.registerTreeDataProvider(
SemgrepHelpProvider.viewType,
Expand Down
Loading
Loading