fix(audit): require integer gateway window padding
Enforce non-negative integer padding_ms/--window-padding-ms across gateway window helper and live baseline capture CLI, with regression coverage for invalid values. Architecture/protocol diagrams reviewed; no updates were needed for this validation-only change.
This commit is contained in:
@@ -520,6 +520,19 @@
|
||||
],
|
||||
"test_status": "pnpm test:run src/audit/phase0BaselineDrift.test.ts + pnpm typecheck passing"
|
||||
},
|
||||
"phase0-live-baseline-gateway-window-padding-integer-validation": {
|
||||
"status": "completed",
|
||||
"date": "2026-02-27",
|
||||
"updated": "2026-02-27",
|
||||
"summary": "Hardened gateway cancel-window auto-detection padding by requiring non-negative integer `padding_ms`/`--window-padding-ms` values in both helper and capture CLI, preventing silent flooring of fractional millisecond values.",
|
||||
"files_modified": [
|
||||
"src/audit/phase0GatewayWindow.ts",
|
||||
"src/audit/phase0GatewayWindow.test.ts",
|
||||
"scripts/capture-phase0-live-baseline.ts",
|
||||
"docs/plans/state.json"
|
||||
],
|
||||
"test_status": "pnpm test:run src/audit/phase0GatewayWindow.test.ts + pnpm typecheck passing"
|
||||
},
|
||||
"phase0-instrumentation-ticket-checklist": {
|
||||
"status": "completed",
|
||||
"date": "2026-02-25",
|
||||
|
||||
@@ -39,7 +39,7 @@ function usage(): string {
|
||||
' --backend <native|pi_embedded|...[,..]> Restrict sample to selected backends (via backend.route timeline)',
|
||||
' --exclude-session-substring <text[,..]> Exclude sessions containing any substring (default: probe)',
|
||||
' --auto-gateway-cancel-window Auto-select latest gateway cancel/cancelled session window',
|
||||
' --window-padding-ms <number> Milliseconds added before/after auto-selected window (default: 250)',
|
||||
' --window-padding-ms <integer> Milliseconds added before/after auto-selected window (default: 250)',
|
||||
' --raw-identifiers Keep raw session/sender/request IDs (default: anonymized)',
|
||||
' --tag <YYYY-MM-DD> Output file tag (default: current date UTC)',
|
||||
' --sample-out <path> Output JSONL sample path override',
|
||||
@@ -202,10 +202,7 @@ async function main(): Promise<void> {
|
||||
const backendTargets = parseBackendTargets(values.backend);
|
||||
const excludeSessionSubstrings = parseCsv(values['exclude-session-substring']) ?? ['probe'];
|
||||
const autoGatewayCancelWindow = Boolean(values['auto-gateway-cancel-window']);
|
||||
const windowPaddingMs = parseOptionalNumber(values['window-padding-ms'], '--window-padding-ms');
|
||||
if (windowPaddingMs !== undefined && windowPaddingMs < 0) {
|
||||
throw new Error('--window-padding-ms must be greater than or equal to 0.');
|
||||
}
|
||||
const windowPaddingMs = parseOptionalInteger(values['window-padding-ms'], '--window-padding-ms');
|
||||
|
||||
if (autoGatewayCancelWindow && (values.since || values.until)) {
|
||||
throw new Error('--auto-gateway-cancel-window cannot be combined with --since/--until.');
|
||||
|
||||
@@ -71,4 +71,14 @@ describe('findLatestGatewayCancelWindow', () => {
|
||||
|
||||
expect(findLatestGatewayCancelWindow(events)).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects invalid padding values', () => {
|
||||
const events: AuditEvent[] = [
|
||||
event(1000, 'run.cancel', { session_id: 's1', source: 'gateway' }),
|
||||
event(1010, 'run.state', { session_id: 's1', source: 'gateway', state: 'cancelled' }),
|
||||
];
|
||||
|
||||
expect(() => findLatestGatewayCancelWindow(events, { padding_ms: -1 })).toThrow('padding_ms');
|
||||
expect(() => findLatestGatewayCancelWindow(events, { padding_ms: 1.5 })).toThrow('padding_ms');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,22 @@ function isGatewayEvent(payload: Record<string, unknown>): boolean {
|
||||
return readString(payload.source) === 'gateway';
|
||||
}
|
||||
|
||||
function normalizePaddingMs(value: number | undefined): number {
|
||||
if (value === undefined) {
|
||||
return 0;
|
||||
}
|
||||
if (!Number.isFinite(value)) {
|
||||
throw new Error('padding_ms must be a finite integer greater than or equal to 0.');
|
||||
}
|
||||
if (!Number.isInteger(value)) {
|
||||
throw new Error('padding_ms must be an integer greater than or equal to 0.');
|
||||
}
|
||||
if (value < 0) {
|
||||
throw new Error('padding_ms must be greater than or equal to 0.');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function findLatestGatewayCancelWindow(
|
||||
events: AuditEvent[],
|
||||
options: FindGatewayCancelWindowOptions = {},
|
||||
@@ -104,10 +120,7 @@ export function findLatestGatewayCancelWindow(
|
||||
return null;
|
||||
}
|
||||
|
||||
const padRaw = options.padding_ms ?? 0;
|
||||
const paddingMs = Number.isFinite(padRaw) && padRaw > 0
|
||||
? Math.floor(padRaw)
|
||||
: 0;
|
||||
const paddingMs = normalizePaddingMs(options.padding_ms);
|
||||
|
||||
return {
|
||||
session_id: latest.session_id,
|
||||
|
||||
Reference in New Issue
Block a user