Found while investigating tempest-identity-compatibility 403s (#997/#998), not the direct cause of any currently-tracked failure — a separate correctness bug surfaced as a byproduct.
Credentials (crates/core/src/policy.rs) serializes its system-scope field as system (pub system: Option<String>, no #[serde(rename)]), populated as "all" for system scope by Credentials::try_from. Several .rego policies instead check a field named system_scope, which is never present in the real OPA input — so their "reader role with system scope may read this" rule is silently dead (always evaluates to undefined/false), not just for these three, for every request that should qualify:
policy/auth/token/show.rego (lines ~40-41)
policy/auth/project/list.rego
policy/assignment/list.rego
policy/service/show.rego
policy/service/list.rego
policy/endpoint/show.rego
policy/endpoint/list.rego
(policy/auth/token/revoke.rego has the same wrong field name but in an already-commented-out block, so it's inert either way.)
Each of these files' own _test.rego mocks the same wrong system_scope key, so the existing unit tests pass while asserting a self-consistently wrong contract — they don't catch this because they never exercise the real Rust→JSON field name.
Impact: a caller with role:reader and system scope (GET a token, list domains-a-project-belongs-to/projects, list role assignments, show/list services, show/list endpoints) is denied even though the policy's own doc comments say they should be allowed. Fails closed (denies access that should be granted), not a privilege escalation, but a real functional/compatibility gap — likely part of why some tempest identity tests using system-scoped reader credentials fail.
Fix: rename system_scope → system in each allow rule listed above and in the corresponding _test.rego mocks (matching the working reference implementation already used correctly elsewhere, e.g. policy/resource/domain/show.rego's input.credentials.system == "all").
Found while investigating tempest-identity-compatibility 403s (#997/#998), not the direct cause of any currently-tracked failure — a separate correctness bug surfaced as a byproduct.
Credentials(crates/core/src/policy.rs) serializes its system-scope field assystem(pub system: Option<String>, no#[serde(rename)]), populated as"all"for system scope byCredentials::try_from. Several.regopolicies instead check a field namedsystem_scope, which is never present in the real OPA input — so their "readerrole with system scope may read this" rule is silently dead (always evaluates toundefined/false), not just for these three, for every request that should qualify:policy/auth/token/show.rego(lines ~40-41)policy/auth/project/list.regopolicy/assignment/list.regopolicy/service/show.regopolicy/service/list.regopolicy/endpoint/show.regopolicy/endpoint/list.rego(
policy/auth/token/revoke.regohas the same wrong field name but in an already-commented-out block, so it's inert either way.)Each of these files' own
_test.regomocks the same wrongsystem_scopekey, so the existing unit tests pass while asserting a self-consistently wrong contract — they don't catch this because they never exercise the real Rust→JSON field name.Impact: a caller with
role:readerand system scope (GETa token, list domains-a-project-belongs-to/projects, list role assignments, show/list services, show/list endpoints) is denied even though the policy's own doc comments say they should be allowed. Fails closed (denies access that should be granted), not a privilege escalation, but a real functional/compatibility gap — likely part of why some tempest identity tests using system-scoped reader credentials fail.Fix: rename
system_scope→systemin eachallowrule listed above and in the corresponding_test.regomocks (matching the working reference implementation already used correctly elsewhere, e.g.policy/resource/domain/show.rego'sinput.credentials.system == "all").