fix(16): CR-01 enforce audit-waiver expiry; IN-01 realpath isMain

Add isWaived() predicate: a waiver with a past 'expires' date is treated
as absent so the High/Critical advisory re-blocks. Applied in both
selectBlocking and partitionAdvisories. Add expired-waiver unit tests.
isMain now compares fully-resolved real paths (mirrors index.ts).
This commit is contained in:
Lucas Berger
2026-06-13 08:42:08 -04:00
parent 5dd84a2861
commit 4bb205fe0f
2 changed files with 78 additions and 11 deletions
+39 -10
View File
@@ -15,34 +15,53 @@
*/
import { execSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { readFileSync, realpathSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { resolve, dirname } from 'node:path';
const BLOCKING_SEVERITIES = new Set(['high', 'critical']);
/**
* Decides whether an advisory is currently waived by the allowlist.
*
* A waiver entry suppresses the advisory ONLY while it is in force: an entry
* with no `expires` field, or with an `expires` date strictly in the future,
* waives the advisory. An entry whose `expires` date is in the past (≤ now) is
* treated as absent — the advisory re-blocks. This makes the time-boxed waiver
* actually time-boxed (CR-01).
*
* @param {{severity: string, github_advisory_id: string}} adv
* @param {Record<string, {reason: string, reviewer: string, expires?: string}>} allowlist
* @returns {boolean}
*/
export function isWaived(adv, allowlist) {
const w = allowlist[adv.github_advisory_id];
if (!w) return false;
// No expiry or future expiry → waived; past (or equal) expiry → NOT waived (re-blocks).
if (w.expires && Date.parse(w.expires) <= Date.now()) return false;
return true;
}
/**
* From an advisories map (keyed by numeric id), returns the subset that
* are High or Critical AND whose github_advisory_id is NOT present in allowlist.
* are High or Critical AND whose github_advisory_id is NOT currently waived.
*
* @param {Record<string, {severity: string, github_advisory_id: string, module_name: string, title: string}>} advisories
* @param {Record<string, {reason: string, reviewer: string, expires: string}>} allowlist
* @param {Record<string, {reason: string, reviewer: string, expires?: string}>} allowlist
* @returns {Array<{severity: string, github_advisory_id: string, module_name: string, title: string}>}
*/
export function selectBlocking(advisories, allowlist) {
return Object.values(advisories).filter(
(adv) =>
BLOCKING_SEVERITIES.has(adv.severity) &&
!allowlist[adv.github_advisory_id],
(adv) => BLOCKING_SEVERITIES.has(adv.severity) && !isWaived(adv, allowlist),
);
}
/**
* Partitions all advisories into blocking (unwaived High/Critical) and
* advisory-only (moderate/low, or waived High/Critical).
* advisory-only (moderate/low, or currently-waived High/Critical).
*
* @param {Record<string, {severity: string, github_advisory_id: string, module_name: string, title: string}>} advisories
* @param {Record<string, {reason: string, reviewer: string, expires: string}>} allowlist
* @param {Record<string, {reason: string, reviewer: string, expires?: string}>} allowlist
* @returns {{ blocking: Array, advisory: Array }}
*/
export function partitionAdvisories(advisories, allowlist) {
@@ -50,7 +69,7 @@ export function partitionAdvisories(advisories, allowlist) {
const advisory = [];
for (const adv of Object.values(advisories)) {
if (BLOCKING_SEVERITIES.has(adv.severity) && !allowlist[adv.github_advisory_id]) {
if (BLOCKING_SEVERITIES.has(adv.severity) && !isWaived(adv, allowlist)) {
blocking.push(adv);
} else {
advisory.push(adv);
@@ -61,8 +80,18 @@ export function partitionAdvisories(advisories, allowlist) {
}
// Main body — only runs when invoked directly (not when imported as a module).
// IN-01: compare fully-resolved real paths (mirrors index.ts isMainModule) so a
// symlinked or non-canonical entrypoint does not silently skip the gate.
const __filename = fileURLToPath(import.meta.url);
const isMain = process.argv[1] === __filename;
function isMainModule() {
if (!process.argv[1]) return false;
try {
return __filename === realpathSync(process.argv[1]);
} catch {
return false;
}
}
const isMain = isMainModule();
if (isMain) {
const __dirname = dirname(__filename);