fix(audit): require integer phase0 summary row limits

Require non-negative integer maxSessions/maxChannels/maxSkipReasons in summary core and both phase0 summary/capture CLIs to prevent silent flooring of fractional values. Architecture/protocol diagrams reviewed; no flow or API shape changes required.
This commit is contained in:
William Valentin
2026-02-27 13:14:24 -08:00
parent 06998ac65d
commit a4794ddea8
5 changed files with 68 additions and 15 deletions
+20 -6
View File
@@ -45,9 +45,9 @@ function usage(): string {
' --sample-out <path> Output JSONL sample path override',
' --summary-json-out <path> Output summary JSON path override',
' --summary-md-out <path> Output summary markdown path override',
' --max-sessions <number> Limit session rows in output (default: 20)',
' --max-channels <number> Limit channel rows in output (default: 20)',
' --max-skip-reasons <number> Limit skip reason rows in output (default: 10)',
' --max-sessions <integer> Limit session rows in output (default: 20)',
' --max-channels <integer> Limit channel rows in output (default: 20)',
' --max-skip-reasons <integer> Limit skip reason rows in output (default: 10)',
].join('\n');
}
@@ -125,6 +125,20 @@ function parseOptionalNumber(raw: string | undefined, flag: string): number | un
return parsed;
}
function parseOptionalInteger(raw: string | undefined, flag: string): number | undefined {
const parsed = parseOptionalNumber(raw, flag);
if (parsed === undefined) {
return undefined;
}
if (!Number.isInteger(parsed)) {
throw new Error(`Invalid ${flag} value "${raw}". Expected an integer.`);
}
if (parsed < 0) {
throw new Error(`${flag} must be greater than or equal to 0.`);
}
return parsed;
}
function parseBackendTargets(raw: string | undefined): Phase0BackendTarget[] | undefined {
const values = parseCsv(raw);
if (!values) {
@@ -237,9 +251,9 @@ async function main(): Promise<void> {
const summaryOptions: Phase0BaselineSummaryOptions = {
channels,
sources,
maxSessions: parseOptionalNumber(values['max-sessions'], '--max-sessions') ?? 20,
maxChannels: parseOptionalNumber(values['max-channels'], '--max-channels') ?? 20,
maxSkipReasons: parseOptionalNumber(values['max-skip-reasons'], '--max-skip-reasons') ?? 10,
maxSessions: parseOptionalInteger(values['max-sessions'], '--max-sessions') ?? 20,
maxChannels: parseOptionalInteger(values['max-channels'], '--max-channels') ?? 20,
maxSkipReasons: parseOptionalInteger(values['max-skip-reasons'], '--max-skip-reasons') ?? 10,
};
const backendRouteEvents = backendTargets && backendTargets.length > 0