guardvibe 1.3.3 → 1.4.0

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.
@@ -0,0 +1,368 @@
1
+ import { readFileSync, existsSync, readdirSync } from "fs";
2
+ import { join, resolve, extname } from "path";
3
+ function tryRead(path) {
4
+ try {
5
+ return existsSync(path) ? readFileSync(path, "utf-8") : null;
6
+ }
7
+ catch {
8
+ return null;
9
+ }
10
+ }
11
+ function collectSourceFiles(dir, results, depth = 0) {
12
+ if (depth > 6)
13
+ return;
14
+ const skip = new Set(["node_modules", ".git", ".next", "build", "dist", "coverage", ".turbo", "vendor"]);
15
+ try {
16
+ const entries = readdirSync(dir, { withFileTypes: true });
17
+ for (const entry of entries) {
18
+ if (skip.has(entry.name))
19
+ continue;
20
+ const full = join(dir, entry.name);
21
+ if (entry.isDirectory()) {
22
+ collectSourceFiles(full, results, depth + 1);
23
+ }
24
+ else if (entry.isFile()) {
25
+ const ext = extname(entry.name).toLowerCase();
26
+ if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".env", ".json", ".toml", ".yaml", ".yml"].includes(ext) ||
27
+ entry.name === ".env" || entry.name === ".env.local" || entry.name === ".env.example") {
28
+ results.push(full);
29
+ }
30
+ }
31
+ }
32
+ }
33
+ catch { /* skip */ }
34
+ }
35
+ function detectStack(root) {
36
+ const pkg = tryRead(join(root, "package.json"));
37
+ const deps = pkg ? { ...JSON.parse(pkg).dependencies, ...JSON.parse(pkg).devDependencies } : {};
38
+ const depKeys = Object.keys(deps);
39
+ const files = [];
40
+ collectSourceFiles(root, files);
41
+ const allContent = files.slice(0, 200).map(f => {
42
+ try {
43
+ return readFileSync(f, "utf-8").substring(0, 5000);
44
+ }
45
+ catch {
46
+ return "";
47
+ }
48
+ }).join("\n");
49
+ const has = (pattern) => depKeys.some(d => d.includes(pattern)) || allContent.includes(pattern);
50
+ const stack = {
51
+ framework: null,
52
+ css: [], auth: [], database: [], payments: [],
53
+ ai: [], storage: [], cms: [], analytics: [], cdns: [],
54
+ };
55
+ // Framework
56
+ if (has("next"))
57
+ stack.framework = "nextjs";
58
+ else if (has("nuxt"))
59
+ stack.framework = "nuxt";
60
+ else if (has("svelte"))
61
+ stack.framework = "sveltekit";
62
+ else if (has("astro"))
63
+ stack.framework = "astro";
64
+ else if (has("remix"))
65
+ stack.framework = "remix";
66
+ // CSS
67
+ if (has("tailwindcss"))
68
+ stack.css.push("tailwindcss");
69
+ if (has("@radix-ui") || has("shadcn"))
70
+ stack.css.push("radix-ui");
71
+ // Auth
72
+ if (has("@clerk"))
73
+ stack.auth.push("clerk");
74
+ if (has("next-auth") || has("@auth/"))
75
+ stack.auth.push("next-auth");
76
+ if (has("@supabase/auth"))
77
+ stack.auth.push("supabase-auth");
78
+ if (has("firebase/auth") || has("firebase-admin"))
79
+ stack.auth.push("firebase-auth");
80
+ if (has("@descope"))
81
+ stack.auth.push("descope");
82
+ // Database
83
+ if (has("@supabase"))
84
+ stack.database.push("supabase");
85
+ if (has("prisma") || has("@prisma"))
86
+ stack.database.push("prisma");
87
+ if (has("drizzle"))
88
+ stack.database.push("drizzle");
89
+ if (has("@neondatabase") || has("@vercel/postgres"))
90
+ stack.database.push("neon");
91
+ if (has("mongoose") || has("mongodb"))
92
+ stack.database.push("mongodb");
93
+ if (has("@upstash/redis"))
94
+ stack.database.push("upstash-redis");
95
+ // Payments
96
+ if (has("stripe"))
97
+ stack.payments.push("stripe");
98
+ if (has("@polar"))
99
+ stack.payments.push("polar");
100
+ if (has("lemonsqueezy") || has("@lemonsqueezy"))
101
+ stack.payments.push("lemonsqueezy");
102
+ // AI
103
+ if (has("openai") || has("@ai-sdk") || has("OPENAI_API_KEY"))
104
+ stack.ai.push("openai");
105
+ if (has("anthropic") || has("ANTHROPIC_API_KEY"))
106
+ stack.ai.push("anthropic");
107
+ if (has("@google/generative-ai") || has("@ai-sdk/google"))
108
+ stack.ai.push("google-ai");
109
+ // Storage
110
+ if (has("@vercel/blob"))
111
+ stack.storage.push("vercel-blob");
112
+ if (has("@aws-sdk/client-s3") || has("aws-sdk"))
113
+ stack.storage.push("s3");
114
+ if (has("cloudinary"))
115
+ stack.storage.push("cloudinary");
116
+ if (has("@uploadthing"))
117
+ stack.storage.push("uploadthing");
118
+ // CMS
119
+ if (has("sanity") || has("@sanity"))
120
+ stack.cms.push("sanity");
121
+ if (has("contentful"))
122
+ stack.cms.push("contentful");
123
+ // Analytics
124
+ if (has("@vercel/analytics"))
125
+ stack.analytics.push("vercel-analytics");
126
+ if (has("posthog") || has("@posthog"))
127
+ stack.analytics.push("posthog");
128
+ if (has("@sentry"))
129
+ stack.analytics.push("sentry");
130
+ // CDN detection from content
131
+ const cdnPatterns = [
132
+ ["fonts.googleapis.com", /fonts\.googleapis\.com/],
133
+ ["fonts.gstatic.com", /fonts\.gstatic\.com/],
134
+ ["cdn.jsdelivr.net", /cdn\.jsdelivr\.net/],
135
+ ["unpkg.com", /unpkg\.com/],
136
+ ["cdnjs.cloudflare.com", /cdnjs\.cloudflare\.com/],
137
+ ["vercel.live", /vercel\.live/],
138
+ ["va.vercel-scripts.com", /va\.vercel-scripts\.com/],
139
+ ];
140
+ for (const [cdn, pattern] of cdnPatterns) {
141
+ if (pattern.test(allContent))
142
+ stack.cdns.push(cdn);
143
+ }
144
+ return stack;
145
+ }
146
+ function generateCSP(stack) {
147
+ const directives = {
148
+ "default-src": ["'self'"],
149
+ "script-src": ["'self'"],
150
+ "style-src": ["'self'", "'unsafe-inline'"],
151
+ "img-src": ["'self'", "data:", "blob:"],
152
+ "font-src": ["'self'"],
153
+ "connect-src": ["'self'"],
154
+ "frame-src": ["'none'"],
155
+ "object-src": ["'none'"],
156
+ "base-uri": ["'self'"],
157
+ "form-action": ["'self'"],
158
+ "frame-ancestors": ["'none'"],
159
+ };
160
+ // Script sources
161
+ if (stack.framework === "nextjs") {
162
+ directives["script-src"].push("'unsafe-eval'"); // needed for dev, remove in production ideally
163
+ }
164
+ if (stack.analytics.includes("vercel-analytics")) {
165
+ directives["script-src"].push("https://va.vercel-scripts.com");
166
+ directives["connect-src"].push("https://vitals.vercel-insights.com");
167
+ }
168
+ if (stack.analytics.includes("posthog")) {
169
+ directives["script-src"].push("https://us.i.posthog.com", "https://eu.i.posthog.com");
170
+ directives["connect-src"].push("https://us.i.posthog.com", "https://eu.i.posthog.com");
171
+ }
172
+ if (stack.analytics.includes("sentry")) {
173
+ directives["script-src"].push("https://*.sentry.io");
174
+ directives["connect-src"].push("https://*.sentry.io");
175
+ }
176
+ // Image sources
177
+ if (stack.storage.includes("vercel-blob")) {
178
+ directives["img-src"].push("https://*.public.blob.vercel-storage.com");
179
+ }
180
+ if (stack.storage.includes("s3")) {
181
+ directives["img-src"].push("https://*.s3.amazonaws.com");
182
+ }
183
+ if (stack.storage.includes("cloudinary")) {
184
+ directives["img-src"].push("https://res.cloudinary.com");
185
+ }
186
+ if (stack.storage.includes("uploadthing")) {
187
+ directives["img-src"].push("https://utfs.io");
188
+ }
189
+ if (stack.cms.includes("sanity")) {
190
+ directives["img-src"].push("https://cdn.sanity.io");
191
+ }
192
+ if (stack.cms.includes("contentful")) {
193
+ directives["img-src"].push("https://images.ctfassets.net");
194
+ }
195
+ // Font sources
196
+ for (const cdn of stack.cdns) {
197
+ if (cdn.includes("fonts.googleapis")) {
198
+ directives["style-src"].push("https://fonts.googleapis.com");
199
+ directives["font-src"].push("https://fonts.gstatic.com");
200
+ }
201
+ if (cdn.includes("jsdelivr") || cdn.includes("unpkg") || cdn.includes("cdnjs")) {
202
+ directives["script-src"].push(`https://${cdn}`);
203
+ }
204
+ }
205
+ // Connect sources for auth
206
+ if (stack.auth.includes("clerk")) {
207
+ directives["connect-src"].push("https://*.clerk.accounts.dev", "https://clerk.com");
208
+ directives["script-src"].push("https://*.clerk.accounts.dev");
209
+ directives["frame-src"] = ["'self'", "https://*.clerk.accounts.dev"];
210
+ }
211
+ if (stack.auth.includes("supabase-auth")) {
212
+ directives["connect-src"].push("https://*.supabase.co");
213
+ }
214
+ if (stack.auth.includes("firebase-auth")) {
215
+ directives["connect-src"].push("https://*.firebaseapp.com", "https://*.googleapis.com");
216
+ directives["frame-src"] = ["'self'", "https://*.firebaseapp.com"];
217
+ }
218
+ // Connect sources for payments
219
+ if (stack.payments.includes("stripe")) {
220
+ directives["script-src"].push("https://js.stripe.com");
221
+ directives["frame-src"] = [...(directives["frame-src"].includes("'none'") ? ["'self'"] : directives["frame-src"]), "https://js.stripe.com"];
222
+ directives["connect-src"].push("https://api.stripe.com");
223
+ }
224
+ // Connect sources for AI
225
+ if (stack.ai.includes("openai"))
226
+ directives["connect-src"].push("https://api.openai.com");
227
+ if (stack.ai.includes("anthropic"))
228
+ directives["connect-src"].push("https://api.anthropic.com");
229
+ if (stack.ai.includes("google-ai"))
230
+ directives["connect-src"].push("https://generativelanguage.googleapis.com");
231
+ // Database connect
232
+ if (stack.database.includes("supabase")) {
233
+ directives["connect-src"].push("https://*.supabase.co");
234
+ }
235
+ // Deduplicate
236
+ for (const key of Object.keys(directives)) {
237
+ directives[key] = [...new Set(directives[key])];
238
+ }
239
+ return Object.entries(directives)
240
+ .map(([key, values]) => `${key} ${values.join(" ")}`)
241
+ .join("; ");
242
+ }
243
+ function generateCORS(stack) {
244
+ const origins = [];
245
+ if (stack.auth.includes("clerk"))
246
+ origins.push("https://*.clerk.accounts.dev");
247
+ if (stack.payments.includes("stripe"))
248
+ origins.push("https://js.stripe.com");
249
+ return {
250
+ allowedOrigins: origins.length > 0 ? origins : ["https://yourdomain.com"],
251
+ allowedMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
252
+ allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
253
+ maxAge: 86400,
254
+ };
255
+ }
256
+ function generateRLS(stack) {
257
+ const suggestions = [];
258
+ if (stack.database.includes("supabase")) {
259
+ suggestions.push({
260
+ table: "profiles",
261
+ policy: `CREATE POLICY "Users can view own profile" ON profiles FOR SELECT USING (auth.uid() = id);`,
262
+ description: "Restrict profile reads to the owner only.",
263
+ }, {
264
+ table: "profiles",
265
+ policy: `CREATE POLICY "Users can update own profile" ON profiles FOR UPDATE USING (auth.uid() = id) WITH CHECK (auth.uid() = id);`,
266
+ description: "Restrict profile updates to the owner only.",
267
+ }, {
268
+ table: "*",
269
+ policy: `ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;`,
270
+ description: "Enable RLS on every table. Without RLS enabled, all data is publicly accessible via the Supabase client.",
271
+ }, {
272
+ table: "*",
273
+ policy: `REVOKE ALL ON your_table FROM anon; GRANT SELECT ON your_table TO anon;`,
274
+ description: "Restrict anonymous role to read-only on public tables.",
275
+ });
276
+ if (stack.payments.length > 0) {
277
+ suggestions.push({
278
+ table: "subscriptions",
279
+ policy: `CREATE POLICY "Users can view own subscription" ON subscriptions FOR SELECT USING (auth.uid() = user_id);`,
280
+ description: "Protect subscription data — users should only see their own.",
281
+ });
282
+ }
283
+ }
284
+ if (stack.database.includes("prisma") || stack.database.includes("drizzle")) {
285
+ suggestions.push({
286
+ table: "N/A (ORM-level)",
287
+ policy: `// Always filter by authenticated user\nconst items = await prisma.item.findMany({ where: { userId: session.user.id } });`,
288
+ description: "Without RLS, enforce row-level access in your ORM queries. Always include user ID in WHERE clauses.",
289
+ });
290
+ }
291
+ return suggestions;
292
+ }
293
+ function generateRateLimiting(stack) {
294
+ return {
295
+ global: { requests: 100, window: "1m" },
296
+ auth: { requests: 5, window: "1m" },
297
+ api: stack.ai.length > 0
298
+ ? { requests: 20, window: "1m" }
299
+ : { requests: 60, window: "1m" },
300
+ };
301
+ }
302
+ function generateHeaders(stack) {
303
+ const headers = [
304
+ { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload", description: "Enforce HTTPS for all connections." },
305
+ { key: "X-Frame-Options", value: "DENY", description: "Prevent clickjacking by blocking iframe embedding." },
306
+ { key: "X-Content-Type-Options", value: "nosniff", description: "Prevent MIME-type sniffing attacks." },
307
+ { key: "Referrer-Policy", value: "strict-origin-when-cross-origin", description: "Control referrer information sent to other sites." },
308
+ { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()", description: "Disable sensitive browser APIs unless explicitly needed." },
309
+ ];
310
+ if (stack.framework === "nextjs") {
311
+ headers.push({ key: "X-DNS-Prefetch-Control", value: "on", description: "Enable DNS prefetching for performance." });
312
+ }
313
+ return headers;
314
+ }
315
+ export function generatePolicy(path, format = "markdown") {
316
+ const root = resolve(path);
317
+ const stack = detectStack(root);
318
+ const csp = generateCSP(stack);
319
+ const cors = generateCORS(stack);
320
+ const rls = generateRLS(stack);
321
+ const rateLimiting = generateRateLimiting(stack);
322
+ const headers = generateHeaders(stack);
323
+ const policy = { stack, csp, cors, rls, rateLimiting, headers };
324
+ if (format === "json") {
325
+ return JSON.stringify(policy);
326
+ }
327
+ const lines = [
328
+ `# GuardVibe Security Policy Generator`,
329
+ ``,
330
+ `Directory: ${root}`,
331
+ ``,
332
+ `## Detected Stack`,
333
+ `- Framework: ${stack.framework ?? "unknown"}`,
334
+ ];
335
+ if (stack.auth.length > 0)
336
+ lines.push(`- Auth: ${stack.auth.join(", ")}`);
337
+ if (stack.database.length > 0)
338
+ lines.push(`- Database: ${stack.database.join(", ")}`);
339
+ if (stack.payments.length > 0)
340
+ lines.push(`- Payments: ${stack.payments.join(", ")}`);
341
+ if (stack.ai.length > 0)
342
+ lines.push(`- AI: ${stack.ai.join(", ")}`);
343
+ if (stack.storage.length > 0)
344
+ lines.push(`- Storage: ${stack.storage.join(", ")}`);
345
+ if (stack.cms.length > 0)
346
+ lines.push(`- CMS: ${stack.cms.join(", ")}`);
347
+ if (stack.analytics.length > 0)
348
+ lines.push(`- Analytics: ${stack.analytics.join(", ")}`);
349
+ lines.push(``);
350
+ lines.push(`## Content-Security-Policy`, ``, "```", csp, "```", ``, `### Next.js Configuration`, ``, "```typescript", `// next.config.ts`, `async headers() {`, ` return [{`, ` source: "/(.*)",`, ` headers: [`, ` { key: "Content-Security-Policy", value: \`${csp}\` },`, ...headers.map(h => ` { key: "${h.key}", value: "${h.value}" },`), ` ]`, ` }];`, `}`, "```", ``);
351
+ lines.push(`## CORS Policy`, ``, "```typescript", `// Recommended CORS configuration`, `const corsConfig = {`, ` allowedOrigins: ${JSON.stringify(cors.allowedOrigins)},`, ` allowedMethods: ${JSON.stringify(cors.allowedMethods)},`, ` allowedHeaders: ${JSON.stringify(cors.allowedHeaders)},`, ` maxAge: ${cors.maxAge},`, `};`, "```", ``);
352
+ if (rls.length > 0) {
353
+ lines.push(`## Row-Level Security Suggestions`, ``);
354
+ for (const r of rls) {
355
+ lines.push(`### ${r.table}`, r.description, "```sql", r.policy, "```", ``);
356
+ }
357
+ }
358
+ lines.push(`## Rate Limiting`, ``, `| Endpoint | Limit | Window |`, `|----------|-------|--------|`, `| Global | ${rateLimiting.global.requests} req | ${rateLimiting.global.window} |`, `| Auth (login/register) | ${rateLimiting.auth.requests} req | ${rateLimiting.auth.window} |`, `| API | ${rateLimiting.api.requests} req | ${rateLimiting.api.window} |`, ``);
359
+ if (stack.database.includes("upstash-redis")) {
360
+ lines.push(`### Upstash Rate Limit Implementation`, ``, "```typescript", `import { Ratelimit } from "@upstash/ratelimit";`, `import { Redis } from "@upstash/redis";`, ``, `const ratelimit = new Ratelimit({`, ` redis: Redis.fromEnv(),`, ` limiter: Ratelimit.slidingWindow(${rateLimiting.api.requests}, "${rateLimiting.api.window}"),`, `});`, "```", ``);
361
+ }
362
+ lines.push(`## Security Headers`, ``, `| Header | Value | Purpose |`, `|--------|-------|---------|`);
363
+ for (const h of headers) {
364
+ lines.push(`| ${h.key} | ${h.value} | ${h.description} |`);
365
+ }
366
+ return lines.join("\n");
367
+ }
368
+ //# sourceMappingURL=generate-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-policy.js","sourceRoot":"","sources":["../../src/tools/generate-policy.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
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[]): string;
2
+ export declare function scanDirectory(path: string, recursive?: boolean, exclude?: string[], format?: "markdown" | "json", rules?: SecurityRule[], baselinePath?: string): string;
3
3
  //# sourceMappingURL=scan-directory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scan-directory.d.ts","sourceRoot":"","sources":["../../src/tools/scan-directory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAsE3D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAc,EACzB,OAAO,GAAE,MAAM,EAAO,EACtB,MAAM,GAAE,UAAU,GAAG,MAAmB,EACxC,KAAK,CAAC,EAAE,YAAY,EAAE,GACrB,MAAM,CA+GR"}
1
+ {"version":3,"file":"scan-directory.d.ts","sourceRoot":"","sources":["../../src/tools/scan-directory.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0I3D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAc,EACzB,OAAO,GAAE,MAAM,EAAO,EACtB,MAAM,GAAE,UAAU,GAAG,MAAmB,EACxC,KAAK,CAAC,EAAE,YAAY,EAAE,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAoNR"}
@@ -1,6 +1,7 @@
1
1
  import { readdirSync, readFileSync, statSync } from "fs";
2
2
  import { join, extname, basename, resolve } from "path";
3
- import { analyzeCode, formatFindingsJson } from "./check-code.js";
3
+ import { createHash, randomUUID } from "crypto";
4
+ import { analyzeCode } from "./check-code.js";
4
5
  import { loadConfig } from "../utils/config.js";
5
6
  const DEFAULT_EXCLUDES = new Set([
6
7
  "node_modules", ".git", "build", "dist", "vendor", "__pycache__",
@@ -27,6 +28,8 @@ const CONFIG_FILE_MAP = {
27
28
  "render.yaml": "render-config",
28
29
  "netlify.toml": "netlify-config",
29
30
  };
31
+ // GuardVibe version — used in scan metadata
32
+ const GUARDVIBE_VERSION = "1.4.0";
30
33
  function walkDirectory(dir, recursive, excludes, results) {
31
34
  let entries;
32
35
  try {
@@ -47,7 +50,6 @@ function walkDirectory(dir, recursive, excludes, results) {
47
50
  if (EXTENSION_MAP[ext]) {
48
51
  results.push(fullPath);
49
52
  }
50
- // Also detect Dockerfiles and config files by name
51
53
  if (entry.name.startsWith("Dockerfile") || entry.name.endsWith(".dockerfile")) {
52
54
  results.push(fullPath);
53
55
  }
@@ -57,7 +59,39 @@ function walkDirectory(dir, recursive, excludes, results) {
57
59
  }
58
60
  }
59
61
  }
60
- export function scanDirectory(path, recursive = true, exclude = [], format = "markdown", rules) {
62
+ function hashContent(content) {
63
+ return createHash("sha256").update(content).digest("hex").substring(0, 16);
64
+ }
65
+ function findingsToBaseline(scanResults) {
66
+ const entries = [];
67
+ for (const result of scanResults) {
68
+ for (const f of result.findings) {
69
+ entries.push({
70
+ id: f.rule.id,
71
+ name: f.rule.name,
72
+ severity: f.rule.severity,
73
+ file: result.path,
74
+ line: f.line,
75
+ match: f.match,
76
+ });
77
+ }
78
+ }
79
+ return entries;
80
+ }
81
+ function computeBaselineDiff(current, previous) {
82
+ const prevKey = (e) => `${e.id}:${e.file}:${e.match}`;
83
+ const currKey = (e) => `${e.id}:${e.file}:${e.match}`;
84
+ const prevSet = new Set(previous.map(prevKey));
85
+ const currSet = new Set(current.map(currKey));
86
+ return {
87
+ new: current.filter(e => !prevSet.has(currKey(e))),
88
+ fixed: previous.filter(e => !currSet.has(prevKey(e))),
89
+ unchanged: current.filter(e => prevSet.has(currKey(e))),
90
+ };
91
+ }
92
+ export function scanDirectory(path, recursive = true, exclude = [], format = "markdown", rules, baselinePath) {
93
+ const startTime = performance.now();
94
+ const scanId = randomUUID();
61
95
  const scanRoot = resolve(path);
62
96
  const config = loadConfig(scanRoot);
63
97
  const excludes = new Set([...DEFAULT_EXCLUDES, ...exclude, ...config.scan.exclude]);
@@ -66,6 +100,8 @@ export function scanDirectory(path, recursive = true, exclude = [], format = "ma
66
100
  walkDirectory(scanRoot, recursive, excludes, filePaths);
67
101
  const scanResults = [];
68
102
  const skippedFiles = [];
103
+ const fileHashes = {};
104
+ const effectiveRules = rules ?? [];
69
105
  for (const filePath of filePaths) {
70
106
  try {
71
107
  const stat = statSync(filePath);
@@ -74,13 +110,12 @@ export function scanDirectory(path, recursive = true, exclude = [], format = "ma
74
110
  continue;
75
111
  }
76
112
  const content = readFileSync(filePath, "utf-8");
113
+ fileHashes[filePath] = hashContent(content);
77
114
  const ext = extname(filePath).toLowerCase();
78
115
  let language = EXTENSION_MAP[ext];
79
- // Detect Dockerfile by name
80
116
  if (!language && (basename(filePath).startsWith("Dockerfile") || ext === ".dockerfile")) {
81
117
  language = "dockerfile";
82
118
  }
83
- // Detect config files by name
84
119
  if (!language) {
85
120
  language = CONFIG_FILE_MAP[basename(filePath)];
86
121
  }
@@ -95,6 +130,17 @@ export function scanDirectory(path, recursive = true, exclude = [], format = "ma
95
130
  skippedFiles.push(`${filePath} (read error)`);
96
131
  }
97
132
  }
133
+ const scanDurationMs = Math.round(performance.now() - startTime);
134
+ const metadata = {
135
+ scanId,
136
+ timestamp: new Date().toISOString(),
137
+ guardvibeVersion: GUARDVIBE_VERSION,
138
+ ruleCount: effectiveRules.length > 0 ? effectiveRules.length : 239,
139
+ scanDurationMs,
140
+ filesScanned: filePaths.length - skippedFiles.length,
141
+ filesSkipped: skippedFiles.length,
142
+ fileHashes,
143
+ };
98
144
  // Scoring
99
145
  const allFindings = scanResults.flatMap(r => r.findings);
100
146
  const totalCritical = allFindings.filter(f => f.rule.severity === "critical").length;
@@ -103,19 +149,87 @@ export function scanDirectory(path, recursive = true, exclude = [], format = "ma
103
149
  const totalIssues = totalCritical + totalHigh + totalMedium;
104
150
  const score = Math.max(0, Math.min(100, 100 - totalCritical * 25 - totalHigh * 10 - totalMedium * 5));
105
151
  const grade = score >= 90 ? "A" : score >= 75 ? "B" : score >= 60 ? "C" : score >= 40 ? "D" : "F";
152
+ // Baseline comparison
153
+ let baselineDiff = null;
154
+ let previousBaseline = null;
155
+ if (baselinePath) {
156
+ try {
157
+ const baselineContent = readFileSync(resolve(baselinePath), "utf-8");
158
+ const parsed = JSON.parse(baselineContent);
159
+ previousBaseline = {
160
+ report: { scanId: parsed.metadata?.scanId ?? "unknown", timestamp: parsed.metadata?.timestamp ?? "unknown" },
161
+ findings: parsed.baseline ?? [],
162
+ };
163
+ const currentEntries = findingsToBaseline(scanResults);
164
+ baselineDiff = computeBaselineDiff(currentEntries, previousBaseline.findings);
165
+ }
166
+ catch {
167
+ // baseline file unreadable, skip comparison
168
+ }
169
+ }
106
170
  if (format === "json") {
107
171
  const findingsWithFiles = scanResults.flatMap(r => r.findings.map(f => ({ ...f, rule: f.rule, file: r.path })));
108
- return formatFindingsJson(findingsWithFiles, { grade, score });
172
+ const baseJson = {
173
+ summary: {
174
+ total: allFindings.length,
175
+ critical: totalCritical, high: totalHigh, medium: totalMedium,
176
+ low: allFindings.filter(f => f.rule.severity === "low").length,
177
+ blocked: totalCritical > 0 || totalHigh > 0,
178
+ grade, score,
179
+ },
180
+ metadata,
181
+ findings: findingsWithFiles.map(f => ({
182
+ id: f.rule.id, name: f.rule.name, severity: f.rule.severity,
183
+ owasp: f.rule.owasp, line: f.line, match: f.match, file: f.file,
184
+ fix: f.rule.fix, fixCode: f.rule.fixCode, compliance: f.rule.compliance,
185
+ })),
186
+ baseline: findingsToBaseline(scanResults),
187
+ };
188
+ if (baselineDiff) {
189
+ baseJson.baselineDiff = {
190
+ previousScanId: previousBaseline?.report.scanId,
191
+ previousTimestamp: previousBaseline?.report.timestamp,
192
+ new: baselineDiff.new.length,
193
+ fixed: baselineDiff.fixed.length,
194
+ unchanged: baselineDiff.unchanged.length,
195
+ newFindings: baselineDiff.new,
196
+ fixedFindings: baselineDiff.fixed,
197
+ };
198
+ }
199
+ return JSON.stringify(baseJson);
109
200
  }
201
+ // Markdown output
110
202
  const lines = [
111
203
  `# GuardVibe Directory Security Report`,
112
204
  ``,
205
+ `Scan ID: ${scanId}`,
206
+ `Timestamp: ${metadata.timestamp}`,
113
207
  `Directory: ${scanRoot}`,
114
- `Files scanned: ${filePaths.length - skippedFiles.length}`,
208
+ `Files scanned: ${metadata.filesScanned}`,
115
209
  `Total issues: ${totalIssues}`,
116
210
  `Security Score: ${grade} (${score}/100)`,
211
+ `Scan duration: ${scanDurationMs}ms`,
212
+ `GuardVibe: v${GUARDVIBE_VERSION} (${metadata.ruleCount} rules)`,
117
213
  ``,
118
214
  ];
215
+ // Baseline diff section
216
+ if (baselineDiff && previousBaseline) {
217
+ lines.push(`## Baseline Comparison`, ``, `Previous scan: ${previousBaseline.report.scanId} (${previousBaseline.report.timestamp})`, ``, `| Status | Count |`, `|--------|-------|`, `| New findings | ${baselineDiff.new.length} |`, `| Fixed findings | ${baselineDiff.fixed.length} |`, `| Unchanged | ${baselineDiff.unchanged.length} |`, ``);
218
+ if (baselineDiff.new.length > 0) {
219
+ lines.push(`### New Findings`, ``);
220
+ for (const entry of baselineDiff.new) {
221
+ lines.push(`- [${entry.severity.toUpperCase()}] ${entry.name} (${entry.id}) in ${entry.file}:${entry.line}`);
222
+ }
223
+ lines.push(``);
224
+ }
225
+ if (baselineDiff.fixed.length > 0) {
226
+ lines.push(`### Fixed Findings`, ``);
227
+ for (const entry of baselineDiff.fixed) {
228
+ lines.push(`- ~~[${entry.severity.toUpperCase()}] ${entry.name} (${entry.id}) in ${entry.file}:${entry.line}~~`);
229
+ }
230
+ lines.push(``);
231
+ }
232
+ }
119
233
  if (totalIssues > 0) {
120
234
  lines.push(`## Summary`, ``, `| Severity | Count |`, `|----------|-------|`);
121
235
  if (totalCritical > 0)