fix(sandbox): 每次执行都必须通过已解锁 Host 会话门禁

This commit is contained in:
2026-09-09 00:00:47 +08:00
parent dbaa1a31e4
commit 1876419468
2 changed files with 67 additions and 10 deletions
@@ -779,12 +779,16 @@ mod tests {
"before_resume", "before_resume",
"permit", "permit",
"credential", "credential",
"credential_without_secret",
"owner_drop", "owner_drop",
"expiry", "expiry",
] { ] {
let mut issuer = Authority::default(); let mut issuer = Authority::default();
let mut waiting = claims.clone(); let mut waiting = claims.clone();
waiting.arguments = vec!["wait_tree".into()]; 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 }; waiting.expires_at_ms = if cause == "expiry" { 2_002 } else { 120_000 };
let permit = issuer.issue(&waiting, 1).unwrap(); let permit = issuer.issue(&waiting, 1).unwrap();
let prepared = context let prepared = context
@@ -822,7 +826,7 @@ mod tests {
let revoked = std::time::Instant::now(); let revoked = std::time::Instant::now();
match cause { match cause {
"permit" => issuer.invalidate_all(), "permit" => issuer.invalidate_all(),
"credential" => broker.lock(), "credential" | "credential_without_secret" => broker.lock(),
"expiry" => {} "expiry" => {}
_ => drop(issuer), _ => drop(issuer),
} }
@@ -839,7 +843,7 @@ mod tests {
assert_eq!(running.active_test_processes().unwrap(), 0); assert_eq!(running.active_test_processes().unwrap(), 0);
assert!(revoked.elapsed() < std::time::Duration::from_secs(5)); assert!(revoked.elapsed() < std::time::Duration::from_secs(5));
let expected = match cause { let expected = match cause {
"credential" => "CREDENTIALS_LOCKED", "credential" | "credential_without_secret" => "CREDENTIALS_LOCKED",
"expiry" => "EXTENSION_PERMIT_EXPIRED", "expiry" => "EXTENSION_PERMIT_EXPIRED",
_ => "EXTENSION_PERMIT_REVOKED", _ => "EXTENSION_PERMIT_REVOKED",
}; };
@@ -850,7 +854,7 @@ mod tests {
revoked.elapsed() revoked.elapsed()
); );
drop(running); drop(running);
if cause == "credential" { if matches!(cause, "credential" | "credential_without_secret") {
broker broker
.unlock(Zeroizing::new(b"native fixture passphrase".to_vec())) .unlock(Zeroizing::new(b"native fixture passphrase".to_vec()))
.unwrap(); .unwrap();
@@ -118,12 +118,11 @@ impl Context<'_> {
now_ms: u64, now_ms: u64,
) -> Result<PreparedLaunch> { ) -> Result<PreparedLaunch> {
let mut lease = authority.lease(permit, claims, now_ms)?; let mut lease = authority.lease(permit, claims, now_ms)?;
if claims // Locking the Host session gates all third-party execution, including
.environment // packages that do not request environment secrets.
.values() lease.bind_credential(broker.lock_signal());
.any(|v| matches!(v, Environment::CredentialScope(_))) if broker.is_locked() {
{ return Err(HostError::new("CREDENTIALS_LOCKED"));
lease.bind_credential(broker.lock_signal());
} }
let data = self.build(authority, permit, claims, entry, broker, now_ms)?; let data = self.build(authority, permit, claims, entry, broker, now_ms)?;
lease.check()?; lease.check()?;
@@ -137,7 +136,7 @@ impl Context<'_> {
} }
/// Caller must still recheck live trust/permit/session state immediately /// Caller must still recheck live trust/permit/session state immediately
/// before resume; returning encoded data is not an execution lease. /// before resume; returning encoded data is not an execution lease.
pub fn build( fn build(
&self, &self,
authority: &Authority, authority: &Authority,
permit: &Permit, permit: &Permit,
@@ -258,6 +257,24 @@ mod tests {
let authority = Authority::default(); let authority = Authority::default();
let permit = authority.issue(&claims, 1).unwrap(); let permit = authority.issue(&claims, 1).unwrap();
let mut broker = CredentialBroker::new(temp.path().join("credentials.v1")); 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 broker
.unlock(Zeroizing::new(b"fixture passphrase 123".to_vec())) .unlock(Zeroizing::new(b"fixture passphrase 123".to_vec()))
.unwrap(); .unwrap();
@@ -349,7 +366,43 @@ mod tests {
.code, .code,
"EXTENSION_CREDENTIAL_ENCODING_INVALID" "EXTENSION_CREDENTIAL_ENCODING_INVALID"
); );
let public_prepared = context
.prepare(
&authority,
&public_permit,
&public_claims,
&entry,
&broker,
2,
)
.unwrap();
broker.lock(); 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!( assert_eq!(
context context
.build(&authority, &permit, &claims, &entry, &broker, 2) .build(&authority, &permit, &claims, &entry, &broker, 2)