large image scans no longer fail when the scanner log rotates

This commit is contained in:
Jarek Krochmalski committed 2026-09-02 15:52:44 +02:00
1 parent 9a3c52374c
commit ae754b3140
3 files changed
+89 -5

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
/**
* Pure, side-effect-free helpers for handling scanner (Trivy/Grype) stdout. Kept out of
* scanner.ts so they can be unit-tested without pulling in docker/db (which break bun test).
*/
/**
* Bound a large scanner output for logging: keep a head and tail (default 2 KB each) with a
* marker for the elided middle, so a failed parse of a multi-MB report never floods stderr /
* the log shipper (#1496). Small outputs pass through unchanged.
*/
export function truncateForLog(output: string, keep = 2048): string {
if (output.length <= keep * 2) return output;
const elided = output.length - keep * 2;
return `${output.slice(0, keep)}\n...[${elided} bytes elided]...\n${output.slice(-keep)}`;
}
/**
* Classify a scanner output that failed to parse as JSON, to surface a useful error.
* - 'truncated': a large buffer that does NOT start with `{` is a mid-document fragment - the
* head of the JSON was lost to container-log rotation (#1496).
* - 'cli-error': a short non-JSON output is a genuine error message printed by the scanner CLI.
* - 'unknown': anything else (e.g. starts with `{` but is still unparseable).
*/
export function classifyUnparseableOutput(output: string): 'truncated' | 'cli-error' | 'unknown' {
const trimmed = output.trimStart();
if (output.length > 100_000 && !trimmed.startsWith('{')) return 'truncated';
const firstLine = trimmed.split('\n', 1)[0].trim();
if (firstLine && !firstLine.startsWith('{')) return 'cli-error';
return 'unknown';
}
+12 -5
View File
@@ -17,6 +17,7 @@ import {
import { getEnvironment, getEnvSetting, getSetting } from './db';
import { sendEventNotification } from './notifications';
import { detectRemoteSocketPath } from './scanner-socket-detect';
import { truncateForLog, classifyUnparseableOutput } from './scanner-output-core';
import {
getHostDockerSocket,
getHostDataDir,
@@ -546,11 +547,17 @@ function parseTrivyOutput(output: string): { vulnerabilities: Vulnerability[]; s
console.error('[Trivy] Failed to parse output:', errorMsg);
console.error('[Trivy] Output length:', output.length);
console.error('[Trivy] First 32 bytes (hex):', Buffer.from(output.slice(0, 32)).toString('hex'));
console.error('[Trivy] Full output:', output);
// Check if output looks like an error message from trivy
const firstLine = output.split('\n')[0].trim();
if (firstLine && !firstLine.startsWith('{')) {
throw new Error(`Scanner output error: ${firstLine}`);
// Never dump the whole report: a large truncated report is ~13 MB and this ran
// on every failed scan (#1496). Log only a bounded head + tail for diagnosis.
console.error('[Trivy] Output (head/tail):', truncateForLog(output));
const kind = classifyUnparseableOutput(output);
if (kind === 'truncated') {
// The head of the JSON was lost to container-log rotation the helper now disables (#1496).
throw new Error('Scanner output truncated (container log rotation?) - the JSON report was too large to read back in full');
}
if (kind === 'cli-error') {
throw new Error(`Scanner output error: ${output.trimStart().split('\n', 1)[0].trim()}`);
}
throw new Error('Failed to parse scanner output - ensure CLI args include "--format json"');
}