feat(service): PlatformGates seam + fail-closed decideOrThrow (P1.3)
LocalDevPorts = permissive in-service default (no testkit at runtime); decideOrThrow turns OPA deny/throw/timeout into PolicyDeniedError. Tests drive deny/timeout via testkit fakes. 12 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { IiosPlatformPorts, PolicyDecision, PolicyDeniedError } from '@insignia/iios-contracts';
|
||||
|
||||
/**
|
||||
* Fail-closed authorization (Bottom-Up hard gate; Critics KG-03).
|
||||
*
|
||||
* Calls the OPA port. The kernel proceeds ONLY on an explicit allow. A denial,
|
||||
* a thrown error, or a timeout all become a PolicyDeniedError — never an
|
||||
* implicit allow. Callers must let this propagate so nothing is written.
|
||||
*/
|
||||
export async function decideOrThrow(ports: IiosPlatformPorts, input: unknown): Promise<PolicyDecision> {
|
||||
let decision: PolicyDecision;
|
||||
try {
|
||||
decision = await ports.opa.decide(input);
|
||||
} catch (err) {
|
||||
throw new PolicyDeniedError(`policy port unavailable: ${(err as Error).message}`);
|
||||
}
|
||||
if (!decision.allow) {
|
||||
throw new PolicyDeniedError(decision.obligations[0]?.reason ?? 'policy denied');
|
||||
}
|
||||
return decision;
|
||||
}
|
||||
Reference in New Issue
Block a user