From 42f1d40f1f531dc9ee4755545a56f3f67ecee861 Mon Sep 17 00:00:00 2001 From: Will Sarg <12886992+willsarg@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:51:07 -0500 Subject: [PATCH] fix(ci): unblock dependabot dependency PR checks (#658) --- .github/workflows/feature-matrix.yml | 12 +++- benches/agent_benchmarks.rs | 2 +- src/security/landlock.rs | 92 ++++++++++++++++++---------- src/tools/screenshot.rs | 10 ++- tests/agent_e2e.rs | 17 +++-- 5 files changed, 84 insertions(+), 49 deletions(-) diff --git a/.github/workflows/feature-matrix.yml b/.github/workflows/feature-matrix.yml index b252b7c..b710b84 100644 --- a/.github/workflows/feature-matrix.yml +++ b/.github/workflows/feature-matrix.yml @@ -37,12 +37,16 @@ jobs: include: - name: no-default-features args: --no-default-features + install_libudev: false - name: all-features args: --all-features + install_libudev: true - name: hardware-only args: --no-default-features --features hardware + install_libudev: false - name: browser-native args: --no-default-features --features browser-native + install_libudev: false steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -54,8 +58,14 @@ jobs: with: key: features-${{ matrix.name }} + - name: Install Linux system dependencies for all-features + if: matrix.install_libudev + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends libudev-dev pkg-config + - name: Check feature combination run: cargo check --locked ${{ matrix.args }} - name: Test feature combination - run: cargo test --locked ${{ matrix.args }} \ No newline at end of file + run: cargo test --locked ${{ matrix.args }} diff --git a/benches/agent_benchmarks.rs b/benches/agent_benchmarks.rs index 4a6c676..b230db0 100644 --- a/benches/agent_benchmarks.rs +++ b/benches/agent_benchmarks.rs @@ -13,7 +13,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use std::sync::{Arc, Mutex}; use zeroclaw::agent::agent::Agent; -use zeroclaw::agent::dispatcher::{NativeToolDispatcher, XmlToolDispatcher, ToolDispatcher}; +use zeroclaw::agent::dispatcher::{NativeToolDispatcher, ToolDispatcher, XmlToolDispatcher}; use zeroclaw::config::MemoryConfig; use zeroclaw::memory; use zeroclaw::memory::{Memory, MemoryCategory}; diff --git a/src/security/landlock.rs b/src/security/landlock.rs index afb990f..53e3b11 100644 --- a/src/security/landlock.rs +++ b/src/security/landlock.rs @@ -4,7 +4,7 @@ //! This module uses the pure-Rust `landlock` crate for filesystem access control. #[cfg(all(feature = "sandbox-landlock", target_os = "linux"))] -use landlock::{AccessFS, Ruleset, RulesetCreated}; +use landlock::{AccessFs, PathBeneath, PathFd, Ruleset, RulesetAttr, RulesetCreatedAttr}; use crate::security::traits::Sandbox; use std::path::Path; @@ -26,9 +26,11 @@ impl LandlockSandbox { /// Create a Landlock sandbox with a specific workspace directory pub fn with_workspace(workspace_dir: Option) -> std::io::Result { // Test if Landlock is available by trying to create a minimal ruleset - let test_ruleset = Ruleset::new().set_access_fs(AccessFS::read_file | AccessFS::write_file); + let test_ruleset = Ruleset::default() + .handle_access(AccessFs::ReadFile | AccessFs::WriteFile) + .and_then(|ruleset| ruleset.create()); - match test_ruleset.create() { + match test_ruleset { Ok(_) => Ok(Self { workspace_dir }), Err(e) => { tracing::debug!("Landlock not available: {}", e); @@ -47,49 +49,75 @@ impl LandlockSandbox { /// Apply Landlock restrictions to the current process fn apply_restrictions(&self) -> std::io::Result<()> { - let mut ruleset = Ruleset::new().set_access_fs( - AccessFS::read_file - | AccessFS::write_file - | AccessFS::read_dir - | AccessFS::remove_dir - | AccessFS::remove_file - | AccessFS::make_char - | AccessFS::make_sock - | AccessFS::make_fifo - | AccessFS::make_block - | AccessFS::make_reg - | AccessFS::make_sym, - ); + let mut ruleset = Ruleset::default() + .handle_access( + AccessFs::ReadFile + | AccessFs::WriteFile + | AccessFs::ReadDir + | AccessFs::RemoveDir + | AccessFs::RemoveFile + | AccessFs::MakeChar + | AccessFs::MakeSock + | AccessFs::MakeFifo + | AccessFs::MakeBlock + | AccessFs::MakeReg + | AccessFs::MakeSym, + ) + .and_then(|ruleset| ruleset.create()) + .map_err(|e| std::io::Error::other(e.to_string()))?; // Allow workspace directory (read/write) if let Some(ref workspace) = self.workspace_dir { if workspace.exists() { - ruleset = ruleset.add_path( - workspace, - AccessFS::read_file | AccessFS::write_file | AccessFS::read_dir, - )?; + let workspace_fd = + PathFd::new(workspace).map_err(|e| std::io::Error::other(e.to_string()))?; + ruleset = ruleset + .add_rule(PathBeneath::new( + workspace_fd, + AccessFs::ReadFile | AccessFs::WriteFile | AccessFs::ReadDir, + )) + .map_err(|e| std::io::Error::other(e.to_string()))?; } } // Allow /tmp for general operations - ruleset = ruleset.add_path( - Path::new("/tmp"), - AccessFS::read_file | AccessFS::write_file, - )?; + let tmp_fd = + PathFd::new(Path::new("/tmp")).map_err(|e| std::io::Error::other(e.to_string()))?; + ruleset = ruleset + .add_rule(PathBeneath::new( + tmp_fd, + AccessFs::ReadFile | AccessFs::WriteFile, + )) + .map_err(|e| std::io::Error::other(e.to_string()))?; // Allow /usr and /bin for executing commands - ruleset = ruleset.add_path(Path::new("/usr"), AccessFS::read_file | AccessFS::read_dir)?; - ruleset = ruleset.add_path(Path::new("/bin"), AccessFS::read_file | AccessFS::read_dir)?; + let usr_fd = + PathFd::new(Path::new("/usr")).map_err(|e| std::io::Error::other(e.to_string()))?; + ruleset = ruleset + .add_rule(PathBeneath::new( + usr_fd, + AccessFs::ReadFile | AccessFs::ReadDir, + )) + .map_err(|e| std::io::Error::other(e.to_string()))?; + + let bin_fd = + PathFd::new(Path::new("/bin")).map_err(|e| std::io::Error::other(e.to_string()))?; + ruleset = ruleset + .add_rule(PathBeneath::new( + bin_fd, + AccessFs::ReadFile | AccessFs::ReadDir, + )) + .map_err(|e| std::io::Error::other(e.to_string()))?; // Apply the ruleset - match ruleset.create() { + match ruleset.restrict_self() { Ok(_) => { tracing::debug!("Landlock restrictions applied successfully"); Ok(()) } Err(e) => { tracing::warn!("Failed to apply Landlock restrictions: {}", e); - Err(std::io::Error::new(std::io::ErrorKind::Other, e)) + Err(std::io::Error::other(e.to_string())) } } } @@ -97,7 +125,7 @@ impl LandlockSandbox { #[cfg(all(feature = "sandbox-landlock", target_os = "linux"))] impl Sandbox for LandlockSandbox { - fn wrap_command(&self, cmd: &mut std::process::Command) -> std::io::Result<()> { + fn wrap_command(&self, _cmd: &mut std::process::Command) -> std::io::Result<()> { // Apply Landlock restrictions before executing the command // Note: This affects the current process, not the child process // Child processes inherit the Landlock restrictions @@ -106,9 +134,9 @@ impl Sandbox for LandlockSandbox { fn is_available(&self) -> bool { // Try to create a minimal ruleset to verify availability - Ruleset::new() - .set_access_fs(AccessFS::read_file) - .create() + Ruleset::default() + .handle_access(AccessFs::ReadFile) + .and_then(|ruleset| ruleset.create()) .is_ok() } diff --git a/src/tools/screenshot.rs b/src/tools/screenshot.rs index e7911c3..a0152ec 100644 --- a/src/tools/screenshot.rs +++ b/src/tools/screenshot.rs @@ -69,8 +69,9 @@ impl ScreenshotTool { ); // Reject filenames with shell-breaking characters to prevent injection in sh -c - const SHELL_UNSAFE: &[char] = - &['\'', '"', '`', '$', '\\', ';', '|', '&', '\n', '\0', '(', ')']; + const SHELL_UNSAFE: &[char] = &[ + '\'', '"', '`', '$', '\\', ';', '|', '&', '\n', '\0', '(', ')', + ]; if safe_name.contains(SHELL_UNSAFE) { return Ok(ToolResult { success: false, @@ -307,10 +308,7 @@ mod tests { .await .unwrap(); assert!(!result.success); - assert!(result - .error - .unwrap() - .contains("unsafe for shell execution")); + assert!(result.error.unwrap().contains("unsafe for shell execution")); } #[test] diff --git a/tests/agent_e2e.rs b/tests/agent_e2e.rs index 4adaf9d..75ff97a 100644 --- a/tests/agent_e2e.rs +++ b/tests/agent_e2e.rs @@ -107,7 +107,12 @@ struct CountingTool { impl CountingTool { fn new() -> (Self, Arc>) { let count = Arc::new(Mutex::new(0)); - (Self { count: count.clone() }, count) + ( + Self { + count: count.clone(), + }, + count, + ) } } @@ -163,10 +168,7 @@ fn tool_response(calls: Vec) -> ChatResponse { } } -fn build_agent( - provider: Box, - tools: Vec>, -) -> Agent { +fn build_agent(provider: Box, tools: Vec>) -> Agent { Agent::builder() .provider(provider) .tools(tools) @@ -178,10 +180,7 @@ fn build_agent( .unwrap() } -fn build_agent_xml( - provider: Box, - tools: Vec>, -) -> Agent { +fn build_agent_xml(provider: Box, tools: Vec>) -> Agent { Agent::builder() .provider(provider) .tools(tools)