guardvibe 3.0.48 → 3.0.49

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.
@@ -16,11 +16,12 @@ export async function runAudit(args) {
16
16
  const failOn = getStringFlag(flags, "fail-on") ?? "critical";
17
17
  const skipDeps = flags["skip-deps"] === true;
18
18
  const skipSecrets = flags["skip-secrets"] === true;
19
+ const full = flags["full"] === true;
19
20
  setRules(builtinRules);
20
21
  // Terminal format by default when outputting to TTY, unless --format is specified
21
22
  const isTerminal = !outputFile && process.stdout.isTTY && !flags["format"];
22
23
  const format = isTerminal ? "terminal" : rawFormat;
23
- const result = await runFullAudit(targetPath, { skipDeps, skipSecrets });
24
+ const result = await runFullAudit(targetPath, { skipDeps, skipSecrets, full });
24
25
  const output = formatAuditResult(result, format);
25
26
  // For shouldFail, always use JSON-parseable format
26
27
  const failCheckOutput = formatAuditResult(result, "json");
package/build/cli/scan.js CHANGED
@@ -46,6 +46,7 @@ export async function runDirectoryScan(targetPath, flags) {
46
46
  const outputFile = getOutputPath(flags);
47
47
  const baselinePath = getStringFlag(flags, "baseline");
48
48
  const saveBaseline = flags["save-baseline"] === true || typeof flags["save-baseline"] === "string";
49
+ const full = flags["full"] === true;
49
50
  const scanPath = resolve(targetPath);
50
51
  let result;
51
52
  if (format === "sarif") {
@@ -53,7 +54,7 @@ export async function runDirectoryScan(targetPath, flags) {
53
54
  result = exportSarif(scanPath);
54
55
  }
55
56
  else {
56
- result = scanDirectory(scanPath, true, [], format === "json" ? "json" : "markdown", undefined, baselinePath ?? undefined);
57
+ result = scanDirectory(scanPath, true, [], format === "json" ? "json" : "markdown", undefined, baselinePath ?? undefined, full);
57
58
  }
58
59
  if (outputFile) {
59
60
  safeWriteOutput(outputFile, result);
@@ -89,6 +89,7 @@ export declare function computeResultHash(findings: FindingRef[]): string;
89
89
  export declare function runFullAudit(path: string, options?: {
90
90
  skipDeps?: boolean;
91
91
  skipSecrets?: boolean;
92
+ full?: boolean;
92
93
  }): Promise<AuditResult>;
93
94
  /**
94
95
  * Format audit result as markdown, JSON, or terminal-friendly output.
@@ -72,6 +72,7 @@ function collectJsFiles(dir, maxFiles = 200) {
72
72
  "node_modules", ".git", ".next", "build", "dist", ".turbo", "coverage",
73
73
  ...config.scan.exclude,
74
74
  ]);
75
+ // `maxFiles = Infinity` is the contract for full mode (CLI --full flag): scan everything.
75
76
  function walk(d) {
76
77
  if (files.length >= maxFiles)
77
78
  return;
@@ -124,19 +125,20 @@ export async function runFullAudit(path, options) {
124
125
  const rules = getRules();
125
126
  const allFindings = [];
126
127
  const sections = [];
128
+ const full = options?.full === true;
127
129
  let filesScanned = 0;
128
130
  let filesSkipped = 0;
129
131
  let score = 100;
130
132
  let grade = "A";
131
- // Truncation tracking
133
+ // Truncation tracking — `full` mode (CLI --full flag) bypasses caps for local debugging.
132
134
  let scanTruncated = false;
133
135
  let scanTotalFindings = 0;
134
- let scanMaxFindings = 50; // MAX_JSON_FINDINGS from scan-directory
136
+ let scanMaxFindings = full ? Number.POSITIVE_INFINITY : 50;
135
137
  let taintFilesProcessed = 0;
136
- const taintFileCap = 200;
138
+ const taintFileCap = full ? Number.POSITIVE_INFINITY : 200;
137
139
  // --- Section 1: Code scan ---
138
140
  try {
139
- const codeJson = scanDirectory(projectRoot, true, [], "json", rules.length > 0 ? rules : undefined);
141
+ const codeJson = scanDirectory(projectRoot, true, [], "json", rules.length > 0 ? rules : undefined, undefined, full);
140
142
  const parsed = safeJsonParse(codeJson);
141
143
  if (parsed) {
142
144
  const counts = parseSectionCounts(parsed);
@@ -296,7 +298,7 @@ export async function runFullAudit(path, options) {
296
298
  }
297
299
  // --- Section 5: Taint analysis ---
298
300
  try {
299
- const jsFiles = collectJsFiles(projectRoot);
301
+ const jsFiles = collectJsFiles(projectRoot, taintFileCap);
300
302
  taintFilesProcessed = jsFiles.length;
301
303
  if (jsFiles.length > 0) {
302
304
  const { crossFileFindings, perFileFindings } = analyzeCrossFileTaint(jsFiles);
@@ -340,7 +342,7 @@ export async function runFullAudit(path, options) {
340
342
  }
341
343
  // --- Section 6: Auth coverage ---
342
344
  try {
343
- const jsFiles = collectJsFiles(projectRoot);
345
+ const jsFiles = collectJsFiles(projectRoot, taintFileCap);
344
346
  const routeFiles = jsFiles.filter(f => /\/(route|page)\.(ts|tsx|js|jsx)$/.test(f.path));
345
347
  const layoutFiles = jsFiles.filter(f => /\/layout\.(ts|tsx|js|jsx)$/.test(f.path));
346
348
  if (routeFiles.length > 0) {
@@ -1,2 +1,2 @@
1
1
  import type { SecurityRule } from "../data/rules/types.js";
2
- export declare function scanDirectory(path: string, recursive?: boolean, exclude?: string[], format?: "markdown" | "json", rules?: SecurityRule[], baselinePath?: string): string;
2
+ export declare function scanDirectory(path: string, recursive?: boolean, exclude?: string[], format?: "markdown" | "json", rules?: SecurityRule[], baselinePath?: string, full?: boolean): string;
@@ -41,7 +41,7 @@ function computeBaselineDiff(current, previous) {
41
41
  unchanged: current.filter(e => prevSet.has(currKey(e))),
42
42
  };
43
43
  }
44
- export function scanDirectory(path, recursive = true, exclude = [], format = "markdown", rules, baselinePath) {
44
+ export function scanDirectory(path, recursive = true, exclude = [], format = "markdown", rules, baselinePath, full = false) {
45
45
  const startTime = performance.now();
46
46
  const scanId = randomUUID();
47
47
  const scanRoot = resolve(path);
@@ -141,8 +141,10 @@ export function scanDirectory(path, recursive = true, exclude = [], format = "ma
141
141
  }
142
142
  // MCP output size limit — large projects can produce 300K+ characters which
143
143
  // exceeds Claude Code's max allowed tokens for tool results.
144
- const MAX_JSON_FINDINGS = 50;
145
- const MAX_MD_FINDINGS = 30;
144
+ // `full=true` (CLI --full flag) bypasses these caps for local debugging where
145
+ // size budget doesn't apply.
146
+ const MAX_JSON_FINDINGS = full ? Number.POSITIVE_INFINITY : 50;
147
+ const MAX_MD_FINDINGS = full ? Number.POSITIVE_INFINITY : 30;
146
148
  if (format === "json") {
147
149
  const findingsWithFiles = scanResults.flatMap(r => r.findings.map(f => ({ ...f, rule: f.rule, file: r.path })));
148
150
  // Sort by severity: critical first, then high, then medium
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.0.48",
3
+ "version": "3.0.49",
4
4
  "mcpName": "io.github.goklab/guardvibe",
5
5
  "description": "Security MCP for vibe coding. 365 rules, 36 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis. 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",