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.
- package/build/data/compliance-metadata.d.ts +24 -0
- package/build/data/compliance-metadata.d.ts.map +1 -0
- package/build/data/compliance-metadata.js +274 -0
- package/build/data/compliance-metadata.js.map +1 -0
- package/build/data/rules/index.d.ts.map +1 -1
- package/build/data/rules/index.js +3 -2
- package/build/data/rules/index.js.map +1 -1
- package/build/data/rules/types.d.ts +2 -0
- package/build/data/rules/types.d.ts.map +1 -1
- package/build/index.js +28 -8
- package/build/index.js.map +1 -1
- package/build/tools/audit-config.d.ts +11 -0
- package/build/tools/audit-config.d.ts.map +1 -0
- package/build/tools/audit-config.js +370 -0
- package/build/tools/audit-config.js.map +1 -0
- package/build/tools/compliance-report.d.ts +1 -1
- package/build/tools/compliance-report.d.ts.map +1 -1
- package/build/tools/compliance-report.js +110 -11
- package/build/tools/compliance-report.js.map +1 -1
- package/build/tools/generate-policy.d.ts +2 -0
- package/build/tools/generate-policy.d.ts.map +1 -0
- package/build/tools/generate-policy.js +368 -0
- package/build/tools/generate-policy.js.map +1 -0
- package/build/tools/scan-directory.d.ts +1 -1
- package/build/tools/scan-directory.d.ts.map +1 -1
- package/build/tools/scan-directory.js +121 -7
- package/build/tools/scan-directory.js.map +1 -1
- package/package.json +2 -2
|
@@ -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":"
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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: ${
|
|
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)
|