Skip to content

Commit 01d043e

Browse files
authored
fix(lockfile-drift): chunk OSV batch queries to prevent timeouts (#2713)
Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
1 parent 459df50 commit 01d043e

1 file changed

Lines changed: 37 additions & 31 deletions

File tree

review-enrichment/src/analyzers/lockfile-drift.ts

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ interface OsvVuln {
5252
const MAX_LOCKFILE_FILES = 12;
5353
const MAX_PATCH_LINES_PER_FILE = 1200;
5454
const MAX_OSV_QUERIES = 40;
55+
const LOCKFILE_OSV_BATCH_CHUNK_SIZE = 10;
5556
const VERSION_SAFE_RE = /^[0-9][0-9A-Za-z._+-]*$/;
5657
const MAX_PACKAGE_LEN = 200;
5758
const MAX_VERSION_LEN = 100;
@@ -377,38 +378,43 @@ export async function queryOsvBatch(
377378
): Promise<Map<string, Cve[]>> {
378379
const results = new Map<string, Cve[]>();
379380
if (!changes.length || signal?.aborted) return results;
380-
const fetchOptions = {
381-
endpointCategory: "osv-querybatch",
382-
method: "POST",
383-
headers: { "content-type": "application/json" },
384-
body: JSON.stringify({
385-
queries: changes.map((change) => ({
386-
package: { name: change.package, ecosystem: change.ecosystem },
387-
version: change.to,
388-
})),
389-
}),
390-
signal,
391-
fetchImpl,
392-
diagnostics: options.diagnostics,
393-
phase: "lockfile-drift",
394-
subcall: "osv-querybatch",
395-
maxBytes: 1024 * 1024,
396-
maxCallsPerCategory: 1,
397-
};
398-
const response = options.analysis
399-
? await options.analysis.fetchJson<{
381+
const maxBatchCalls = Math.ceil(changes.length / LOCKFILE_OSV_BATCH_CHUNK_SIZE);
382+
for (let i = 0; i < changes.length; i += LOCKFILE_OSV_BATCH_CHUNK_SIZE) {
383+
if (signal?.aborted) break;
384+
const chunk = changes.slice(i, i + LOCKFILE_OSV_BATCH_CHUNK_SIZE);
385+
const fetchOptions = {
386+
endpointCategory: "osv-querybatch",
387+
method: "POST",
388+
headers: { "content-type": "application/json" },
389+
body: JSON.stringify({
390+
queries: chunk.map((change) => ({
391+
package: { name: change.package, ecosystem: change.ecosystem },
392+
version: change.to,
393+
})),
394+
}),
395+
signal,
396+
fetchImpl,
397+
diagnostics: options.diagnostics,
398+
phase: "lockfile-drift",
399+
subcall: "osv-querybatch",
400+
maxBytes: 1024 * 1024,
401+
maxCallsPerCategory: maxBatchCalls,
402+
};
403+
const response = options.analysis
404+
? await options.analysis.fetchJson<{
405+
results?: Array<{ vulns?: OsvVuln[] }>;
406+
}>("https://api.osv.dev/v1/querybatch", fetchOptions)
407+
: await boundedFetchJson<{
400408
results?: Array<{ vulns?: OsvVuln[] }>;
401-
}>("https://api.osv.dev/v1/querybatch", fetchOptions)
402-
: await boundedFetchJson<{
403-
results?: Array<{ vulns?: OsvVuln[] }>;
404-
}>("https://api.osv.dev/v1/querybatch", fetchOptions);
405-
if (!response.ok) return results;
406-
changes.forEach((change, index) => {
407-
results.set(
408-
`${change.ecosystem}::${change.package}@${change.to}`,
409-
toCves(response.data.results?.[index]?.vulns),
410-
);
411-
});
409+
}>("https://api.osv.dev/v1/querybatch", fetchOptions);
410+
if (!response.ok) continue;
411+
chunk.forEach((change, index) => {
412+
results.set(
413+
`${change.ecosystem}::${change.package}@${change.to}`,
414+
toCves(response.data.results?.[index]?.vulns),
415+
);
416+
});
417+
}
412418
return results;
413419
}
414420

0 commit comments

Comments
 (0)