mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 22:58:42 -10:00
fix(policy): Seam A require description + all four config action-lists (guard finding)
Guard SEAM A NOT-CLEAR: validatePolicySpec returned ok:true for a spec with NO description and for a config-surface spec omitting all four action-list fields — an advisory false-green for incomplete custom specs, contrary to the schema (description required; config requires default_posture + allow/ask/deny/destructive_class all present). Minimal correction (RED-first): require description as a non-empty string; require all four config action-list fields present as string arrays (use [] for none). 3 new RED pins proven (RED before fix); all four authoritative built-ins still validate (each carries description + its required fields); parse/ body/serialization/A-only scope/fences unchanged. 14/14 GREEN; daemon tsc 0. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
b240b8cf45
commit
4694e86d9d
2 files changed
+20
-1
No files matched your search
@@ -71,6 +71,7 @@ export function validatePolicySpec(fm: Record<string, unknown>): ValidationResul
|
||||
if (fm["policy_schema_version"] !== 1) errors.push("policy_schema_version must be the number 1");
|
||||
if (typeof fm["name"] !== "string" || (fm["name"] as string).length === 0) errors.push("name must be a non-empty string");
|
||||
if (typeof fm["source"] !== "string" || !SOURCES.has(fm["source"] as string)) errors.push("source must be 'builtin' or 'custom'");
|
||||
if (typeof fm["description"] !== "string" || (fm["description"] as string).length === 0) errors.push("description must be a non-empty string");
|
||||
|
||||
const surface = fm["surface"];
|
||||
if (typeof surface !== "string" || !SURFACES.has(surface)) {
|
||||
@@ -86,8 +87,10 @@ export function validatePolicySpec(fm: Record<string, unknown>): ValidationResul
|
||||
// config
|
||||
if (fm["launch_posture"] !== undefined) errors.push("config-surface policy must NOT carry launch_posture (flag-surface field)");
|
||||
if (!DEFAULT_POSTURES.has(fm["default_posture"] as string)) errors.push("config-surface policy requires default_posture ('allow', 'ask' or 'deny')");
|
||||
// The schema requires all four action-list fields present (use [] for none) — not optional.
|
||||
for (const f of ACTION_LIST_FIELDS) {
|
||||
if (fm[f] !== undefined && !isStringList(fm[f])) errors.push(`${f} must be a list of strings`);
|
||||
if (fm[f] === undefined) errors.push(`config-surface policy requires '${f}' (a list of strings; use [] for none)`);
|
||||
else if (!isStringList(fm[f])) errors.push(`${f} must be a list of strings`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,22 @@ describe("validatePolicySpec — advisory, fail-open, surface-appropriate", () =
|
||||
expect(validatePolicySpec({ source: "builtin", name: "x", surface: "sideways", policy_schema_version: 1 }).ok).toBe(false);
|
||||
expect(validatePolicySpec({ source: "builtin", name: "x", surface: "config", default_posture: "maybe", policy_schema_version: 1 }).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("missing description → error (required field)", () => {
|
||||
const r = validatePolicySpec({ source: "builtin", name: "x", surface: "flag", launch_posture: "floor", policy_schema_version: 1 });
|
||||
expect(r.ok).toBe(false);
|
||||
expect(r.errors.some((e) => e.includes("description"))).toBe(true);
|
||||
});
|
||||
it("non-string description → error", () => {
|
||||
const r = validatePolicySpec({ source: "builtin", name: "x", surface: "flag", launch_posture: "floor", policy_schema_version: 1, description: 42 });
|
||||
expect(r.ok).toBe(false);
|
||||
expect(r.errors.some((e) => e.includes("description"))).toBe(true);
|
||||
});
|
||||
it("config policy MISSING an action list (destructive_class) → error (all four required)", () => {
|
||||
const r = validatePolicySpec({ source: "builtin", name: "x", surface: "config", description: "d", default_posture: "allow", policy_schema_version: 1, allow: [], ask: [], deny: [] });
|
||||
expect(r.ok).toBe(false);
|
||||
expect(r.errors.some((e) => e.includes("destructive_class"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("round-trip — canonical serialize stability + semantic equality + body preservation", () => {
|
||||
|
||||
Reference in new issue
Block a user