guardvibe 3.0.57 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -228,7 +228,7 @@ Malicious postinstall scripts, unpinned GitHub Actions, typosquat detection
228
228
  | `verify_fix` | Verify a security fix was applied correctly — returns fixed/still_vulnerable/new_issues |
229
229
  | `security_workflow` | Get recommended tool workflow for your current task (writing, pre-commit, PR review, etc.) |
230
230
  | `auth_coverage` | **Auth coverage map** — enumerate routes, parse middleware matchers, detect auth guards, report coverage % |
231
- | `deep_scan` | **LLM-powered deep analysis** — IDOR, business logic, race conditions, privilege escalation (requires API key) |
231
+ | `deep_scan` | **LLM-powered deep analysis** — IDOR, business logic, race conditions, auth bypass. Defaults to Claude Haiku 4.5 (~cents/scan). Pass `model: 'sonnet'` for deeper analysis. CLI: `npx guardvibe deep-scan <file> --focus idor` |
232
232
  | `full_audit` | **Single source of truth** — runs ALL checks in one call, returns PASS/FAIL/WARN verdict + score + coverage % + deterministic result hash |
233
233
  | `remediation_plan` | **Remediation plan** — generates section-by-section fix checklist after audit |
234
234
  | `verify_remediation` | **Remediation verification** — compares before/after audit, flags skipped sections |
@@ -0,0 +1 @@
1
+ export declare function runDeepScan(args: string[]): Promise<void>;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * CLI: guardvibe deep-scan <file>
3
+ * LLM-powered deep security analysis.
4
+ */
5
+ import { readFileSync, statSync } from "node:fs";
6
+ import { resolve, extname } from "node:path";
7
+ import { parseArgs } from "./args.js";
8
+ import { buildDeepScanPrompt, callLLM, parseDeepScanResult, formatDeepScanFindings, DEFAULT_MAX_BYTES, } from "../tools/deep-scan.js";
9
+ const EXT_TO_LANG = {
10
+ ".ts": "typescript", ".tsx": "typescript", ".mts": "typescript", ".cts": "typescript",
11
+ ".js": "javascript", ".jsx": "javascript", ".mjs": "javascript", ".cjs": "javascript",
12
+ ".py": "python", ".go": "go", ".rb": "ruby", ".java": "java",
13
+ ".rs": "rust", ".php": "php", ".cs": "csharp",
14
+ };
15
+ const VALID_FOCUS = ["all", "idor", "business-logic", "auth-bypass", "race-condition"];
16
+ export async function runDeepScan(args) {
17
+ const { flags, positional } = parseArgs(args);
18
+ const file = positional[0];
19
+ if (!file) {
20
+ console.error(" [ERR] Please specify a file: npx guardvibe deep-scan <file>");
21
+ console.error("");
22
+ console.error(" Options:");
23
+ console.error(" --focus <area> all (default) | idor | business-logic | auth-bypass | race-condition");
24
+ console.error(" --model <model> haiku (default, ~cents/scan) | sonnet (deeper, more expensive)");
25
+ console.error(" --max-bytes <n> Truncate input to N bytes (default 10000)");
26
+ console.error(" --format <type> markdown (default) | json");
27
+ console.error("");
28
+ console.error(" Requires ANTHROPIC_API_KEY (or OPENAI_API_KEY) environment variable.");
29
+ process.exit(1);
30
+ }
31
+ const path = resolve(file);
32
+ let content;
33
+ try {
34
+ const stat = statSync(path);
35
+ if (!stat.isFile()) {
36
+ console.error(` [ERR] Not a file: ${path}`);
37
+ process.exit(1);
38
+ }
39
+ content = readFileSync(path, "utf-8");
40
+ }
41
+ catch (e) {
42
+ console.error(` [ERR] Cannot read file: ${path}`);
43
+ console.error(` ${e.message}`);
44
+ process.exit(1);
45
+ }
46
+ if (!process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) {
47
+ console.error(" [ERR] No LLM API key. Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your environment.");
48
+ console.error(" Default model is Claude Haiku 4.5 — typically ~cents per scan.");
49
+ process.exit(1);
50
+ }
51
+ const focusArg = flags.focus ?? "all";
52
+ if (!VALID_FOCUS.includes(focusArg)) {
53
+ console.error(` [ERR] Invalid --focus: ${focusArg}. Use one of: ${VALID_FOCUS.join(", ")}`);
54
+ process.exit(1);
55
+ }
56
+ const focus = focusArg;
57
+ const modelArg = flags.model ?? "haiku";
58
+ if (modelArg !== "haiku" && modelArg !== "sonnet") {
59
+ console.error(` [ERR] Invalid --model: ${modelArg}. Use haiku or sonnet.`);
60
+ process.exit(1);
61
+ }
62
+ const model = modelArg;
63
+ const maxBytes = flags["max-bytes"] != null ? Number(flags["max-bytes"]) : DEFAULT_MAX_BYTES;
64
+ if (!Number.isFinite(maxBytes) || maxBytes < 500 || maxBytes > 50_000) {
65
+ console.error(` [ERR] --max-bytes must be 500..50000 (got ${flags["max-bytes"]})`);
66
+ process.exit(1);
67
+ }
68
+ const format = (flags.format === "json" ? "json" : "markdown");
69
+ const language = EXT_TO_LANG[extname(path).toLowerCase()] ?? "unknown";
70
+ const prompt = buildDeepScanPrompt(content, language, [], focus);
71
+ const llmResponse = await callLLM(prompt, { model, maxBytes });
72
+ if (llmResponse === null) {
73
+ console.error(" [ERR] LLM call failed — check API key validity and network.");
74
+ process.exit(1);
75
+ }
76
+ const findings = parseDeepScanResult(llmResponse);
77
+ const output = formatDeepScanFindings(findings, format);
78
+ console.log(output);
79
+ }
package/build/cli.js CHANGED
@@ -28,6 +28,7 @@ function printUsage() {
28
28
  npx guardvibe check-cmd "<cmd>" Check if a shell command is safe to execute
29
29
  npx guardvibe auth-coverage [path] Auth coverage analysis (Next.js routes)
30
30
  npx guardvibe compliance [path] Compliance report (--framework SOC2|GDPR|...)
31
+ npx guardvibe deep-scan <file> LLM-powered deep scan (IDOR, business logic, race conditions)
31
32
  npx guardvibe init <platform> Setup MCP server configuration
32
33
  npx guardvibe hook install Install pre-commit security hook
33
34
  npx guardvibe hook uninstall Remove pre-commit security hook
@@ -152,6 +153,10 @@ async function main() {
152
153
  const { runCompliance } = await import("./cli/compliance.js");
153
154
  await runCompliance(subArgs);
154
155
  }
156
+ else if (command === "deep-scan") {
157
+ const { runDeepScan } = await import("./cli/deep-scan.js");
158
+ await runDeepScan(subArgs);
159
+ }
155
160
  else {
156
161
  console.error(` Unknown command: ${command}`);
157
162
  printUsage();
package/build/index.js CHANGED
@@ -863,20 +863,24 @@ server.tool("auth_coverage", "Analyze authentication coverage across Next.js App
863
863
  return { content: [{ type: "text", text: output }] };
864
864
  });
865
865
  // Tool 32: LLM-powered deep scan
866
- server.tool("deep_scan", "LLM-powered deep security analysis for vulnerabilities that pattern-matching cannot detect: IDOR, business logic flaws, race conditions, stale auth, mass assignment, privilege escalation. Requires ANTHROPIC_API_KEY or OPENAI_API_KEY environment variable. Run pattern scan first, then use this for deeper analysis.", {
866
+ server.tool("deep_scan", "LLM-powered deep security analysis for vulnerabilities that pattern-matching cannot detect: IDOR, business logic flaws, race conditions, stale auth, mass assignment, privilege escalation. Defaults to Claude Haiku 4.5 (~cents per scan); pass `model: 'sonnet'` for deeper analysis at higher cost. Requires ANTHROPIC_API_KEY or OPENAI_API_KEY env var.", {
867
867
  code: z.string().describe("Code to analyze"),
868
868
  language: z.string().describe("Programming language"),
869
869
  context: z.string().optional().describe("Additional context (e.g., 'This is a payment endpoint')"),
870
870
  existingFindings: z.array(z.string()).default([]).describe("Already-detected findings to avoid duplicating"),
871
+ focus: z.enum(["all", "idor", "business-logic", "auth-bypass", "race-condition"]).default("all").describe("Focus area — narrows the prompt to a specific vulnerability class"),
872
+ model: z.enum(["haiku", "sonnet"]).default("haiku").describe("LLM model. haiku = fast & cheap (default), sonnet = deeper analysis"),
873
+ maxBytes: z.number().int().min(500).max(50_000).default(10_000).describe("Max prompt size in bytes — caps cost. Code over this limit is truncated."),
871
874
  format: z.enum(["markdown", "json"]).default("markdown").describe("Output format"),
872
- }, async ({ code, language, context, existingFindings, format }) => {
873
- const prompt = buildDeepScanPrompt(code, language, existingFindings);
874
- const llmResponse = await callLLM(context ? `${prompt}\n\nAdditional context: ${context}` : prompt);
875
+ }, async ({ code, language, context, existingFindings, focus, model, maxBytes, format }) => {
876
+ const prompt = buildDeepScanPrompt(code, language, existingFindings, focus);
877
+ const fullPrompt = context ? `${prompt}\n\nAdditional context: ${context}` : prompt;
878
+ const llmResponse = await callLLM(fullPrompt, { model, maxBytes });
875
879
  if (llmResponse === null) {
876
880
  return {
877
881
  content: [{
878
882
  type: "text",
879
- text: "## Deep Scan — Setup Required\n\nNo LLM API key found. Set one of:\n- `ANTHROPIC_API_KEY` — uses Claude\n- `OPENAI_API_KEY` — uses GPT-4o\n\nThe deep scan sends code to the LLM API for semantic vulnerability analysis.",
883
+ text: "## Deep Scan — Setup Required\n\nNo LLM API key found. Set one of:\n- `ANTHROPIC_API_KEY` — uses Claude (default: Haiku 4.5; pass `model: 'sonnet'` for deeper analysis)\n- `OPENAI_API_KEY` — uses GPT-4o-mini / GPT-4o\n\nThe deep scan sends code to the LLM API for semantic vulnerability analysis. Default cost is a few cents per scan with Haiku.",
880
884
  }],
881
885
  };
882
886
  }
@@ -12,10 +12,14 @@ export interface DeepScanFinding {
12
12
  location: string;
13
13
  fix: string;
14
14
  }
15
+ export type DeepScanFocus = "all" | "idor" | "business-logic" | "auth-bypass" | "race-condition";
16
+ export type DeepScanModel = "haiku" | "sonnet";
17
+ export declare const MODEL_IDS: Record<DeepScanModel, string>;
18
+ export declare const DEFAULT_MAX_BYTES = 10000;
15
19
  /**
16
20
  * Build a structured prompt for the LLM to analyze code.
17
21
  */
18
- export declare function buildDeepScanPrompt(code: string, language: string, existingFindings: string[]): string;
22
+ export declare function buildDeepScanPrompt(code: string, language: string, existingFindings: string[], focus?: DeepScanFocus): string;
19
23
  /**
20
24
  * Parse LLM response into structured findings.
21
25
  * Handles raw JSON, JSON in markdown code blocks, and malformed responses.
@@ -25,9 +29,16 @@ export declare function parseDeepScanResult(response: string): DeepScanFinding[]
25
29
  * Format deep scan findings as markdown or JSON.
26
30
  */
27
31
  export declare function formatDeepScanFindings(findings: DeepScanFinding[], format: "markdown" | "json"): string;
32
+ export interface CallLLMOptions {
33
+ model?: DeepScanModel;
34
+ maxBytes?: number;
35
+ }
28
36
  /**
29
37
  * Call an LLM API for deep analysis. Uses native fetch.
30
38
  * Supports Anthropic (ANTHROPIC_API_KEY) or OpenAI (OPENAI_API_KEY).
31
39
  * Returns null if no API key is available.
40
+ *
41
+ * Defaults to Haiku 4.5 for cost; pass `model: "sonnet"` for higher-quality analysis.
42
+ * `maxBytes` truncates the prompt to keep cost bounded (default 10 KB).
32
43
  */
33
- export declare function callLLM(prompt: string): Promise<string | null>;
44
+ export declare function callLLM(prompt: string, options?: CallLLMOptions): Promise<string | null>;
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Uses native fetch — no extra dependencies.
7
7
  */
8
- const FOCUS_AREAS = [
8
+ const ALL_AREAS = [
9
9
  "IDOR (Insecure Direct Object Reference) — can users access resources belonging to other users?",
10
10
  "Business logic flaws — are there authorization bypasses, price manipulation, or state machine violations?",
11
11
  "Race conditions — are there TOCTOU issues, double-spend, or concurrent mutation without locking?",
@@ -13,18 +13,50 @@ const FOCUS_AREAS = [
13
13
  "Mass assignment — can users set fields they shouldn't (role, isAdmin, price)?",
14
14
  "Privilege escalation — can a regular user perform admin actions through parameter manipulation?",
15
15
  ];
16
+ const FOCUS_AREAS = {
17
+ all: ALL_AREAS,
18
+ idor: [
19
+ "IDOR (Insecure Direct Object Reference) — can users access resources belonging to other users?",
20
+ "Missing ownership scope on database queries (where: { id } instead of { id, userId })",
21
+ "URL/path parameters used directly as DB keys without authorization gate",
22
+ ],
23
+ "business-logic": [
24
+ "Authorization bypass via parameter manipulation (e.g., role/isAdmin in body)",
25
+ "Price/amount manipulation or coupon stacking",
26
+ "State machine violations (skip steps, replay completed actions)",
27
+ "Idempotency / replay protection on payment / order paths",
28
+ ],
29
+ "auth-bypass": [
30
+ "Missing or insufficient auth check before sensitive operations",
31
+ "Stale tokens / sessions still accepted after revoke",
32
+ "Cookie / JWT validation skipped on a subset of routes",
33
+ "Privilege-elevation through parameter manipulation",
34
+ ],
35
+ "race-condition": [
36
+ "TOCTOU between read and write (check-then-act without locking)",
37
+ "Concurrent rate-limit increments without atomic ops",
38
+ "Double-spend / double-grant via parallel requests",
39
+ "Optimistic-update races on shared mutable state",
40
+ ],
41
+ };
42
+ export const MODEL_IDS = {
43
+ haiku: "claude-haiku-4-5-20251001",
44
+ sonnet: "claude-sonnet-4-6",
45
+ };
46
+ export const DEFAULT_MAX_BYTES = 10_000;
16
47
  /**
17
48
  * Build a structured prompt for the LLM to analyze code.
18
49
  */
19
- export function buildDeepScanPrompt(code, language, existingFindings) {
50
+ export function buildDeepScanPrompt(code, language, existingFindings, focus = "all") {
51
+ const areas = FOCUS_AREAS[focus] ?? FOCUS_AREAS.all;
20
52
  const lines = [
21
53
  "You are a senior application security engineer performing a deep code review.",
22
54
  "Analyze the following code for security vulnerabilities that automated pattern-matching scanners miss.",
23
55
  "",
24
- "## Focus Areas",
56
+ `## Focus Areas (${focus})`,
25
57
  "",
26
58
  ];
27
- for (const area of FOCUS_AREAS) {
59
+ for (const area of areas) {
28
60
  lines.push(`- ${area}`);
29
61
  }
30
62
  lines.push("");
@@ -123,11 +155,20 @@ export function formatDeepScanFindings(findings, format) {
123
155
  * Call an LLM API for deep analysis. Uses native fetch.
124
156
  * Supports Anthropic (ANTHROPIC_API_KEY) or OpenAI (OPENAI_API_KEY).
125
157
  * Returns null if no API key is available.
158
+ *
159
+ * Defaults to Haiku 4.5 for cost; pass `model: "sonnet"` for higher-quality analysis.
160
+ * `maxBytes` truncates the prompt to keep cost bounded (default 10 KB).
126
161
  */
127
- export async function callLLM(prompt) {
162
+ export async function callLLM(prompt, options = {}) {
128
163
  // guardvibe-ignore — API URLs are hardcoded trusted endpoints, not user-controlled
129
164
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
130
165
  const openaiKey = process.env.OPENAI_API_KEY;
166
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
167
+ const model = options.model ?? "haiku";
168
+ // Truncate prompt to keep token budget bounded
169
+ const trimmedPrompt = prompt.length > maxBytes
170
+ ? prompt.slice(0, maxBytes) + "\n\n[truncated by GuardVibe to stay within budget]"
171
+ : prompt;
131
172
  if (anthropicKey) {
132
173
  const res = await fetch("https://api.anthropic.com/v1/messages", {
133
174
  method: "POST",
@@ -137,9 +178,9 @@ export async function callLLM(prompt) {
137
178
  "anthropic-version": "2023-06-01",
138
179
  },
139
180
  body: JSON.stringify({
140
- model: "claude-sonnet-4-6",
181
+ model: MODEL_IDS[model],
141
182
  max_tokens: 2048,
142
- messages: [{ role: "user", content: prompt }],
183
+ messages: [{ role: "user", content: trimmedPrompt }],
143
184
  }),
144
185
  });
145
186
  if (!res.ok)
@@ -155,9 +196,9 @@ export async function callLLM(prompt) {
155
196
  "Authorization": `Bearer ${openaiKey}`,
156
197
  },
157
198
  body: JSON.stringify({
158
- model: "gpt-4o",
199
+ model: model === "sonnet" ? "gpt-4o" : "gpt-4o-mini",
159
200
  max_tokens: 2048,
160
- messages: [{ role: "user", content: prompt }],
201
+ messages: [{ role: "user", content: trimmedPrompt }],
161
202
  }),
162
203
  });
163
204
  if (!res.ok)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.0.57",
3
+ "version": "3.1.0",
4
4
  "mcpName": "io.github.goklab/guardvibe",
5
5
  "description": "Security MCP for vibe coding. 390 rules, 36 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis, +25 AI-native rules (MCP supply-chain, RAG/vector poisoning, agent loop DoS, public-prefix LLM keys, sandbox bypass). Plus Next.js, Supabase, Clerk, Stripe, Prisma, tRPC, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK, and the full AI-generated stack.",
6
6
  "type": "module",