fix(audit): reject malformed rolling artifact tags

Harden phase0 rolling retention timestamp parsing with explicit bounds and UTC round-trip validation; add regression coverage for invalid date/time tags. No architecture/protocol flow changes; diagram files reviewed and no updates were needed.
This commit is contained in:
William Valentin
2026-02-27 13:04:27 -08:00
parent 49a5a44c8a
commit ad395bbdd6
3 changed files with 47 additions and 1 deletions
@@ -97,4 +97,19 @@ describe('phase0BaselineArtifactRetention', () => {
it('rejects negative keep limit', () => {
expect(() => planRollingPhase0ArtifactRetention([], -1)).toThrow('keepPerFamily');
});
it('ignores malformed rolling tags with impossible date or time values', () => {
const rows = collectRollingPhase0ArtifactFiles([
'phase0_baseline_live_2026-13-27-010203.json',
'phase0_baseline_live_gateway_2026-02-30-010203.json',
'phase0_baseline_live_backend_pi_embedded_2026-02-27-246001.json',
'phase0_baseline_live_backend_native_2026-02-27-016061.json',
'phase0_baseline_live_backend_drift_2026-02-27-010203.md',
'phase0_baseline_live_prune_2026-00-27-010203.json',
]);
expect(rows).toHaveLength(1);
expect(rows[0]?.file_name).toBe('phase0_baseline_live_backend_drift_2026-02-27-010203.md');
expect(rows[0]?.family).toBe('backend_drift');
});
});
+20 -1
View File
@@ -74,9 +74,28 @@ function parseRollingTagTimestampMs(tag: string): number | undefined {
if (!Number.isFinite(hour) || !Number.isFinite(minute) || !Number.isFinite(second)) {
return undefined;
}
if (month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
return undefined;
}
const timestampMs = Date.UTC(year, month - 1, day, hour, minute, second);
return Number.isFinite(timestampMs) ? timestampMs : undefined;
if (!Number.isFinite(timestampMs)) {
return undefined;
}
const utc = new Date(timestampMs);
if (
utc.getUTCFullYear() !== year
|| utc.getUTCMonth() !== month - 1
|| utc.getUTCDate() !== day
|| utc.getUTCHours() !== hour
|| utc.getUTCMinutes() !== minute
|| utc.getUTCSeconds() !== second
) {
return undefined;
}
return timestampMs;
}
function parseRollingArtifactFile(fileName: string): Phase0RollingArtifactFile | undefined {