Skip to content

Commit 1698f87

Browse files
committed
fix(agent): address macos host review follow-ups
1 parent c52c469 commit 1698f87

File tree

4 files changed

+88
-37
lines changed

4 files changed

+88
-37
lines changed

apps/agent/src-tauri/src/api_server.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,14 @@ fn macos_host_health_status(status: &CombinedSystemExtensionStatus) -> &'static
14461446
}
14471447
}
14481448

1449+
fn agent_health_status(status: &CombinedSystemExtensionStatus) -> &'static str {
1450+
if cfg!(target_os = "macos") {
1451+
macos_host_health_status(status)
1452+
} else {
1453+
"ok"
1454+
}
1455+
}
1456+
14491457
#[derive(Debug, Deserialize)]
14501458
struct DaemonEndpointDrift {
14511459
#[serde(default)]
@@ -1961,7 +1969,7 @@ async fn agent_health(
19611969
};
19621970

19631971
Ok(Json(AgentHealthResponse {
1964-
status: macos_host_health_status(&macos_host),
1972+
status: agent_health_status(&macos_host),
19651973
daemon,
19661974
session,
19671975
macos_host,
@@ -3345,6 +3353,16 @@ mod tests {
33453353
assert_eq!(macos_host_health_status(&active), "ok");
33463354
}
33473355

3356+
#[test]
3357+
fn agent_health_status_preserves_non_macos_ok_fallback() {
3358+
let status = CombinedSystemExtensionStatus::default();
3359+
if cfg!(target_os = "macos") {
3360+
assert_eq!(agent_health_status(&status), "pending");
3361+
} else {
3362+
assert_eq!(agent_health_status(&status), "ok");
3363+
}
3364+
}
3365+
33483366
#[test]
33493367
fn auth_accepts_bearer_token() {
33503368
let state = test_state();
@@ -3661,7 +3679,12 @@ mod tests {
36613679
.unwrap_or_else(|e| panic!("failed to read response body: {e}"));
36623680
let payload: serde_json::Value = serde_json::from_slice(&body)
36633681
.unwrap_or_else(|e| panic!("failed to decode response body: {e}"));
3664-
assert_eq!(payload["status"], "pending");
3682+
let expected_status = if cfg!(target_os = "macos") {
3683+
"pending"
3684+
} else {
3685+
"ok"
3686+
};
3687+
assert_eq!(payload["status"], expected_status);
36653688
assert_eq!(payload["macos_host"]["install_state"], "unknown");
36663689
assert_eq!(payload["macos_host"]["approval"], "unknown");
36673690
assert_eq!(

apps/agent/src-tauri/src/macos/collector.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,10 @@ fn merge_install_state(
231231
candidate: SystemExtensionInstallState,
232232
) -> SystemExtensionInstallState {
233233
match (current, candidate) {
234-
(SystemExtensionInstallState::Installed, _) | (_, SystemExtensionInstallState::Installed) => {
235-
SystemExtensionInstallState::Installed
236-
}
237234
(SystemExtensionInstallState::NotInstalled, _)
238235
| (_, SystemExtensionInstallState::NotInstalled) => SystemExtensionInstallState::NotInstalled,
236+
(SystemExtensionInstallState::Installed, _)
237+
| (_, SystemExtensionInstallState::Installed) => SystemExtensionInstallState::Installed,
239238
_ => SystemExtensionInstallState::Unknown,
240239
}
241240
}
@@ -415,4 +414,22 @@ mod tests {
415414
);
416415
assert_eq!(combined.network_extension, ProviderStatus::unknown());
417416
}
417+
418+
#[test]
419+
fn merge_install_state_fails_closed_for_partial_installation() {
420+
assert_eq!(
421+
merge_install_state(
422+
SystemExtensionInstallState::Installed,
423+
SystemExtensionInstallState::NotInstalled,
424+
),
425+
SystemExtensionInstallState::NotInstalled
426+
);
427+
assert_eq!(
428+
merge_install_state(
429+
SystemExtensionInstallState::NotInstalled,
430+
SystemExtensionInstallState::Installed,
431+
),
432+
SystemExtensionInstallState::NotInstalled
433+
);
434+
}
418435
}

crates/libs/clawdstrike/src/sandbox/attestation.rs

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ impl SandboxRuntimeState {
326326
deadline_miss_count: 0,
327327
dropped_event_count: 0,
328328
degraded_reasons: Vec::new(),
329-
provider_states: default_provider_states(applied, false),
329+
provider_states: default_provider_states(applied),
330330
failure_reason,
331331
};
332332
if !state.applied {
@@ -356,7 +356,7 @@ impl SandboxRuntimeState {
356356
deadline_miss_count: 0,
357357
dropped_event_count: 0,
358358
degraded_reasons: Vec::new(),
359-
provider_states: default_provider_states(applied, true),
359+
provider_states: default_provider_states(applied),
360360
failure_reason,
361361
};
362362

@@ -397,7 +397,7 @@ impl SandboxRuntimeState {
397397
"macos_authorization_contract_unavailable".to_string(),
398398
"supervised_launch_refused_without_live_authorization_provider".to_string(),
399399
],
400-
provider_states: default_provider_states(false, true),
400+
provider_states: default_provider_states(false),
401401
failure_reason: Some(failure_reason.into()),
402402
}
403403
}
@@ -599,12 +599,15 @@ fn platform_mechanisms(runtime: &SandboxRuntimeState) -> Vec<String> {
599599
.filter(|provider| provider.active)
600600
{
601601
match provider.provider.as_str() {
602-
"endpoint_security" => mechanisms.push("endpoint_security_contract".to_string()),
603-
"network_extension" => mechanisms.push("network_extension_contract".to_string()),
602+
"endpoint_security" => {
603+
push_unique_mechanism(&mut mechanisms, "endpoint_security_contract")
604+
}
605+
"network_extension" => {
606+
push_unique_mechanism(&mut mechanisms, "network_extension_contract")
607+
}
604608
_ => {}
605609
}
606610
}
607-
mechanisms.dedup();
608611
mechanisms
609612
} else if cfg!(target_os = "linux") {
610613
let mut mechanisms = vec!["landlock".to_string()];
@@ -617,6 +620,12 @@ fn platform_mechanisms(runtime: &SandboxRuntimeState) -> Vec<String> {
617620
}
618621
}
619622

623+
fn push_unique_mechanism(mechanisms: &mut Vec<String>, mechanism: &str) {
624+
if !mechanisms.iter().any(|existing| existing == mechanism) {
625+
mechanisms.push(mechanism.to_string());
626+
}
627+
}
628+
620629
fn effective_enforcement_level(
621630
runtime: &SandboxRuntimeState,
622631
provider_states: &[ProviderState],
@@ -642,26 +651,13 @@ fn effective_enforcement_level(
642651
}
643652
}
644653

645-
fn default_provider_states(applied: bool, supervised_requested: bool) -> Vec<ProviderState> {
654+
fn default_provider_states(applied: bool) -> Vec<ProviderState> {
646655
if cfg!(target_os = "macos") {
647-
let mut providers = vec![if applied {
656+
vec![if applied {
648657
ProviderState::active_without_approval("seatbelt")
649658
} else {
650659
ProviderState::unavailable_without_approval("seatbelt", "sandbox_apply_failed")
651-
}];
652-
653-
if supervised_requested {
654-
providers.push(ProviderState::unknown(
655-
"endpoint_security",
656-
"provider_state_unknown",
657-
));
658-
providers.push(ProviderState::unknown(
659-
"network_extension",
660-
"provider_state_unknown",
661-
));
662-
}
663-
664-
providers
660+
}]
665661
} else {
666662
Vec::new()
667663
}
@@ -721,14 +717,10 @@ mod tests {
721717
&caps,
722718
SandboxRuntimeState::supervised_mode(true, true, None),
723719
);
724-
if cfg!(target_os = "linux") {
725-
assert_eq!(
726-
attestation.enforcement_level,
727-
EnforcementLevel::KernelSupervised
728-
);
729-
} else {
730-
assert_eq!(attestation.enforcement_level, EnforcementLevel::Degraded);
731-
}
720+
assert_eq!(
721+
attestation.enforcement_level,
722+
EnforcementLevel::KernelSupervised
723+
);
732724
}
733725

734726
#[test]
@@ -762,12 +754,22 @@ mod tests {
762754

763755
let attestation = build_attestation(&caps, SandboxRuntimeState::static_mode(true, None));
764756
let json = serde_json::to_value(&attestation).unwrap();
757+
let expected_mechanism = if cfg!(target_os = "linux") {
758+
"landlock"
759+
} else if cfg!(target_os = "macos") {
760+
"seatbelt"
761+
} else {
762+
"none"
763+
};
765764

766765
assert!(json["enforced"].is_boolean());
767766
assert!(json["runtime"]["applied"].is_boolean());
768767
assert!(json["capabilities"]["proxy_port"].as_u64().is_some());
769768
assert_eq!(json["capabilities"]["proxy_port"].as_u64().unwrap(), 8080);
770-
assert_eq!(json["platform"]["mechanism"].as_str(), Some("seatbelt"));
769+
assert_eq!(
770+
json["platform"]["mechanism"].as_str(),
771+
Some(expected_mechanism)
772+
);
771773
assert!(json["platform"]["mechanisms"].is_array());
772774
assert!(!json["capabilities"]["blocked_commands"]
773775
.as_array()
@@ -900,6 +902,13 @@ mod tests {
900902

901903
if cfg!(target_os = "macos") {
902904
assert!(mechanisms.iter().any(|value| value == "seatbelt"));
905+
assert_eq!(
906+
mechanisms
907+
.iter()
908+
.filter(|value| *value == "seatbelt")
909+
.count(),
910+
1
911+
);
903912
assert!(mechanisms
904913
.iter()
905914
.any(|value| value == "endpoint_security_contract"));

crates/services/hush-cli/src/supervised_exec.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
use std::collections::HashMap;
2121
use std::sync::Arc;
2222

23-
use clawdstrike::sandbox::attestation::{ProviderState, SandboxRuntimeState};
23+
#[cfg(not(target_os = "linux"))]
24+
use clawdstrike::sandbox::attestation::ProviderState;
25+
use clawdstrike::sandbox::attestation::SandboxRuntimeState;
2426
use clawdstrike::sandbox::{SupervisorStats, TimestampedDenial};
2527
use clawdstrike::{GuardContext, HushEngine};
2628
use nono::{CapabilitySet, NeverGrantChecker};

0 commit comments

Comments
 (0)