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
21 changes: 21 additions & 0 deletions e2e/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,27 @@ export default defineConfig({
expect(result.stderr).toContain("Duration must look like 500ms, 30s, 10m, or 2h");
});

it("rejects an unknown severity on every flag that takes one", () => {
// Each of these compares severities differently, so an unvalidated value
// fails differently too — silently widening one filter, emptying another.
const flags = [
["revalidate", "--min-severity"],
["enrich", "--min-severity"],
["triage", "--severity"],
["export", "--min-severity"],
["export", "--only-severity"],
["metrics", "--min-severity"],
];

for (const [command, flag] of flags) {
const result = runBundle([command, flag, "CRTICAL"]);
expect(result.status, `${command} ${flag}`).toBe(1);
expect(result.stderr, `${command} ${flag}`).toContain(
"Allowed choices are CRITICAL, HIGH, MEDIUM, HIGH_BUG, BUG, LOW.",
);
}
});

it("init --scaffold-only writes a workspace seeded with the first project", () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "deepsec-init-"));
const workspace = path.join(tmp, "audits");
Expand Down
44 changes: 29 additions & 15 deletions packages/deepsec/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ dotenvConfig(); // also load .env as fallback

import { getRegistry } from "@deepsec/core";
import { setAttributionVersion } from "@deepsec/processor";
import { Command } from "commander";
import { Command, Option } from "commander";
import { collectRepeatable } from "./agent-config.js";
import { enrichCommand } from "./commands/enrich.js";
import { exportCommand } from "./commands/export.js";
Expand Down Expand Up @@ -37,6 +37,8 @@ import { getDeepsecVersion } from "./version.js";

installSandboxOutputCap();

const SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "HIGH_BUG", "BUG", "LOW"] as const;

const program = new Command();

function parsePackageManager(value: string): "pnpm" | "npm" {
Expand Down Expand Up @@ -396,9 +398,11 @@ program
"--thinking-level <level>",
"Reasoning/thinking effort for the main agent run: minimal, low, medium, high, or xhigh (default: xhigh)",
)
.option(
"--min-severity <sev>",
"Only revalidate findings at this severity or above (CRITICAL, HIGH, MEDIUM, HIGH_BUG, BUG)",
.addOption(
new Option(
"--min-severity <sev>",
"Only revalidate findings at this severity or above",
).choices(SEVERITIES),
)
.option("--force", "Re-check already-validated findings")
.option("--limit <n>", "Max files to revalidate", parseInt)
Expand All @@ -419,9 +423,11 @@ program
"Project identifier (default: the only project in deepsec.config.ts; required if there are multiple)",
)
.option("--filter <prefix>", "Only enrich files matching path prefix")
.option(
"--min-severity <sev>",
"Only enrich files with a finding at this severity or above (CRITICAL, HIGH, MEDIUM, HIGH_BUG, BUG, LOW)",
.addOption(
new Option(
"--min-severity <sev>",
"Only enrich files with a finding at this severity or above",
).choices(SEVERITIES),
)
.option("--force", "Re-enrich already-enriched files")
.option("--concurrency <n>", "Parallel ownership oracle requests (default: cores - 1)", parseInt)
Expand All @@ -434,7 +440,9 @@ program
"--project-id <id>",
"Project identifier (default: the only project in deepsec.config.ts; required if there are multiple)",
)
.option("--severity <sev>", "Severity to triage (default: MEDIUM)", "MEDIUM")
.addOption(
new Option("--severity <sev>", "Severity to triage").choices(SEVERITIES).default("MEDIUM"),
)
.option("--model <model>", "Model to use (default: claude-sonnet-4-6 — cheaper)")
.option("--force", "Re-triage already-triaged findings")
.option("--limit <n>", "Max findings to triage", parseInt)
Expand All @@ -455,13 +463,15 @@ program
.description("Export findings as JSON or as a directory of per-finding markdown files")
.option("--format <kind>", "Output format: json (default) or md-dir", "json")
.option("--project-id <csv>", "Comma-separated project IDs (omit for all)")
.option(
"--min-severity <sev>",
"Only export findings at this severity or above (CRITICAL, HIGH, MEDIUM, HIGH_BUG, BUG, LOW)",
.addOption(
new Option("--min-severity <sev>", "Only export findings at this severity or above").choices(
SEVERITIES,
),
)
.option(
"--only-severity <sev>",
"Only export findings at this exact severity (CRITICAL, HIGH, MEDIUM, HIGH_BUG, BUG, LOW)",
.addOption(
new Option("--only-severity <sev>", "Only export findings at this exact severity").choices(
SEVERITIES,
),
)
.option("--discovered-today", "Only findings whose most recent analysis was today (local time)")
.option(
Expand Down Expand Up @@ -498,7 +508,11 @@ program
.command("metrics")
.description("Report findings metrics across all projects (or one project)")
.option("--project-id <id>", "Project identifier (omit for all projects)")
.option("--min-severity <sev>", "Minimum severity to include (default: LOW)")
.addOption(
new Option("--min-severity <sev>", "Minimum severity to include (default: LOW)").choices(
SEVERITIES,
),
)
.action(metricsCommand);

const sandboxCmd = program
Expand Down
3 changes: 0 additions & 3 deletions packages/deepsec/src/commands/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,6 @@ export async function exportCommand(opts: {

const minSeverity = opts.minSeverity as Severity | undefined;
const onlySeverity = opts.onlySeverity as Severity | undefined;
if (onlySeverity && !(onlySeverity in SEVERITY_ORDER)) {
throw new Error(`--only-severity: not a valid severity: ${opts.onlySeverity}`);
}

let sinceMs: number | undefined;
let untilMs = Number.POSITIVE_INFINITY;
Expand Down