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
+12
View File
@@ -439,6 +439,18 @@
],
"test_status": "pnpm test:run src/audit/phase0BaselineScriptWiring.test.ts + pnpm typecheck passing"
},
"phase0-live-baseline-rolling-tag-validation-hardening": {
"status": "completed",
"date": "2026-02-27",
"updated": "2026-02-27",
"summary": "Hardened rolling artifact retention tag parsing to reject impossible timestamp components (month/day/time bounds and invalid calendar dates) so malformed filenames cannot be misclassified through date normalization.",
"files_modified": [
"src/audit/phase0BaselineArtifactRetention.ts",
"src/audit/phase0BaselineArtifactRetention.test.ts",
"docs/plans/state.json"
],
"test_status": "pnpm test:run src/audit/phase0BaselineArtifactRetention.test.ts + pnpm typecheck passing"
},
"phase0-instrumentation-ticket-checklist": {
"status": "completed",
"date": "2026-02-25",
@@ -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 {