Oct 30, 2026·10 min read·3 visits
The Cedar Policy Language JSON parser failed to validate slot identifiers inside `is ... in` scope constraints for policy templates. Supplying mismatched slot names causes either a denial-of-service via a Rust runtime panic during template linking or an authorization bypass due to inverted variable bindings during policy evaluation. Users should upgrade `cedar-policy` and `@cedar-policy/cedar-wasm` to version 4.11.1 or 3.4.3.
An improper input validation vulnerability (CWE-20) exists in the Entity Syntax Tree (EST) JSON deserialization component of the Cedar Policy Language (`cedar-policy` and `@cedar-policy/cedar-wasm`). When parsing JSON-formatted policy templates that define `is ... in` scope constraints on `principal` or `resource`, the parser used wildcard pattern matching that ignored the provided slot identifier (`?principal` or `?resource`). This omission allows malformed JSON templates to bypass initial deserialization validation, resulting in either a runtime panic during template linking or inverted slot bindings that alter authorization outcomes.
The Cedar Policy Language is an open-source authorization policy language and evaluation engine used to express and enforce fine-grained permissions across applications. Cedar supports two primary policy serialization formats: the human-readable native Cedar syntax and a structured JSON representation known as the Entity Syntax Tree (EST). Both formats support policy templates, which allow administrators to author parameterized policies containing placeholder slots (?principal and ?resource) that are dynamically linked to concrete entity identifiers at runtime.
Within the cedar-policy-core crate, the EST module (cedar-policy-core/src/est/scope_constraints.rs) is responsible for deserializing JSON-formatted scope constraints and converting them into internal Abstract Syntax Tree (ast) representations. In Cedar's semantic model, the principal scope may only reference the ?principal slot, and the resource scope may only reference the ?resource slot. While the native syntax parser strictly enforces this invariant during lexical and syntactic analysis, the JSON EST parser failed to validate slot names when processing is <EntityType> in <Slot> scope constraints.
This flaw is classified under CWE-20 (Improper Input Validation). By omitting verification of the slot field inside the PrincipalOrResourceInConstraint::Slot variant, the parser silently coerced any arbitrary or mismatched slot identifier into the default slot corresponding to the enclosing scope position. Consequently, the internal AST representation diverged from the authored JSON specification.
This discrepancy exposes applications ingesting JSON policy templates to two distinct failure modes. When a single slot is mismatched, attempting to link the template triggers an internal invariant violation, resulting in an unrecoverable Rust runtime panic and denial of service. When both the principal and resource slots are inverted, template linking succeeds but binds the target entities to the wrong scope positions, causing decision divergence that can lead to unauthorized access or unintended denials.
The root cause of GHSA-c33x-vcv4-vg3g resides in the TryFrom trait implementations that convert est::PrincipalConstraint and est::ResourceConstraint into ast::PrincipalOrResourceConstraint within cedar-policy-core/src/est/scope_constraints.rs. In Cedar's JSON format, a scope constraint using the is operator combined with an in clause is represented by the PrincipalOrResourceIsConstraint struct, which contains an entity_type string and an optional in_entity field of type PrincipalOrResourceInConstraint.
When a policy template defines a slot inside the in clause, the JSON object takes the form {"slot": "?principal"} or {"slot": "?resource"}, which deserializes via Serde into the PrincipalOrResourceInConstraint::Slot { slot: ast::SlotId } enum variant. At this stage of deserialization, Serde validates that the string is a valid SlotId (?principal or ?resource), but it does not possess the contextual awareness to verify whether that specific SlotId is legal within the surrounding PrincipalConstraint or ResourceConstraint.
Contextual validation is deferred to the TryFrom conversion functions. However, when handling the Is constraint variant, the conversion logic matched the in_entity field using a Rust struct wildcard pattern: Some(PrincipalOrResourceInConstraint::Slot { .. }). By using the { .. } wildcard, the compiler ignored the inner slot field entirely. The conversion immediately invoked ast::PrincipalOrResourceConstraint::is_entity_type_in_slot(Arc::new(entity_type)), which implicitly constructs an AST node bound to SlotId::principal() when called from the principal conversion path, or SlotId::resource() when called from the resource conversion path.
Because the AST construction succeeded without raising an InvalidSlotName error, the resulting ast::Template contained internal state contradictions. Specifically, the high-level template metadata or calling application expected the slot identifiers declared in the JSON source, whereas the underlying scope constraint AST nodes were hardcoded to positional defaults. This violation of internal invariants directly enables the downstream linking panic and evaluation divergence.
The vulnerability was remediated in Pull Request #2351 via commit a4adfaaa300cc925c644e4a0d7b1a3469bafbfd5. The patch modifies cedar-policy-core/src/est/scope_constraints.rs across both impl TryFrom<PrincipalConstraint> for ast::PrincipalOrResourceConstraint and impl TryFrom<ResourceConstraint> for ast::PrincipalOrResourceConstraint.
In the unpatched code, both implementations ignored the slot variable using { .. } and wrapped the entire match expression in an unconditional Ok(...) constructor:
// Unpatched implementation in cedar-policy-core/src/est/scope_constraints.rs
PrincipalConstraint::Is(PrincipalOrResourceIsConstraint {
entity_type,
in_entity,
}) => {
ast::EntityType::from_normalized_str(entity_type.as_str())
.map_err(Self::Error::InvalidEntityType)
.and_then(|entity_type| {
Ok(match in_entity {
None => ast::PrincipalOrResourceConstraint::is_entity_type(Arc::new(
entity_type,
)),
Some(PrincipalOrResourceInConstraint::Entity { entity }) => {
ast::PrincipalOrResourceConstraint::is_entity_type_in(
Arc::new(entity_type),
Arc::new(entity.into_euid(&|| {
JsonDeserializationErrorContext::EntityUid
})?),
)
}
// VULNERABILITY: Wildcard pattern `{ .. }` discards the `slot` value.
// Assumes the slot is valid for this position without verification.
Some(PrincipalOrResourceInConstraint::Slot { .. }) => {
ast::PrincipalOrResourceConstraint::is_entity_type_in_slot(
Arc::new(entity_type),
)
}
})
})
}The patched implementation destructures the slot field explicitly (Slot { slot }) and compares it against the expected positional ast::SlotId. If the identifier does not match ast::SlotId::principal() within a PrincipalConstraint (or ast::SlotId::resource() within a ResourceConstraint), the conversion halts and returns Err(Self::Error::InvalidSlotName):
// Patched implementation in cedar-policy-core/src/est/scope_constraints.rs
PrincipalConstraint::Is(PrincipalOrResourceIsConstraint {
entity_type,
in_entity,
}) => ast::EntityType::from_normalized_str(entity_type.as_str())
.map_err(Self::Error::InvalidEntityType)
.and_then(|entity_type| match in_entity {
None => Ok(ast::PrincipalOrResourceConstraint::is_entity_type(
Arc::new(entity_type),
)),
Some(PrincipalOrResourceInConstraint::Entity { entity }) => {
Ok(ast::PrincipalOrResourceConstraint::is_entity_type_in(
Arc::new(entity_type),
Arc::new(
entity.into_euid(&|| JsonDeserializationErrorContext::EntityUid)?,
),
))
}
// FIX: Bind `slot` and explicitly validate it against `SlotId::principal()`.
Some(PrincipalOrResourceInConstraint::Slot { slot }) => {
if slot == ast::SlotId::principal() {
Ok(ast::PrincipalOrResourceConstraint::is_entity_type_in_slot(
Arc::new(entity_type),
))
} else {
Err(Self::Error::InvalidSlotName)
}
}
}),The exact same structural change was applied to TryFrom<ResourceConstraint>, verifying if slot == ast::SlotId::resource(). The fix is complete and brings the Is constraint conversion into parity with other constraint types in scope_constraints.rs (such as Eq and In constraints without entity type restrictions), which already performed explicit slot identifier comparisons during EST-to-AST translation.
Triggering this issue requires an application or service to accept Cedar policy templates in the JSON (EST) format, instantiate templates containing is ... in scope constraints with slot placeholders, and subsequently link and evaluate those templates. Depending on how the JSON template is constructed, two distinct operational failures occur.
If a policy author or attacker supplies a JSON template where only one scope uses a mismatched slot—for example, setting the principal constraint's in clause to {"slot": "?resource"} while leaving the resource scope unconstrained—the EST parser silently converts the principal slot into ?principal inside the scope constraint AST. When the host application attempts to link the template by passing a concrete entity UID for ?resource (matching the JSON definition), Cedar rejects the operation with a linking error because the AST does not register ?resource as an active slot. However, if the caller links the template by supplying a value for ?principal, the initial slot check passes, but downstream linking routines encounter an internal invariant mismatch, causing the Rust thread to panic and terminating the host process if panics are not caught across FFI or thread boundaries.
A more severe logical failure occurs when both principal and resource constraints define is ... in clauses with inverted slot identifiers. Consider the following JSON policy template designed to forbid members of a specific project (?resource mistakenly used in principal) from accessing classified documents (?principal mistakenly used in resource):
{
"effect": "forbid",
"principal": {
"op": "is",
"entity_type": "User",
"in": { "slot": "?resource" }
},
"action": { "op": "All" },
"resource": {
"op": "is",
"entity_type": "Doc",
"in": { "slot": "?principal" }
},
"conditions": []
}When this template is parsed, the principal scope constraint silently binds to ?principal and the resource scope constraint silently binds to ?resource. Because both ?principal and ?resource are present in the resulting template, linking succeeds without errors or panics when the application provides values for both slots:
?principal = Classification::"topsecret"?resource = Project::"alpha"Instead of generating the intended policy forbid(principal is User in Project::"alpha", action, resource is Doc in Classification::"topsecret");, the engine evaluates forbid(principal is User in Classification::"topsecret", action, resource is Doc in Project::"alpha");. When a user in Project::"alpha" requests access to a Doc in Classification::"topsecret", the forbid rule fails to match because the user is not a member of Classification::"topsecret". Consequently, the explicit deny rule is bypassed.
The overall severity of GHSA-c33x-vcv4-vg3g is rated Low with a CVSS v4.0 score of 2.1 (CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:N/VC:N/VI:N/VA:L/SC:L/SI:L/SA:L). The score reflects the high privileges required (PR:H) and specific attack requirements (AT:P), as policy template creation is typically restricted to administrative users or control-plane services rather than unauthenticated end users.
In environments where untrusted or semi-trusted tenants are permitted to submit custom JSON policy templates (such as multi-tenant policy-as-a-service platforms or delegated administration portals), the availability impact (VA:L) is realized directly on the vulnerable authorization service. Triggering the single-slot linking invariant violation causes an immediate Rust panic!. In standard Rust binaries or WebAssembly modules (@cedar-policy/cedar-wasm), an unhandled panic aborts execution or traps the WASM instance, resulting in an application-level denial of service.
The subsequent system impacts (SC:L/SI:L/SA:L) stem from authorization decision divergence in downstream applications relying on Cedar to enforce access control boundaries. If a permit template experiences dual-slot inversion, principals may be granted access to unintended resources. Conversely, if a forbid template experiences dual-slot inversion, restrictive guardrails fail to activate, allowing unauthorized operations to proceed.
To remediate this vulnerability, development teams must upgrade their Cedar dependencies to versions that enforce strict slot identifier validation during JSON EST deserialization. The patches are available in the following releases:
cedar-policy on Crates.io): Upgrade to version 4.11.1 (for the 4.x release branch) or 3.4.3 (for the 3.x release branch).@cedar-policy/cedar-wasm on npm): Upgrade to version 4.11.1 (for the 4.x release branch) or 3.4.3 (for the 3.x release branch).> [!NOTE]
> Upgrading to the patched versions introduces stricter validation that will reject previously accepted malformed JSON policy templates with an InvalidSlotName deserialization error. Organizations should audit stored JSON templates prior to upgrading to ensure existing policies do not break at runtime.
For environments where immediate dependency upgrades are not feasible, administrators can mitigate the issue through static auditing and pre-validation of JSON policy templates before passing them to the Cedar engine. Specifically, inspect all JSON policy definitions where principal.op == "is" or resource.op == "is" and verify the inner in.slot property:
principal object containing "op": "is" and an "in" slot specifies "slot": "?principal".resource object containing "op": "is" and an "in" slot specifies "slot": "?resource".?resource appears inside the principal constraint or ?principal appears inside the resource constraint.Alternatively, converting policy ingestion pipelines to parse templates using the native Cedar policy syntax rather than the JSON EST format avoids the vulnerable code path entirely, as the native parser was not affected by this flaw.
CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:N/VC:N/VI:N/VA:L/SC:L/SI:L/SA:L| Product | Affected Versions | Fixed Version |
|---|---|---|
cedar-policy cedar-policy | >= 4.0.0, < 4.11.1 | 4.11.1 |
cedar-policy cedar-policy | >= 3.0.0, < 3.4.3 | 3.4.3 |
@cedar-policy/cedar-wasm cedar-policy | >= 4.0.1, < 4.11.1 | 4.11.1 |
@cedar-policy/cedar-wasm cedar-policy | >= 3.2.0, < 3.4.3 | 3.4.3 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | GHSA-c33x-vcv4-vg3g |
| Weakness Class | CWE-20 (Improper Input Validation) |
| CVSS v4.0 Score | 2.1 (Low) |
| Attack Vector | Network (JSON Policy Template Ingestion) |
| Primary Impact | Denial of Service (Panic) & Authorization Decision Divergence |
| Affected Component | cedar-policy-core/src/est/scope_constraints.rs |
| Exploit Status | Proof-of-Concept Available |
| Patched Versions | 4.11.1, 3.4.3 |
The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly.
The @eeacms/volto-chatbot package for Plone Volto contains an unauthenticated confused deputy vulnerability (CWE-862) in versions 2.0.3 and earlier. The package registers Express middleware on wildcard proxy routes (`**/_v1_da/**` and `**/_v1_ha/**`) without verifying client authentication or restricting target endpoints. When processing incoming HTTP requests, the middleware automatically attaches server-side administrative credentials before forwarding the requests to upstream Danswer (Onyx) or Halloumi (LLMGW) API servers. This allows unauthenticated network attackers to execute arbitrary administrative API calls against the backend infrastructure.
CVE-2026-9705 is an insufficient session expiration and authorization bypass vulnerability in Keycloak and Red Hat build of Keycloak. The vulnerability occurs in the OAuth 2.0 Dynamic Client Registration Management Protocol implementation, where the authentication handler fails to verify whether a target client is enabled before accepting requests authenticated with a previously issued Registration Access Token (RAT). Consequently, an actor possessing a valid RAT for an administratively disabled client can query, update, and re-enable the client configuration, restoring revoked access to realm resources.
CVE-2026-9800 is an authorization bypass vulnerability affecting the Keycloak Policy Enforcer (org.keycloak:keycloak-policy-enforcer), which is integrated into Red Hat build of Keycloak, Red Hat build of Quarkus, Red Hat Single Sign-On 7, and Red Hat JBoss Enterprise Application Platform Expansion Pack. The flaw stems from improper comparison logic in the PolicyEnforcer.isDefaultAccessDeniedUri method, which uses loose substring matching on the full raw request URI rather than exact, normalized path comparisons against the configured access-denied landing page. Consequently, an authenticated attacker possessing any valid low-privileged token can bypass role-based, scope-based, and User-Managed Access (UMA) policy evaluations by embedding the access-denied path segment into the URL path or query parameters.
CVE-2026-9799 is an authorization bypass vulnerability in the User-Managed Access (UMA) subsystem of Keycloak (org.keycloak.authorization). When a resource server is configured with the non-default PERMISSIVE policy enforcement mode, an authenticated user holding a valid UMA permission ticket for a single typed resource can obtain an authorization Requesting Party Token (RPT) granting access to all resources of the same type across the resource server. By submitting an authorization request with the type selector prefix 'resource-type-any:<type>', the attacker bypasses instance-level owner approval checks for unshared peer resources that lack explicit static administrative policies.
CVE-2026-9083 is a path traversal and information disclosure vulnerability (CWE-22) affecting Keycloak and the Red Hat build of Keycloak. The vulnerability resides in the Java Keystore key provider component (JavaKeystoreKeyProviderFactory), which fails to restrict the user-supplied keystore file path to a designated directory. An authenticated realm administrator holding the 'manage-realm' role can supply arbitrary absolute or relative filesystem paths when configuring a key provider. Because the application returns distinct error messages depending on whether the target path does not exist, lacks read permissions, or fails keystore parsing, an attacker can use this component as an oracle to probe the underlying host filesystem.
CVE-2026-9086 is a stored Cross-Site Scripting (XSS) vulnerability in Keycloak and the Red Hat build of Keycloak. The vulnerability stems from a case-sensitive URI scheme validation check in DefaultClientValidationProvider, allowing attackers with client registration or management permissions to inject javascript: or data: URIs using mixed-case characters.