guardvibe 1.9.4 → 1.9.5

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "module";
3
- import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, unlinkSync } from "fs";
3
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, unlinkSync, statSync } from "fs";
4
4
  import { join, dirname } from "path";
5
5
  import { homedir } from "os";
6
6
  const require = createRequire(import.meta.url);
@@ -523,8 +523,8 @@ function printUsage() {
523
523
  Options:
524
524
  --format <type> Output format: markdown (default), json, sarif
525
525
  --output <file> Write results to file instead of stdout
526
- --fail-on <level> Exit 1 when findings at this level exist
527
- Options: critical (default), high, medium, low, none
526
+ --fail-on <level> Exit 1 when findings at this level or above exist
527
+ critical (default) | high | medium | low | none
528
528
  --baseline <file> Compare against a previous scan JSON for fix tracking
529
529
  --save-baseline Save current scan as baseline (.guardvibe-baseline.json)
530
530
  --version, -V Print version and exit
@@ -618,7 +618,14 @@ async function main() {
618
618
  const cliArgs = args.slice(1);
619
619
  const { flags, positional } = parseArgs(cliArgs);
620
620
  const targetPath = positional[0] ?? ".";
621
- await runDirectoryScan(targetPath, flags);
621
+ // If target is a file (not directory), auto-redirect to check mode
622
+ if (targetPath !== "." && existsSync(targetPath) && !statSync(targetPath).isDirectory()) {
623
+ console.log(` [INFO] "${targetPath}" is a file. Running: guardvibe check ${targetPath}\n`);
624
+ await runFileCheck(targetPath, flags);
625
+ }
626
+ else {
627
+ await runDirectoryScan(targetPath, flags);
628
+ }
622
629
  }
623
630
  else if (command === "diff") {
624
631
  const cliArgs = args.slice(1);
@@ -163,6 +163,10 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
163
163
  const rateLimitRuleIds = new Set(["VG956", "VG030"]);
164
164
  if (isCronRoute && rateLimitRuleIds.has(rule.id))
165
165
  continue;
166
+ // Context-aware: skip rate limiting rules for webhook routes
167
+ // Webhooks are called by external services, not users — rate limiting is irrelevant
168
+ if (isWebhookRoute && rateLimitRuleIds.has(rule.id))
169
+ continue;
166
170
  // Context-aware: skip rate limiting rules for admin routes that have admin auth
167
171
  const isAdminRoute = filePath && /\/admin\//i.test(filePath);
168
172
  const hasAdminAuth = isAdminRoute && /(?:requireAdmin|adminOnly|orgRole|org:admin|isAdmin|checkRole|requireRole)/i.test(code);
@@ -126,7 +126,7 @@ export function checkProject(files, format = "markdown", rules) {
126
126
  actionItems.forEach((item, i) => {
127
127
  const fileCount = item.files.size;
128
128
  const fileLabel = fileCount === 1 ? "1 file" : `${fileCount} files`;
129
- lines.push(`${i + 1}. **[${item.rule.severity.toUpperCase()}] ${item.rule.name}** (${item.rule.id}) — ${item.count} occurrences in ${fileLabel}`, ` ${item.rule.fix}`, ``);
129
+ lines.push(`${i + 1}. **[${item.rule.severity.toUpperCase()}] ${item.rule.name}** (${item.rule.id}) — ${item.count} ${item.count === 1 ? "occurrence" : "occurrences"} in ${fileLabel}`, ` ${item.rule.fix}`, ``);
130
130
  });
131
131
  }
132
132
  lines.push(`---`, ``);
@@ -220,7 +220,7 @@ export function scanDirectory(path, recursive = true, exclude = [], format = "ma
220
220
  actionItems.forEach((item, i) => {
221
221
  const fileCount = item.files.size;
222
222
  const fileLabel = fileCount === 1 ? "1 file" : `${fileCount} files`;
223
- lines.push(`${i + 1}. **[${item.rule.severity.toUpperCase()}] ${item.rule.name}** (${item.rule.id}) — ${item.count} occurrences in ${fileLabel}`, ` ${item.rule.fix}`, ``);
223
+ lines.push(`${i + 1}. **[${item.rule.severity.toUpperCase()}] ${item.rule.name}** (${item.rule.id}) — ${item.count} ${item.count === 1 ? "occurrence" : "occurrences"} in ${fileLabel}`, ` ${item.rule.fix}`, ``);
224
224
  });
225
225
  lines.push(`---`, ``);
226
226
  for (const result of scanResults) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "1.9.4",
3
+ "version": "1.9.5",
4
4
  "description": "Security MCP for vibe coding. 277 rules, 24 tools for Next.js, Supabase, Clerk, Stripe, Prisma, tRPC, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK, and the full AI-generated stack.",
5
5
  "type": "module",
6
6
  "bin": {