guardvibe 2.9.5 → 2.9.6
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/index.js +18 -8
- package/build/tools/check-code.js +43 -7
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -133,10 +133,14 @@ server.tool("check_dependencies", "Check npm, PyPI, or Go packages for known sec
|
|
|
133
133
|
return val;
|
|
134
134
|
}, z.array(packageSchema)).describe("List of packages to check: [{name, version, ecosystem}]"),
|
|
135
135
|
}, async ({ packages }) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
content: [{ type: "text", text: results }]
|
|
139
|
-
}
|
|
136
|
+
try {
|
|
137
|
+
const results = await checkDependencies(packages);
|
|
138
|
+
return { content: [{ type: "text", text: results }] };
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
142
|
+
return { content: [{ type: "text", text: `⚠️ Dependency check failed: ${msg}\n\nThis may be a network issue reaching the OSV database. Try again or check your internet connection.` }] };
|
|
143
|
+
}
|
|
140
144
|
});
|
|
141
145
|
// Tool 5: Scan directory for security vulnerabilities (filesystem-native)
|
|
142
146
|
server.tool("scan_directory", "Scan an entire project directory for security vulnerabilities. Reads files directly from the filesystem — no need to pass file contents. Returns a security score (A-F) and detailed findings. Includes scan metadata (ID, timestamp, duration, file hashes) for audit trails. Use baseline to compare with a previous scan.", {
|
|
@@ -170,7 +174,7 @@ server.tool("scan_directory", "Scan an entire project directory for security vul
|
|
|
170
174
|
return { content: [{ type: "text", text: results + summary }] };
|
|
171
175
|
});
|
|
172
176
|
// Tool 6: Scan manifest/lockfile for dependency vulnerabilities
|
|
173
|
-
server.tool("scan_dependencies", "Parse a lockfile or manifest (package.json, package-lock.json, requirements.txt, go.mod) and check all dependencies for known CVEs via the OSV database. Reads the file directly.", {
|
|
177
|
+
server.tool("scan_dependencies", "Parse a lockfile or manifest (package.json, package-lock.json, requirements.txt, go.mod) and check all dependencies for known CVEs via the OSV database. Reads the file directly. Use this after installing dependencies, during CI, or when auditing existing projects for vulnerable packages.", {
|
|
174
178
|
manifest_path: z.string().describe("Path to manifest file (e.g. 'package.json', 'requirements.txt', 'go.mod')"),
|
|
175
179
|
format: z.enum(["markdown", "json"]).default("markdown").describe("Output format: markdown (human) or json (machine-readable for agents)"),
|
|
176
180
|
}, async ({ manifest_path, format }) => {
|
|
@@ -258,8 +262,14 @@ server.tool("check_package_health", "Check npm packages for typosquat risk, main
|
|
|
258
262
|
packages: z.array(z.string()).describe("List of package names to check (e.g. ['lodash', 'expres', 'react-qeury'])"),
|
|
259
263
|
format: z.enum(["markdown", "json"]).default("markdown").describe("Output format: markdown (human) or json (machine-readable for agents)"),
|
|
260
264
|
}, async ({ packages, format }) => {
|
|
261
|
-
|
|
262
|
-
|
|
265
|
+
try {
|
|
266
|
+
const results = await checkPackageHealth(packages, format);
|
|
267
|
+
return { content: [{ type: "text", text: results }] };
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
271
|
+
return { content: [{ type: "text", text: `⚠️ Package health check failed: ${msg}\n\nThis may be a network issue reaching the npm registry. Try again or check your internet connection.` }] };
|
|
272
|
+
}
|
|
263
273
|
});
|
|
264
274
|
// Tool 12: Auto-fix security vulnerabilities
|
|
265
275
|
server.tool("fix_code", "Analyze code for security vulnerabilities and return fix suggestions with concrete patches. The AI agent can apply these patches to automatically fix issues. Returns structured fix data including before/after code, severity, and line numbers.", {
|
|
@@ -325,7 +335,7 @@ server.tool("scan_secrets_history", "Scan git history for leaked secrets. Finds
|
|
|
325
335
|
return { content: [{ type: "text", text: results }] };
|
|
326
336
|
});
|
|
327
337
|
// Tool 17: Compliance Policy Check
|
|
328
|
-
server.tool("policy_check", "Check project against compliance policies defined in .guardviberc. Validates custom framework requirements, severity thresholds, required controls, and risk exceptions. Returns pass/fail status with detailed findings per control.", {
|
|
338
|
+
server.tool("policy_check", "Check project against compliance policies defined in .guardviberc. Use this in CI/CD pipelines to enforce security gates, or before releases to verify compliance requirements are met. Validates custom framework requirements, severity thresholds, required controls, and risk exceptions. Returns pass/fail status with detailed findings per control.", {
|
|
329
339
|
path: z.string().describe("Project root directory"),
|
|
330
340
|
format: z.enum(["markdown", "json"]).default("markdown").describe("Output format"),
|
|
331
341
|
}, async ({ path, format }) => {
|
|
@@ -173,6 +173,13 @@ function hasRoleCheckPattern(code) {
|
|
|
173
173
|
// Destructured role check: const { role } = ...; if (role !== "admin")
|
|
174
174
|
if (/\brole\b[\s\S]{0,100}?(?:!==|===)\s*["']/i.test(code))
|
|
175
175
|
return true;
|
|
176
|
+
// Import + call of admin/role guard function (requireAdmin, requireRole, checkAdmin, etc.)
|
|
177
|
+
if (/import\s+.*(?:requireAdmin|requireRole|checkAdmin|isAdmin|verifyAdmin|assertAdmin)\b/i.test(code) &&
|
|
178
|
+
/(?:requireAdmin|requireRole|checkAdmin|isAdmin|verifyAdmin|assertAdmin)\s*\(/i.test(code))
|
|
179
|
+
return true;
|
|
180
|
+
// await requireAdmin() with error check pattern (naming-agnostic admin guard)
|
|
181
|
+
if (/await\s+(?:\w+\.)*\w*(?:Admin|admin)\w*\s*\([^)]*\)\s*;?\s*\n\s*if\s*\(/i.test(code))
|
|
182
|
+
return true;
|
|
176
183
|
return false;
|
|
177
184
|
}
|
|
178
185
|
/**
|
|
@@ -327,18 +334,47 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
327
334
|
// Skip open redirect rules when redirect URL validation is present
|
|
328
335
|
if (codeHasRedirectValidation && ["VG425", "VG409", "VG660"].includes(rule.id))
|
|
329
336
|
continue;
|
|
330
|
-
// Skip
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
+
// Skip VG678 (Missing X-Content-Type-Options) in client components —
|
|
338
|
+
// client-side code calling getPublicUrl() can't set response headers.
|
|
339
|
+
// Also skip when code only uses Supabase getPublicUrl/getSignedUrl to generate
|
|
340
|
+
// URL strings (not actually serving file content via response stream).
|
|
341
|
+
if (rule.id === "VG678") {
|
|
342
|
+
const isClientComponent = /^['"]use client['"]/.test(code.trimStart()) ||
|
|
343
|
+
(filePath && /Client\.\w+$/.test(filePath));
|
|
344
|
+
if (isClientComponent)
|
|
337
345
|
continue;
|
|
346
|
+
const hasOnlyUrlGeneration = /(?:getPublicUrl|getSignedUrl)\s*\(/i.test(code) &&
|
|
347
|
+
!/(?:createReadStream|sendFile|res\.download|\.pipe\s*\()/i.test(code);
|
|
348
|
+
if (hasOnlyUrlGeneration)
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
// Skip VG131 (state-changing GET) when the GET function body has no mutations.
|
|
352
|
+
// Must check per-GET-function, not the whole file — files with GET+POST would false-positive.
|
|
353
|
+
if (rule.id === "VG131") {
|
|
354
|
+
const getMatch = /export\s+(?:async\s+)?function\s+GET\s*\([^)]*\)\s*\{/.exec(code);
|
|
355
|
+
if (getMatch) {
|
|
356
|
+
const getStart = getMatch.index + getMatch[0].length;
|
|
357
|
+
let depth = 1, pos = getStart;
|
|
358
|
+
while (depth > 0 && pos < code.length) {
|
|
359
|
+
if (code[pos] === "{")
|
|
360
|
+
depth++;
|
|
361
|
+
else if (code[pos] === "}")
|
|
362
|
+
depth--;
|
|
363
|
+
pos++;
|
|
364
|
+
}
|
|
365
|
+
const getBody = code.substring(getStart, pos);
|
|
366
|
+
const hasMutationInGet = /(?:\.create\s*\(|\.update\s*\(|\.delete\s*\(|\.destroy\s*\(|\.remove\s*\(|\.insert\s*\(|\.upsert\s*\(|DELETE\s+FROM|UPDATE\s+\w|INSERT\s+INTO)/i.test(getBody);
|
|
367
|
+
if (!hasMutationInGet)
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
338
370
|
}
|
|
339
371
|
// Skip CVE version rules in peerDependencies (ranges, not actual versions)
|
|
340
372
|
if (isPeerDeps && rule.id === "VG903")
|
|
341
373
|
continue;
|
|
374
|
+
// Skip VG020 (wildcard dependency version) in lock files — engine constraints
|
|
375
|
+
// like "node": ">=6" are not dependency versions
|
|
376
|
+
if (rule.id === "VG020" && filePath && /(?:package-lock\.json|yarn\.lock|pnpm-lock\.yaml|npm-shrinkwrap\.json)$/.test(filePath))
|
|
377
|
+
continue;
|
|
342
378
|
// VG872/VG873 legitimate package filtering is handled at match level below
|
|
343
379
|
// Skip server-only import rule (VG964) for files that are inherently server-only:
|
|
344
380
|
// Route Handlers (app/api/), middleware, instrumentation, next.config,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.6",
|
|
4
4
|
"mcpName": "io.github.goklab/guardvibe",
|
|
5
5
|
"description": "Security MCP for vibe coding. 334 rules, 31 tools, CLI + doctor. Host security: CVE-2025-59536 hook injection, CVE-2026-21852 base URL hijack, MCP config audit, AI host hardening. 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",
|