diff --git a/docs/library-decisions/_template.md b/docs/library-decisions/_template.md index 1f16bb0..f25d9a7 100644 --- a/docs/library-decisions/_template.md +++ b/docs/library-decisions/_template.md @@ -6,6 +6,7 @@ decision: approved | rejected date: deciders: [, ...] adr: adr-NNN | null +lastRevalidated: null filter-results: license: types: native | "@types/" | none @@ -15,6 +16,7 @@ filter-results: eu-residency: ok | n/a | self-hostable | fail cve-scan: clean | "" | fail named-consumer: pass | fail + socketRisk: clean | flagged | verification-commands: - accepted-cves: [] @@ -84,6 +86,26 @@ Answer: "Who calls this code path today, or who is blocked waiting for it?" Hypothetical future callers are not consumers. This filter is the direct response to the 2026-05-14 OpenAPI near-miss (ADR-022 §Context). +## Filter: socketRisk + + + +Run `socket npm:report ` (or check socket.dev) for supply-chain +risk signals. `clean` = no issues detected. `flagged` = one or more high- or +critical-severity signals (requires explicit risk-acceptance note here before +approval). An arbitrary string records the specific risk label returned by the +Socket CLI (e.g. `"obfuscated-code"`, `"install-script"`). The `lastRevalidated` +frontmatter field is set to the ISO date of the most recent re-run. + +## Field: lastRevalidated + + + +ISO 8601 date of the last time the Socket supply-chain scan (and any other +time-sensitive filter) was re-run against the current installed version. +`null` = never revalidated since initial adoption (acceptable for fresh traces). +Updated automatically by the weekly revalidation cron (Story 05). + ## Prompt: replaces diff --git a/scripts/library-decisions/schema.mjs b/scripts/library-decisions/schema.mjs index 5b7f5a6..eb37ad7 100644 --- a/scripts/library-decisions/schema.mjs +++ b/scripts/library-decisions/schema.mjs @@ -17,6 +17,7 @@ const filterResultsSchema = z "eu-residency": z.enum(["ok", "n/a", "self-hostable", "fail"]), "cve-scan": z.string().min(1), "named-consumer": z.enum(["pass", "fail"]), + socketRisk: z.union([z.literal("clean"), z.literal("flagged"), z.string()]), }) .strict(); @@ -29,6 +30,7 @@ export const traceSchema = z date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD"), deciders: z.array(z.string()), adr: z.string().nullable(), + lastRevalidated: z.string().nullable(), "filter-results": filterResultsSchema, "verification-commands": z.array(z.string()), "accepted-cves": z.array(z.string()).optional(), diff --git a/scripts/library-decisions/schema.test.mjs b/scripts/library-decisions/schema.test.mjs index 8dc05e8..2b285e3 100644 --- a/scripts/library-decisions/schema.test.mjs +++ b/scripts/library-decisions/schema.test.mjs @@ -14,6 +14,7 @@ function validRaw(overrides = {}) { date: "2026-05-14", deciders: ["alice"], adr: null, + lastRevalidated: null, "filter-results": { license: "MIT", types: "native", @@ -23,6 +24,7 @@ function validRaw(overrides = {}) { "eu-residency": "ok", "cve-scan": "clean", "named-consumer": "pass", + socketRisk: "clean", }, "verification-commands": ["pnpm audit --audit-level=moderate"], ...overrides, @@ -43,6 +45,7 @@ decision: approved date: 2026-05-14 deciders: [alice, bob] adr: null +lastRevalidated: null filter-results: license: MIT types: native @@ -52,6 +55,7 @@ filter-results: eu-residency: ok cve-scan: clean named-consumer: pass + socketRisk: clean verification-commands: - pnpm audit --audit-level=moderate`; @@ -125,6 +129,59 @@ describe("validateTrace > rejection cases", () => { /unrecognized_keys|Unrecognized key/i, ); }); + + test("missing socketRisk in filter-results fails validation", () => { + const raw = validRaw(); + delete raw["filter-results"].socketRisk; + assert.throws(() => validateTrace(raw), /invalid_type|Required/i); + }); +}); + +describe("validateTrace > socketRisk", () => { + test('socketRisk "clean" round-trips', () => { + const result = validateTrace(validRaw()); + assert.equal(result["filter-results"].socketRisk, "clean"); + }); + + test('socketRisk "flagged" round-trips', () => { + const raw = validRaw({ + "filter-results": { + ...validRaw()["filter-results"], + socketRisk: "flagged", + }, + }); + assert.equal(validateTrace(raw)["filter-results"].socketRisk, "flagged"); + }); + + test("socketRisk arbitrary string round-trips", () => { + const raw = validRaw({ + "filter-results": { + ...validRaw()["filter-results"], + socketRisk: "obfuscated-code", + }, + }); + assert.equal( + validateTrace(raw)["filter-results"].socketRisk, + "obfuscated-code", + ); + }); +}); + +describe("validateTrace > lastRevalidated", () => { + test("lastRevalidated null is valid", () => { + assert.equal( + validateTrace(validRaw({ lastRevalidated: null })).lastRevalidated, + null, + ); + }); + + test("lastRevalidated ISO date string is valid", () => { + assert.equal( + validateTrace(validRaw({ lastRevalidated: "2026-05-14" })) + .lastRevalidated, + "2026-05-14", + ); + }); }); describe("parseFrontmatter", () => {