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.
package/build/cli/audit.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
145
|
-
|
|
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.
|
|
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",
|