fix(ci): unblock dependabot dependency PR checks (#658)

This commit is contained in:
Will Sarg 2026-02-17 15:51:07 -05:00 committed by GitHub
parent 5be4fd9138
commit 42f1d40f1f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 84 additions and 49 deletions

View file

@ -37,12 +37,16 @@ jobs:
include: include:
- name: no-default-features - name: no-default-features
args: --no-default-features args: --no-default-features
install_libudev: false
- name: all-features - name: all-features
args: --all-features args: --all-features
install_libudev: true
- name: hardware-only - name: hardware-only
args: --no-default-features --features hardware args: --no-default-features --features hardware
install_libudev: false
- name: browser-native - name: browser-native
args: --no-default-features --features browser-native args: --no-default-features --features browser-native
install_libudev: false
steps: steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@ -54,6 +58,12 @@ jobs:
with: with:
key: features-${{ matrix.name }} 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 - name: Check feature combination
run: cargo check --locked ${{ matrix.args }} run: cargo check --locked ${{ matrix.args }}

View file

@ -13,7 +13,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use zeroclaw::agent::agent::Agent; 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::config::MemoryConfig;
use zeroclaw::memory; use zeroclaw::memory;
use zeroclaw::memory::{Memory, MemoryCategory}; use zeroclaw::memory::{Memory, MemoryCategory};

View file

@ -4,7 +4,7 @@
//! This module uses the pure-Rust `landlock` crate for filesystem access control. //! This module uses the pure-Rust `landlock` crate for filesystem access control.
#[cfg(all(feature = "sandbox-landlock", target_os = "linux"))] #[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 crate::security::traits::Sandbox;
use std::path::Path; use std::path::Path;
@ -26,9 +26,11 @@ impl LandlockSandbox {
/// Create a Landlock sandbox with a specific workspace directory /// Create a Landlock sandbox with a specific workspace directory
pub fn with_workspace(workspace_dir: Option<std::path::PathBuf>) -> std::io::Result<Self> { pub fn with_workspace(workspace_dir: Option<std::path::PathBuf>) -> std::io::Result<Self> {
// Test if Landlock is available by trying to create a minimal ruleset // 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 }), Ok(_) => Ok(Self { workspace_dir }),
Err(e) => { Err(e) => {
tracing::debug!("Landlock not available: {}", e); tracing::debug!("Landlock not available: {}", e);
@ -47,49 +49,75 @@ impl LandlockSandbox {
/// Apply Landlock restrictions to the current process /// Apply Landlock restrictions to the current process
fn apply_restrictions(&self) -> std::io::Result<()> { fn apply_restrictions(&self) -> std::io::Result<()> {
let mut ruleset = Ruleset::new().set_access_fs( let mut ruleset = Ruleset::default()
AccessFS::read_file .handle_access(
| AccessFS::write_file AccessFs::ReadFile
| AccessFS::read_dir | AccessFs::WriteFile
| AccessFS::remove_dir | AccessFs::ReadDir
| AccessFS::remove_file | AccessFs::RemoveDir
| AccessFS::make_char | AccessFs::RemoveFile
| AccessFS::make_sock | AccessFs::MakeChar
| AccessFS::make_fifo | AccessFs::MakeSock
| AccessFS::make_block | AccessFs::MakeFifo
| AccessFS::make_reg | AccessFs::MakeBlock
| AccessFS::make_sym, | AccessFs::MakeReg
); | AccessFs::MakeSym,
)
.and_then(|ruleset| ruleset.create())
.map_err(|e| std::io::Error::other(e.to_string()))?;
// Allow workspace directory (read/write) // Allow workspace directory (read/write)
if let Some(ref workspace) = self.workspace_dir { if let Some(ref workspace) = self.workspace_dir {
if workspace.exists() { if workspace.exists() {
ruleset = ruleset.add_path( let workspace_fd =
workspace, PathFd::new(workspace).map_err(|e| std::io::Error::other(e.to_string()))?;
AccessFS::read_file | AccessFS::write_file | AccessFS::read_dir, 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 // Allow /tmp for general operations
ruleset = ruleset.add_path( let tmp_fd =
Path::new("/tmp"), PathFd::new(Path::new("/tmp")).map_err(|e| std::io::Error::other(e.to_string()))?;
AccessFS::read_file | AccessFS::write_file, 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 // Allow /usr and /bin for executing commands
ruleset = ruleset.add_path(Path::new("/usr"), AccessFS::read_file | AccessFS::read_dir)?; let usr_fd =
ruleset = ruleset.add_path(Path::new("/bin"), AccessFS::read_file | AccessFS::read_dir)?; 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 // Apply the ruleset
match ruleset.create() { match ruleset.restrict_self() {
Ok(_) => { Ok(_) => {
tracing::debug!("Landlock restrictions applied successfully"); tracing::debug!("Landlock restrictions applied successfully");
Ok(()) Ok(())
} }
Err(e) => { Err(e) => {
tracing::warn!("Failed to apply Landlock restrictions: {}", 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"))] #[cfg(all(feature = "sandbox-landlock", target_os = "linux"))]
impl Sandbox for LandlockSandbox { 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 // Apply Landlock restrictions before executing the command
// Note: This affects the current process, not the child process // Note: This affects the current process, not the child process
// Child processes inherit the Landlock restrictions // Child processes inherit the Landlock restrictions
@ -106,9 +134,9 @@ impl Sandbox for LandlockSandbox {
fn is_available(&self) -> bool { fn is_available(&self) -> bool {
// Try to create a minimal ruleset to verify availability // Try to create a minimal ruleset to verify availability
Ruleset::new() Ruleset::default()
.set_access_fs(AccessFS::read_file) .handle_access(AccessFs::ReadFile)
.create() .and_then(|ruleset| ruleset.create())
.is_ok() .is_ok()
} }

View file

@ -69,8 +69,9 @@ impl ScreenshotTool {
); );
// Reject filenames with shell-breaking characters to prevent injection in sh -c // Reject filenames with shell-breaking characters to prevent injection in sh -c
const SHELL_UNSAFE: &[char] = const SHELL_UNSAFE: &[char] = &[
&['\'', '"', '`', '$', '\\', ';', '|', '&', '\n', '\0', '(', ')']; '\'', '"', '`', '$', '\\', ';', '|', '&', '\n', '\0', '(', ')',
];
if safe_name.contains(SHELL_UNSAFE) { if safe_name.contains(SHELL_UNSAFE) {
return Ok(ToolResult { return Ok(ToolResult {
success: false, success: false,
@ -307,10 +308,7 @@ mod tests {
.await .await
.unwrap(); .unwrap();
assert!(!result.success); assert!(!result.success);
assert!(result assert!(result.error.unwrap().contains("unsafe for shell execution"));
.error
.unwrap()
.contains("unsafe for shell execution"));
} }
#[test] #[test]

View file

@ -107,7 +107,12 @@ struct CountingTool {
impl CountingTool { impl CountingTool {
fn new() -> (Self, Arc<Mutex<usize>>) { fn new() -> (Self, Arc<Mutex<usize>>) {
let count = Arc::new(Mutex::new(0)); 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<ToolCall>) -> ChatResponse {
} }
} }
fn build_agent( fn build_agent(provider: Box<dyn Provider>, tools: Vec<Box<dyn Tool>>) -> Agent {
provider: Box<dyn Provider>,
tools: Vec<Box<dyn Tool>>,
) -> Agent {
Agent::builder() Agent::builder()
.provider(provider) .provider(provider)
.tools(tools) .tools(tools)
@ -178,10 +180,7 @@ fn build_agent(
.unwrap() .unwrap()
} }
fn build_agent_xml( fn build_agent_xml(provider: Box<dyn Provider>, tools: Vec<Box<dyn Tool>>) -> Agent {
provider: Box<dyn Provider>,
tools: Vec<Box<dyn Tool>>,
) -> Agent {
Agent::builder() Agent::builder()
.provider(provider) .provider(provider)
.tools(tools) .tools(tools)