guardvibe 3.0.52 → 3.0.53
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/tools/full-audit.js +20 -1
- package/build/utils/osv-client.js +40 -36
- package/package.json +1 -1
|
@@ -223,11 +223,30 @@ export async function runFullAudit(path, options) {
|
|
|
223
223
|
}
|
|
224
224
|
// --- Section 3: Dependencies ---
|
|
225
225
|
if (!options?.skipDeps) {
|
|
226
|
-
|
|
226
|
+
// Prefer lockfiles for resolved (installed) versions over package.json spec ranges.
|
|
227
|
+
// package.json says `"@clerk/nextjs": "^7.0.1"` but the installed version may be 7.2.8.
|
|
228
|
+
// OSV needs the resolved version to give accurate vuln matches; spec lower bound
|
|
229
|
+
// produces false positives where the bug was already patched in a higher minor.
|
|
230
|
+
const lockCandidates = ["package-lock.json", "npm-shrinkwrap.json", "yarn.lock", "pnpm-lock.yaml"];
|
|
231
|
+
let manifestPath = resolve(projectRoot, "package.json");
|
|
232
|
+
for (const lock of lockCandidates) {
|
|
233
|
+
const lockPath = resolve(projectRoot, lock);
|
|
234
|
+
if (existsSync(lockPath)) {
|
|
235
|
+
manifestPath = lockPath;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
227
239
|
if (existsSync(manifestPath)) {
|
|
228
240
|
try {
|
|
229
241
|
const depsJson = await scanDependencies(manifestPath, "json");
|
|
230
242
|
const parsed = safeJsonParse(depsJson);
|
|
243
|
+
if (!parsed) {
|
|
244
|
+
// scanDependencies returned a markdown error (OSV API unreachable, parse failed, etc).
|
|
245
|
+
// Push a dependencies section so downstream consumers always see it; surface the
|
|
246
|
+
// error in `details` so users can troubleshoot instead of silently missing the section.
|
|
247
|
+
const errSnippet = (depsJson.match(/Error:[^\n]+/) || ["Scan failed"])[0];
|
|
248
|
+
sections.push({ name: "dependencies", status: "error", findings: 0, critical: 0, high: 0, medium: 0, details: errSnippet });
|
|
249
|
+
}
|
|
231
250
|
if (parsed) {
|
|
232
251
|
const vulnPackages = parsed.summary?.vulnerable ?? 0;
|
|
233
252
|
const depFindings = [];
|
|
@@ -15,47 +15,51 @@ export async function queryOsv(name, version, ecosystem) {
|
|
|
15
15
|
return data.vulns ?? [];
|
|
16
16
|
}
|
|
17
17
|
export async function queryOsvBatch(packages) {
|
|
18
|
-
const queries = packages.map(pkg => ({
|
|
19
|
-
package: { name: pkg.name, ecosystem: pkg.ecosystem },
|
|
20
|
-
version: pkg.version,
|
|
21
|
-
}));
|
|
22
|
-
const response = await fetch("https://api.osv.dev/v1/querybatch", {
|
|
23
|
-
method: "POST",
|
|
24
|
-
headers: { "Content-Type": "application/json" },
|
|
25
|
-
body: JSON.stringify({ queries }),
|
|
26
|
-
signal: AbortSignal.timeout(10000),
|
|
27
|
-
});
|
|
28
18
|
const results = new Map();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
19
|
+
// OSV batch can time out on large monorepo lockfiles (1000+ packages),
|
|
20
|
+
// and very-large requests can be rate-limited. Chunk into 500-pkg batches
|
|
21
|
+
// and process sequentially so the per-request timeout is generous.
|
|
22
|
+
const CHUNK_SIZE = 500;
|
|
23
|
+
for (let start = 0; start < packages.length; start += CHUNK_SIZE) {
|
|
24
|
+
const chunk = packages.slice(start, start + CHUNK_SIZE);
|
|
25
|
+
const queries = chunk.map(pkg => ({
|
|
26
|
+
package: { name: pkg.name, ecosystem: pkg.ecosystem },
|
|
27
|
+
version: pkg.version,
|
|
28
|
+
}));
|
|
29
|
+
const response = await fetch("https://api.osv.dev/v1/querybatch", {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: JSON.stringify({ queries }),
|
|
33
|
+
signal: AbortSignal.timeout(60000),
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(`OSV batch API error: ${response.status} ${response.statusText}`);
|
|
40
37
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (vulnResponse.ok) {
|
|
49
|
-
const vulnData = await vulnResponse.json();
|
|
50
|
-
fullVulns.push(vulnData);
|
|
51
|
-
}
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
40
|
+
const key = `${chunk[i].name}@${chunk[i].version}`;
|
|
41
|
+
const batchVulns = data.results[i]?.vulns || [];
|
|
42
|
+
if (batchVulns.length === 0) {
|
|
43
|
+
results.set(key, []);
|
|
44
|
+
continue;
|
|
52
45
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
const fullVulns = [];
|
|
47
|
+
for (const bv of batchVulns) {
|
|
48
|
+
try {
|
|
49
|
+
const vulnResponse = await fetch(`https://api.osv.dev/v1/vulns/${bv.id}`, {
|
|
50
|
+
signal: AbortSignal.timeout(5000),
|
|
51
|
+
});
|
|
52
|
+
if (vulnResponse.ok) {
|
|
53
|
+
const vulnData = await vulnResponse.json();
|
|
54
|
+
fullVulns.push(vulnData);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
fullVulns.push({ id: bv.id, summary: "Details unavailable" });
|
|
59
|
+
}
|
|
56
60
|
}
|
|
61
|
+
results.set(key, fullVulns);
|
|
57
62
|
}
|
|
58
|
-
results.set(key, fullVulns);
|
|
59
63
|
}
|
|
60
64
|
return results;
|
|
61
65
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.53",
|
|
4
4
|
"mcpName": "io.github.goklab/guardvibe",
|
|
5
5
|
"description": "Security MCP for vibe coding. 365 rules, 36 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis. 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",
|