Our Personal Data Server from scratch!
0

Configure Feed

Select the types of activity you want to include in your feed.

fix: prevent rpc scopes from escaping permission set namespace

Signed-off-by: Trezy <tre@trezy.com>

author
Trezy
committer
Tangled
date (Jul 24, 2026, 8:11 PM +0300) commit fa50d7d5 parent 19e7ec29
+46 -9
+46 -9
crates/tranquil-scopes/src/permission_set.rs
··· 364 364 if let Some(lxms) = &perm.lxm { 365 365 let perm_aud = perm.aud.as_deref().or(default_aud); 366 366 367 - lxms.iter().for_each(|lxm| { 368 - let scope = match perm_aud { 369 - Some(aud) => format!("rpc:{}?aud={}", lxm, aud), 370 - None => format!("rpc:{}", lxm), 371 - }; 372 - scopes.push(scope); 373 - }); 367 + lxms.iter() 368 + .filter(|lxm| is_under_authority(lxm, namespace_authority)) 369 + .for_each(|lxm| { 370 + let scope = match perm_aud { 371 + Some(aud) => format!("rpc:{}?aud={}", lxm, aud), 372 + None => format!("rpc:{}", lxm), 373 + }; 374 + scopes.push(scope); 375 + }); 374 376 } 375 377 } 376 378 _ => {} ··· 492 494 493 495 let expanded = build_expanded_scopes(&permissions, None, "io.atcr"); 494 496 assert!(expanded.contains("rpc:io.atcr.getManifest?aud=*")); 495 - assert!(expanded.contains("rpc:com.atproto.repo.getRecord?aud=*")); 497 + assert!(!expanded.contains("com.atproto.repo.getRecord")); 496 498 } 497 499 498 500 #[test] ··· 545 547 546 548 let expanded = build_expanded_scopes(&permissions, None, "io.atcr"); 547 549 assert!(expanded.contains("repo:io.atcr.manifest?action=create")); 548 - assert!(expanded.contains("rpc:com.atproto.repo.getRecord?aud=*")); 550 + assert!(!expanded.contains("com.atproto.repo.getRecord")); 551 + } 552 + 553 + #[test] 554 + fn test_build_expanded_scopes_rpc_cannot_escape_namespace() { 555 + let permissions = vec![PermissionEntry { 556 + resource: "rpc".to_string(), 557 + action: None, 558 + collection: None, 559 + lxm: Some(vec![ 560 + "com.atproto.repo.deleteRecord".to_string(), 561 + "chat.bsky.convo.sendMessage".to_string(), 562 + "io.atcrEVIL.getManifest".to_string(), 563 + ]), 564 + aud: Some("*".to_string()), 565 + }]; 566 + 567 + let expanded = build_expanded_scopes(&permissions, None, "io.atcr"); 568 + assert!( 569 + expanded.is_empty(), 570 + "cross-namespace lxm values must all be dropped, got: {expanded}" 571 + ); 572 + } 573 + 574 + #[test] 575 + fn test_build_expanded_scopes_rpc_allows_child_namespace() { 576 + let permissions = vec![PermissionEntry { 577 + resource: "rpc".to_string(), 578 + action: None, 579 + collection: None, 580 + lxm: Some(vec!["io.atcr.sailor.star.getThing".to_string()]), 581 + aud: None, 582 + }]; 583 + 584 + let expanded = build_expanded_scopes(&permissions, None, "io.atcr"); 585 + assert_eq!(expanded, "rpc:io.atcr.sailor.star.getThing"); 549 586 } 550 587 551 588 #[test]