You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Enforcement-core hardening: fail-open & policy-widening classes in validatabl/limitabl/identifiabl/explicabl #41
Enforcement-core security review (2026-07-23) of the OSS packages. These live in code that a star-push evaluator WILL read. Note: production's coding-agent hook path uses a private engine, so most of these are reputational/external-consumer correctness rather than live prod exposure — but the repo is the trust artifact, so they matter here.
Findings (each with fix direction)
validatabl-core: schema-only config is fail-open when input is absent (src/decision.ts:101). if (options.inputSchema && request.input !== undefined) skips validation when input is undefined (GET, wrong content-type). Fix: when inputSchema is configured, missing input must DENY (validate undefined against the schema).
validatabl-core: hasScope builds a regex from the unescaped scope string (src/scopes.ts:30). A required scope with . matches any char (tool.write satisfied by toolXwrite); metachars throw → 500. Fix: getScopeStringFromClaims(...).split(" ").includes(scope) — no regex.
validatabl-core: policy widening (src/policy.ts): unanchored matches ("search" allows unsafe-search-exec), in array/String() coercion, empty-conditions matches every request. Fix: anchor by default, strict string typecheck, reject empty-condition rules at load.
limitabl-core: AgentGuard is client-opt-out and unbounded (src/preflight.ts:78, key from x-workflow-id). Omit header → guard never runs; rotate IDs → counters reset + Map grows unbounded. Fix: derive the key server-side, deny when guard configured but no key resolves, add TTL cleanup.
limitabl: singleton engine ignores every config after the first (src/index.ts:26-33). A stricter later mount silently reuses the first (laxer) config. Fix: key the cache per-config or build one engine per invocation.
identifiabl-core: JWT exp not required (src/index.ts:161-165). A token minted without exp is valid forever. Fix: requiredClaims:["exp"], reject empty issuer/audience at factory time.
explicabl: audit events carry no identity (src/index.ts:106-109) — reads res.locals.gatewayContext which nothing sets; identity lives in ALS. Every audit line logs context: undefined. Fix: read getGatewayContext() in the finish handler; warn once if absent.
explicabl: /webhooks/auth0 mounts the router FACTORY as middleware (src/index.ts:157) → requests hang forever, and the webhook is dead. Fix: call auth0LogsWebhook().
explicabl: aborted requests emit no audit event (src/index.ts:96, only finish hooked). Fix: also hook close with a fired-once guard.
Missing tests to lock the guarantees
decision() with inputSchema + undefined input denies; hasScope("a.b") doesn't match aXb; empty-conditions rule rejected; JWT round-trips reject alg:none/expired/missing-exp/wrong-aud; two limitabl mounts enforce different limits; redactPii overlap; client-abort emits audit.
Activity
Enforcement-core hardening: fail-open & policy-widening fixes (#41)
Enforcement-core hardening: fail-open & policy-widening fixes (#41) (#51
davidcrowe commentedon Sep 22, 2026
Fixed on main by #51 (enforcement-core hardening: fail-open and policy-widening fixes).