feat: 完成 OpenNexus 第三阶段核心功能与生产化基础 #45
@@ -438,3 +438,12 @@ Core 的独立数据目录目前不等于已授权 Vault。Python 旧笔记写
|
|||||||
- Windows SessionMonitor 支持有界多域信号组,锁屏、注销、控制台/远程断开同时撤销凭据与 Host execution Authority;解锁消息不会恢复原代次。线程创建失败返回稳定不可用错误,拒绝重复信号或超过 8 个域的配置。
|
- Windows SessionMonitor 支持有界多域信号组,锁屏、注销、控制台/远程断开同时撤销凭据与 Host execution Authority;解锁消息不会恢复原代次。线程创建失败返回稳定不可用错误,拒绝重复信号或超过 8 个域的配置。
|
||||||
- 8 项 Host 二进制测试、2 项原生会话消息测试、3 项许可测试通过,全目标 Clippy -D warnings 通过。验证在持有凭据互斥锁时手动锁定信号已发出、工作区替换/关闭推进代次,以及不轮换 HMAC 密钥只推进事件代次也能拒绝旧许可/租约。日志 `.build/extension-host-lifecycle-tests.log`、`.build/extension-host-session-tests.log`、`.build/extension-host-permit-tests.log`、`.build/extension-host-events-clippy.log`。
|
- 8 项 Host 二进制测试、2 项原生会话消息测试、3 项许可测试通过,全目标 Clippy -D warnings 通过。验证在持有凭据互斥锁时手动锁定信号已发出、工作区替换/关闭推进代次,以及不轮换 HMAC 密钥只推进事件代次也能拒绝旧许可/租约。日志 `.build/extension-host-lifecycle-tests.log`、`.build/extension-host-session-tests.log`、`.build/extension-host-permit-tests.log`、`.build/extension-host-events-clippy.log`。
|
||||||
- 会话测试仅向本测试的隐藏窗口注入原生消息,未锁定用户桌面;尚未完成实际 OS 锁屏与已打包 Host/扩展实例的组合验收。第三方执行仍未开放,后续实例签发必须使用 Host 持有的 Authority,实际在线信任撤销、实例注册/派发、broker 与完整资源策略继续实施。
|
- 会话测试仅向本测试的隐藏窗口注入原生消息,未锁定用户桌面;尚未完成实际 OS 锁屏与已打包 Host/扩展实例的组合验收。第三方执行仍未开放,后续实例签发必须使用 Host 持有的 Authority,实际在线信任撤销、实例注册/派发、broker 与完整资源策略继续实施。
|
||||||
|
|
||||||
|
|
||||||
|
## 修复:锁定会话下无秘密包的重新准备与撤销
|
||||||
|
|
||||||
|
- Context.prepare 现在对所有正式第三方启动绑定保险库会话代次并要求当前已解锁,不再仅对 CredentialScope 环境声明绑定。即使包不需要秘密、签名原语又产生了新许可,锁定期间也不能准备执行。
|
||||||
|
- 将 Context.build 收为内部方法,公开准备路径返回带租约的 PreparedLaunch,避免调用方从该授权组件直接取出不带会话约束的启动字节。底层受控原生测试入口仍存在,但完整第三方运行编排不得绕过 prepare。
|
||||||
|
- 测试验证无秘密包在初始锁定时拒绝,解锁后能准备,准备后锁定则不能创建进程;锁定期间新签发的许可也不能用于准备。真实 AppContainer 无秘密双进程实例在锁定后约 55.14 ms 清空,后续工具调用被拒绝。
|
||||||
|
- 53 项扩展回归通过,4 项 ignored 为三个父测试实际驱动的 Job 辅助入口及单独执行的 60 秒验收;全目标 Clippy -D warnings 通过。日志 `.build/extension-locked-session-tests.log`、`.build/extension-locked-session-clippy.log`。
|
||||||
|
- 这修复会话门禁缺口,尚未开放执行能力或完成实际桌面实例注册、broker、CPU/scratch 限额及整体验收;完整生产化目标继续进行。
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user