Eh2406 opened a new issue, #431: URL: https://github.com/apache/casbin-rs/issues/431
Hi, I am Co-Lead of the Rust Cargo Team and was recently hired by the Rust Foundation to help triage AI discovered bugs. This bug was found with AI, namely scrutineer and Fable. Except where specifically labeled this report was written by me. I am posting this as a public issue as I have not received a response to our attempts to reach out on https://security.apache.org/report-code/, by email, and on your discord. From the AI: > ## Summary > > Authorization bypass in `CachedEnforcer` allows a decision produced by one matcher section to be reused for the same request tuple under another matcher section. `CachedEnforcer::enforce_with_context` derives its cache key only from request values, so the `EnforceContext` selecting the `r`/`p`/`e`/`m` assertion set is ignored on cache lookup. > > ## Impact > > A remote user who can submit ordinary authorization requests can receive an allow decision from a more permissive matcher section when the application enables the `cached` feature and uses multiple assertion sets. This can cross application privilege tiers, such as replaying a public-section grant in an administrative section, without controlling the model, policy, or `EnforceContext`. > > ## Proof of concept > > Create `model.conf` with two request, policy, effect, and matcher sections. Give `alice` access only in section 1 and `bob` access only in section 2, then run this test with `cargo test --features cached --test poc_cache_context -- --nocapture`: > > ```rust > use casbin::prelude::*; > use casbin::{CachedEnforcer, EnforceContext}; > > #[tokio::test] > async fn cache_context_mixup() { > let plain = Enforcer::new("model.conf", "policy.csv").await.unwrap(); > assert!(!plain.enforce_with_context( > EnforceContext::new("2"), ("alice", "data1", "read") > ).unwrap()); > > let cached = CachedEnforcer::new("model.conf", "policy.csv").await.unwrap(); > assert!(cached.enforce(("alice", "data1", "read")).unwrap()); > assert!(cached.enforce_with_context( > EnforceContext::new("2"), ("alice", "data1", "read") > ).unwrap()); // incorrect: section 2 denies alice > } > ``` > > At current HEAD, the uncached enforcer returns `false` for `alice` under section 2, but the cached enforcer returns `true` after section 1 has cached the identical request tuple: > > ```text > plain section 1: true > plain section 2: false > cached section 1: true > cached section 2: true (expected false) > ``` > > The smoking gun is EnforceContext::get_cache_key() at src/enforcer.rs:95: a method that formats the four assertion names into EnforceContext{r2-p2-e2-m2} — exactly the discriminator this cache key needs. grep -rn get_cache_key over the whole crate (and over the published 2.20.0 tarball) returns its definition and nothing else. It is dead code; the call site that would have used it never materialised. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
