Skip to content

Validate grant conditions where they enter the hub - #254

Merged
alexanderguy merged 9 commits into
mainfrom
intr-292-validate-grant-conditions-shape-at-the-creation-boundary
Nov 3, 2026
Merged

alexanderguy merged 9 commits into
mainfrom
intr-292-validate-grant-conditions-shape-at-the-creation-boundary

Conversation

@alexanderguy

Copy link
Copy Markdown
Member

Summary

  • Each condition evaluator exposes a validate built from the same arktype schema as its evaluate, so boundary checks and evaluation read one set of shape rules. validateGrantConditions checks a grant's conditions against every registry that evaluates the grant: the hub's, and for a row on a workflow run's principal, also the run child's (time_window) and the credential gate's (tool), where the row's patterns reach them.
  • POST and PATCH /api/tenants/:tenantId/grants return a 400 that lists each problem when a grant's conditions cannot be evaluated, and store nothing. The probe gate refuses a workflow definition whose declared grant requirement carries such conditions, with the reason grant_requirement_conditions_invalid, before the operator approval checks.
  • A condition key that names an inherited object member, such as constructor, is an unknown condition. Evaluation still throws on any stored condition it cannot evaluate.

Verification

  • make all exits 0 on the branch tip: lint, the API docs check, tsc -b --force, the admin-ui bundle, and the tests with the DB-backed suites enabled.
  • tests/hub-api/grant-routes.test.ts covers the create and update 400s against a real database, including run-principal, credential-use and role-grant cases, and checks that a rejected request leaves the stored row unchanged. workflow-probe-gate.grants.test.ts covers the gate refusals and their order before the approval check.

Closes INTR-292

A condition key that names an inherited object member, such as
"constructor", resolved to a built-in function that returned a truthy
value, so a grant carrying it applied as if it had no condition. The
key is now an unknown condition and the evaluation throws.
@alexanderguy

Copy link
Copy Markdown
Member Author

Self-Review (code-review skill)

The branch carries the following intentional exceptions, each authorized
by Greybeard:

  • packages/hub-sessions/src/workflow-probe-gate.ts:716: the probe gate validates declared requirements against the constant HUB_CONDITION_REGISTRY, while the grant routes use the mount's conditionRegistry. No createApp or mountHubRoutes caller injects a registry, so both validate against HUB_CONDITION_REGISTRY, and the unused option is a pre-existing seam on main. Authorized by Greybeard on grounds of churn-avoidance: a registry parameter on the gate's call chain would carry only the constant.

@linear

linear Bot commented Nov 3, 2026

Copy link
Copy Markdown

INTR-292

A boundary that stores grant conditions needs to reject a body that
the evaluator would later refuse, and it can only do that without
drift if both read one set of shape rules. The new schemas are also
stricter than the old hand-rolled checks: time_window rejects
undeclared keys, and tool rejects an empty condition value. A stored
grant carrying one of these now throws at evaluation instead of being
ignored or silently withheld.
An empty default registry makes every condition key throw, so the
default did no work except hide a caller that forgot its registry.
The packages that the hub API depends on cannot import from it, so a
registry kept there was out of reach for every hub layer below the
routes that must check conditions against the same evaluators.
A run's declared grant requirements are evaluated in the run child as
well as on the hub, so the hub's probe gate must check them against
the child's registry, and it cannot reach one kept in the child.
A grant on a workflow run's principal is evaluated by the run child
and the credential gate as well as the hub, each with a narrower
registry, so a check against the hub registry alone accepts rows that
throw wherever the run evaluates them.
A grant whose conditions an evaluating registry cannot evaluate was
stored with a 2xx and only failed when authorization first reached
it, far from the operator who wrote it.
A requirement's conditions are copied verbatim into the grant rows a
run mints, so a condition one of the run's evaluators cannot evaluate
froze cleanly and then failed every authorization that reached the
row, far from the author who declared it.
@alexanderguy
alexanderguy force-pushed the intr-292-validate-grant-conditions-shape-at-the-creation-boundary branch from 7363e5d to 522d949 Compare November 3, 2026 16:54
@alexanderguy
alexanderguy merged commit b62cd50 into main Nov 3, 2026
6 checks passed
@alexanderguy
alexanderguy deleted the intr-292-validate-grant-conditions-shape-at-the-creation-boundary branch November 3, 2026 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant