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 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
- const results = await checkDependencies(packages);
137
- return {
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
- const results = await checkPackageHealth(packages, format);
262
- return { content: [{ type: "text", text: results }] };
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 VG131 (state-changing GET) when only read operations are present
331
- if (rule.id === "VG131") {
332
- // If code only has read operations (findMany, findFirst, count, aggregate, select)
333
- // and no actual mutations, skip this rule
334
- const hasMutation = /(?:\.create\s*\(|\.update\s*\(|\.delete\s*\(|\.destroy\s*\(|\.remove\s*\(|\.insert\s*\(|DELETE\s+FROM|UPDATE\s+\w|INSERT\s+INTO)/i.test(code);
335
- const onlyInComments = !hasMutation;
336
- if (onlyInComments)
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.5",
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",