feat(scripts): extend trace schema with socketRisk and lastRevalidated
Add socketRisk (9th filter result) and lastRevalidated (nullable ISO date) to the library-decision trace schema. Downstream enforcement layers (evaluate-library skill, check.mjs major-bump mode, revalidate.mjs cron) all depend on these fields being validated at the schema layer first. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ decision: approved | rejected
|
|||||||
date: <YYYY-MM-DD>
|
date: <YYYY-MM-DD>
|
||||||
deciders: [<author>, ...]
|
deciders: [<author>, ...]
|
||||||
adr: adr-NNN | null
|
adr: adr-NNN | null
|
||||||
|
lastRevalidated: null
|
||||||
filter-results:
|
filter-results:
|
||||||
license: <SPDX id>
|
license: <SPDX id>
|
||||||
types: native | "@types/<x>" | none
|
types: native | "@types/<x>" | none
|
||||||
@@ -15,6 +16,7 @@ filter-results:
|
|||||||
eu-residency: ok | n/a | self-hostable | fail
|
eu-residency: ok | n/a | self-hostable | fail
|
||||||
cve-scan: clean | "<advisory-id>" | fail
|
cve-scan: clean | "<advisory-id>" | fail
|
||||||
named-consumer: pass | fail
|
named-consumer: pass | fail
|
||||||
|
socketRisk: clean | flagged | <arbitrary-string>
|
||||||
verification-commands:
|
verification-commands:
|
||||||
- <literal command that produced each filter result>
|
- <literal command that produced each filter result>
|
||||||
accepted-cves: []
|
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
|
Hypothetical future callers are not consumers. This filter is the direct
|
||||||
response to the 2026-05-14 OpenAPI near-miss (ADR-022 §Context).
|
response to the 2026-05-14 OpenAPI near-miss (ADR-022 §Context).
|
||||||
|
|
||||||
|
## Filter: socketRisk
|
||||||
|
|
||||||
|
<!-- Result: clean | flagged | <arbitrary-string> -->
|
||||||
|
|
||||||
|
Run `socket npm:report <package>` (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
|
||||||
|
|
||||||
|
<!-- Value: YYYY-MM-DD | null -->
|
||||||
|
|
||||||
|
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
|
## Prompt: replaces
|
||||||
|
|
||||||
<!-- Required: answer in either direction with justification -->
|
<!-- Required: answer in either direction with justification -->
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const filterResultsSchema = z
|
|||||||
"eu-residency": z.enum(["ok", "n/a", "self-hostable", "fail"]),
|
"eu-residency": z.enum(["ok", "n/a", "self-hostable", "fail"]),
|
||||||
"cve-scan": z.string().min(1),
|
"cve-scan": z.string().min(1),
|
||||||
"named-consumer": z.enum(["pass", "fail"]),
|
"named-consumer": z.enum(["pass", "fail"]),
|
||||||
|
socketRisk: z.union([z.literal("clean"), z.literal("flagged"), z.string()]),
|
||||||
})
|
})
|
||||||
.strict();
|
.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"),
|
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD"),
|
||||||
deciders: z.array(z.string()),
|
deciders: z.array(z.string()),
|
||||||
adr: z.string().nullable(),
|
adr: z.string().nullable(),
|
||||||
|
lastRevalidated: z.string().nullable(),
|
||||||
"filter-results": filterResultsSchema,
|
"filter-results": filterResultsSchema,
|
||||||
"verification-commands": z.array(z.string()),
|
"verification-commands": z.array(z.string()),
|
||||||
"accepted-cves": z.array(z.string()).optional(),
|
"accepted-cves": z.array(z.string()).optional(),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ function validRaw(overrides = {}) {
|
|||||||
date: "2026-05-14",
|
date: "2026-05-14",
|
||||||
deciders: ["alice"],
|
deciders: ["alice"],
|
||||||
adr: null,
|
adr: null,
|
||||||
|
lastRevalidated: null,
|
||||||
"filter-results": {
|
"filter-results": {
|
||||||
license: "MIT",
|
license: "MIT",
|
||||||
types: "native",
|
types: "native",
|
||||||
@@ -23,6 +24,7 @@ function validRaw(overrides = {}) {
|
|||||||
"eu-residency": "ok",
|
"eu-residency": "ok",
|
||||||
"cve-scan": "clean",
|
"cve-scan": "clean",
|
||||||
"named-consumer": "pass",
|
"named-consumer": "pass",
|
||||||
|
socketRisk: "clean",
|
||||||
},
|
},
|
||||||
"verification-commands": ["pnpm audit --audit-level=moderate"],
|
"verification-commands": ["pnpm audit --audit-level=moderate"],
|
||||||
...overrides,
|
...overrides,
|
||||||
@@ -43,6 +45,7 @@ decision: approved
|
|||||||
date: 2026-05-14
|
date: 2026-05-14
|
||||||
deciders: [alice, bob]
|
deciders: [alice, bob]
|
||||||
adr: null
|
adr: null
|
||||||
|
lastRevalidated: null
|
||||||
filter-results:
|
filter-results:
|
||||||
license: MIT
|
license: MIT
|
||||||
types: native
|
types: native
|
||||||
@@ -52,6 +55,7 @@ filter-results:
|
|||||||
eu-residency: ok
|
eu-residency: ok
|
||||||
cve-scan: clean
|
cve-scan: clean
|
||||||
named-consumer: pass
|
named-consumer: pass
|
||||||
|
socketRisk: clean
|
||||||
verification-commands:
|
verification-commands:
|
||||||
- pnpm audit --audit-level=moderate`;
|
- pnpm audit --audit-level=moderate`;
|
||||||
|
|
||||||
@@ -125,6 +129,59 @@ describe("validateTrace > rejection cases", () => {
|
|||||||
/unrecognized_keys|Unrecognized key/i,
|
/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", () => {
|
describe("parseFrontmatter", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user