From 1876419468bd72a7e5ecd76f3fc935c4c1e3b74e Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Wed, 9 Sep 2026 00:00:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(sandbox):=20=E6=AF=8F=E6=AC=A1=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E9=83=BD=E5=BF=85=E9=A1=BB=E9=80=9A=E8=BF=87=E5=B7=B2?= =?UTF-8?q?=E8=A7=A3=E9=94=81=20Host=20=E4=BC=9A=E8=AF=9D=E9=97=A8?= =?UTF-8?q?=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src-tauri/src/extension_container.rs | 10 ++- .../src/extension_launch_authorization.rs | 67 +++++++++++++++++-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/frontend/src-tauri/src/extension_container.rs b/frontend/src-tauri/src/extension_container.rs index dd389ed..9ad87ac 100644 --- a/frontend/src-tauri/src/extension_container.rs +++ b/frontend/src-tauri/src/extension_container.rs @@ -779,12 +779,16 @@ mod tests { "before_resume", "permit", "credential", + "credential_without_secret", "owner_drop", "expiry", ] { let mut issuer = Authority::default(); let mut waiting = claims.clone(); waiting.arguments = vec!["wait_tree".into()]; + if cause == "credential_without_secret" { + waiting.environment.clear(); + } waiting.expires_at_ms = if cause == "expiry" { 2_002 } else { 120_000 }; let permit = issuer.issue(&waiting, 1).unwrap(); let prepared = context @@ -822,7 +826,7 @@ mod tests { let revoked = std::time::Instant::now(); match cause { "permit" => issuer.invalidate_all(), - "credential" => broker.lock(), + "credential" | "credential_without_secret" => broker.lock(), "expiry" => {} _ => drop(issuer), } @@ -839,7 +843,7 @@ mod tests { assert_eq!(running.active_test_processes().unwrap(), 0); assert!(revoked.elapsed() < std::time::Duration::from_secs(5)); let expected = match cause { - "credential" => "CREDENTIALS_LOCKED", + "credential" | "credential_without_secret" => "CREDENTIALS_LOCKED", "expiry" => "EXTENSION_PERMIT_EXPIRED", _ => "EXTENSION_PERMIT_REVOKED", }; @@ -850,7 +854,7 @@ mod tests { revoked.elapsed() ); drop(running); - if cause == "credential" { + if matches!(cause, "credential" | "credential_without_secret") { broker .unlock(Zeroizing::new(b"native fixture passphrase".to_vec())) .unwrap(); diff --git a/frontend/src-tauri/src/extension_launch_authorization.rs b/frontend/src-tauri/src/extension_launch_authorization.rs index 28979ec..a3c42b8 100644 --- a/frontend/src-tauri/src/extension_launch_authorization.rs +++ b/frontend/src-tauri/src/extension_launch_authorization.rs @@ -118,12 +118,11 @@ impl Context<'_> { now_ms: u64, ) -> Result { let mut lease = authority.lease(permit, claims, now_ms)?; - if claims - .environment - .values() - .any(|v| matches!(v, Environment::CredentialScope(_))) - { - lease.bind_credential(broker.lock_signal()); + // Locking the Host session gates all third-party execution, including + // packages that do not request environment secrets. + lease.bind_credential(broker.lock_signal()); + if broker.is_locked() { + return Err(HostError::new("CREDENTIALS_LOCKED")); } let data = self.build(authority, permit, claims, entry, broker, now_ms)?; lease.check()?; @@ -137,7 +136,7 @@ impl Context<'_> { } /// Caller must still recheck live trust/permit/session state immediately /// before resume; returning encoded data is not an execution lease. - pub fn build( + fn build( &self, authority: &Authority, permit: &Permit, @@ -258,6 +257,24 @@ mod tests { let authority = Authority::default(); let permit = authority.issue(&claims, 1).unwrap(); let mut broker = CredentialBroker::new(temp.path().join("credentials.v1")); + let mut public_claims = claims.clone(); + public_claims.environment.clear(); + let public_permit = authority.issue(&public_claims, 1).unwrap(); + assert_eq!( + context + .prepare( + &authority, + &public_permit, + &public_claims, + &entry, + &broker, + 2 + ) + .err() + .unwrap() + .code, + "CREDENTIALS_LOCKED" + ); broker .unlock(Zeroizing::new(b"fixture passphrase 123".to_vec())) .unwrap(); @@ -349,7 +366,43 @@ mod tests { .code, "EXTENSION_CREDENTIAL_ENCODING_INVALID" ); + let public_prepared = context + .prepare( + &authority, + &public_permit, + &public_claims, + &entry, + &broker, + 2, + ) + .unwrap(); broker.lock(); + let profile = crate::extension_container::Profile::create().unwrap(); + assert_eq!( + public_prepared + .create_suspended(&profile, &entry) + .err() + .unwrap() + .code, + "CREDENTIALS_LOCKED" + ); + profile.remove().unwrap(); + let fresh_while_locked = authority.issue(&public_claims, 2).unwrap(); + assert_eq!( + context + .prepare( + &authority, + &fresh_while_locked, + &public_claims, + &entry, + &broker, + 3 + ) + .err() + .unwrap() + .code, + "CREDENTIALS_LOCKED" + ); assert_eq!( context .build(&authority, &permit, &claims, &entry, &broker, 2)