Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

GHSA-C33X-VCV4-VG3G: GHSA-c33x-vcv4-vg3g: Missing Slot Identifier Validation in Cedar Policy Language JSON Template Parser…

GHSA-c33x-vcv4-vg3g: Missing Slot Identifier Validation in Cedar Policy Language JSON Template Parser Causes Panic and Authorization Divergence Vulnerability ID: GHSA-C33X-VCV4-VG3G CVSS Score: 2.1 Published: 2026-10-

GHSA-c33x-vcv4-vg3g: Missing Slot Identifier Validation in Cedar Policy Language JSON Template Parser Causes Panic and Authorization Divergence

Vulnerability ID: GHSA-C33X-VCV4-VG3G
CVSS Score: 2.1
Published: 2026-10-29

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.

TL;DR

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.

⚠️ Exploit Status: POC

Technical Details

  • 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

Affected Systems

  • cedar-policy (Rust crate)
  • cedar-policy-core (Rust crate)
  • @cedar-policy/cedar-wasm (npm package)
  • cedar-policy: >= 4.0.0, < 4.11.1 (Fixed in: 4.11.1)
  • cedar-policy: >= 3.0.0, < 3.4.3 (Fixed in: 3.4.3)
  • @cedar-policy/cedar-wasm: >= 4.0.1, < 4.11.1 (Fixed in: 4.11.1)
  • @cedar-policy/cedar-wasm: >= 3.2.0, < 3.4.3 (Fixed in: 3.4.3)

Code Analysis

Commit: a4adfaa

Validate slot id in is ... in ?slot scope constraints when deserializing from EST JSON format.

diff --git a/cedar-policy-core/src/est/scope_constraints.rs b/cedar-policy-core/src/est/scope_constraints.rs
index 2d155352ed..f35b02ac90 100644
--- a/cedar-policy-core/src/est/scope_constraints.rs
+++ b/cedar-policy-core/src/est/scope_constraints.rs
@@ -794,30 +794,30 @@ impl TryFrom<PrincipalConstraint> for ast::PrincipalOrResourceConstraint {
             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
-                                    })?),
-                                )
-                            }
-                            Some(PrincipalOrResourceInConstraint::Slot { .. }) => {
-                                ast::PrincipalOrResourceConstraint::is_entity_type_in_slot(
-                                    Arc::new(entity_type),
-                                )
-                            }
-                        })
-                    })
-            }
+            }) => 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)?,
+                            ),
+                        ))
+                    }
+                    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)
+                        }
+                    }
+                }),
         }
     }
 }
@@ -860,30 +860,30 @@ impl TryFrom<ResourceConstraint> for ast::PrincipalOrResourceConstraint {
             ResourceConstraint::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
-                                    })?),
-                                )
-                            }
-                            Some(PrincipalOrResourceInConstraint::Slot { .. }) => {
-                                ast::PrincipalOrResourceConstraint::is_entity_type_in_slot(
-                                    Arc::new(entity_type),
-                                )
-                            }
-                        })
-                    })
-            }
+            }) => 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)?,
+                            ),
+                        ))
+                    }
+                    Some(PrincipalOrResourceInConstraint::Slot { slot }) => {
+                        if slot == ast::SlotId::resource() {
+                            Ok(ast::PrincipalOrResourceConstraint::is_entity_type_in_slot(
+                                Arc::new(entity_type),
+                            ))
+                        } else {
+                            Err(Self::Error::InvalidSlotName)
+                        }
+                    }
+                }),
         }
     }
 }

Exploit Details

  • GitHub Security Advisory: Official security advisory containing proof-of-concept JSON templates demonstrating single-slot linking panics and dual-slot decision divergence.

Mitigation Strategies

  • Upgrade cedar-policy and @cedar-policy/cedar-wasm to patched maintenance releases (4.11.1 or 3.4.3).
  • Implement pre-deserialization JSON schema or static validation to ensure principal constraints only reference ?principal and resource constraints only reference ?resource.
  • Use the native Cedar policy syntax format instead of the JSON (EST) format when ingesting policy templates.

Remediation Steps:

  1. Locate all Cargo.toml files referencing cedar-policy or cedar-policy-core and update version constraints to >= 4.11.1 (for 4.x) or >= 3.4.3, < 4.0.0 (for 3.x).
  2. Locate all package.json files referencing @cedar-policy/cedar-wasm and update version constraints to ^4.11.1 or ^3.4.3.
  3. Audit existing JSON policy template stores for anti-patterns where principal.in.slot is set to ?resource or resource.in.slot is set to ?principal.
  4. Run regression tests on policy template linking workflows to ensure no legacy malformed templates trigger InvalidSlotName errors after upgrading.

References

Read the full report for GHSA-C33X-VCV4-VG3G on our website for more details including interactive diagrams and full exploit analysis.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.