From 93c58cfacbc9ec64c5c2c4eca2fed2010994c7cf Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Tue, 26 May 2026 19:06:04 -0400 Subject: [PATCH] fix(sandbox): probe Landlock before build, skip on unsupported kernels On kernels without Landlock (e.g. gVisor's sentry returns ENOSYS for syscall 444), the previous best_effort path still logged "Applying Landlock" + "Landlock ruleset built" events even though no enforcement was happening. Probe at the top of `landlock::prepare` and short-circuit with a single High-severity "Sandbox Unavailable" finding. Signed-off-by: Davanum Srinivas --- .../src/sandbox/linux/landlock.rs | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs index 214fc700a..6b121e0ca 100644 --- a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs +++ b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs @@ -119,6 +119,45 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result { + openshell_ocsf::ocsf_emit!( + openshell_ocsf::DetectionFindingBuilder::new(crate::ocsf_ctx()) + .activity(openshell_ocsf::ActivityId::Open) + .severity(openshell_ocsf::SeverityId::High) + .confidence(openshell_ocsf::ConfidenceId::High) + .is_alert(true) + .finding_info( + openshell_ocsf::FindingInfo::new( + "landlock-unavailable", + "Landlock Filesystem Sandbox Unavailable", + ) + .with_desc(&format!( + "Running WITHOUT filesystem restrictions: Landlock is {availability}. \ + Set landlock.compatibility to 'hard_requirement' to make this fatal." + )), + ) + .message(format!( + "Landlock filesystem sandbox unavailable: {availability}" + )) + .build() + ); + return Ok(None); + } + LandlockCompatibility::HardRequirement => { + return Err(miette::miette!( + "Landlock unavailable in hard_requirement mode: {availability}" + )); + } + } + } + let total_paths = read_only.len() + read_write.len(); let abi = ABI::V2; openshell_ocsf::ocsf_emit!( @@ -135,8 +174,6 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result = (|| { let access_all = AccessFs::from_all(abi); let access_read = AccessFs::from_read(abi);