Skip to main content Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/majiayu000/sage --skill sage-sandbox-securityEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Ocupaciones relacionadasSOC
Basado en la clasificación ocupacional SOC
| name | sage-sandbox-security |
| description | Sage 沙箱安全开发指南,涵盖命令验证、路径策略、OS 级隔离、违规追踪 |
| when_to_use | 当涉及命令安全检查、路径访问控制、沙箱配置、安全策略时使用 |
| allowed_tools | ["Read","Grep","Glob","Edit","Write","Bash"] |
| user_invocable | true |
| priority | 92 |
Sage 沙箱安全开发指南
模块概览
沙箱模块是 Sage 的安全核心,代码量 5792 行,提供多层安全防护:
crates/sage-core/src/sandbox/
├── mod.rs # 公开接口 + DefaultSandbox (415行)
├── config.rs # 沙箱配置
├── limits.rs # 资源限制
├── executor/ # 命令执行器
│ ├── mod.rs # 入口
│ ├── executor.rs # SandboxExecutor
│ ├── builder.rs # ExecutionBuilder
│ ├── limits.rs # 资源限制执行
│ ├── types.rs # SandboxedExecution
│ └── tests.rs # 测试
├── policy/ # 安全策略
│ ├── mod.rs # SandboxPolicy
│ ├── path_policy.rs # 路径访问控制 (460行)
│ ├── command_policy.rs # 命令过滤 (137行)
│ └── network_policy.rs # 网络访问控制 (139行)
├── validation/ # 命令验证
│ ├── mod.rs # 入口 (98行)
│ ├── heredoc_check.rs # Heredoc 注入检测 (222行)
│ ├── metacharacter_check.rs # Shell 元字符检测 (292行)
│ ├── pattern_check.rs # 危险模式检测 (219行)
│ ├── removal_check.rs # 危险删除检测 (276行)
│ ├── variable_check.rs # 变量注入检测 (173行)
│ └── types.rs # 类型定义 (256行)
├── os_sandbox/ # OS 级沙箱
│ ├── mod.rs # 入口 (77行)
│ ├── macos.rs # macOS sandbox-exec (272行)
│ ├── linux.rs # Linux seccomp (229行)
│ └── types.rs # 配置类型 (206行)
└── violations/ # 违规追踪
├── mod.rs # 入口
├── types.rs # ViolationType (232行)
├── store.rs # ViolationStore (328行)
└── annotator.rs # 违规注解 (216行)
一、安全架构
1.1 三层安全模型
┌─────────────────────────────────────────────────────────────┐
│ Layer 3: OS Sandbox │
│ macOS sandbox-exec / Linux seccomp │
│ (可选, 最强隔离) │
└─────────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────────┐
│ Layer 2: Resource Limits │
│ CPU, Memory, Output, File Size (rlimit) │
│ (Unix 资源限制) │
└─────────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: Policy-based │
│ Path/Command/Network Restrictions (默认) │
│ (策略检查层) │
└─────────────────────────────────────────────────────────────┘
1.2 Sandbox Trait
#[async_trait]
pub trait Sandbox: Send + Sync {
fn name(&self) -> &str;
fn check_path(&self, path: &PathBuf, write: bool) -> SandboxResult<()>;
fn check_command(&self, command: &str) -> SandboxResult<()>;
fn check_network(&self, host: &str, port: u16) -> SandboxResult<()>;
fn resource_limits(&self) -> &ResourceLimits;
async fn execute_command(
&self,
command: &str,
args: &[String],
working_dir: Option<&PathBuf>,
env: Option<&HashMap<String, String>>,
) -> SandboxResult<SandboxedExecution>;
async fn read_file(&self, path: &PathBuf) -> SandboxResult<String>;
async fn write_file(&self, path: &PathBuf, content: &str) -> SandboxResult<()>;
fn is_active(&self) -> bool;
fn current_usage(&self) -> ResourceUsage;
}
二、命令验证系统
2.1 验证流程
pub fn validate_command(command: &str, context: &ValidationContext) -> ValidationResult {
let checks = [
check_heredoc_safety(command),
check_shell_metacharacters(command, context),
check_dangerous_variables(command),
check_dangerous_patterns(command),
check_dangerous_removal(command),
];
let mut all_warnings = Vec::new();
for result in &checks {
if !result.allowed {
return result.clone_with_warnings(all_warnings);
}
all_warnings.extend(result.warnings.clone());
}
ValidationResult::pass_with_warnings(CheckType::Composite, all_warnings)
}
2.2 Heredoc 注入检测
pub fn check_heredoc_safety(command: &str) -> ValidationResult {
let heredoc_pattern = Regex::new(r"<<\s*(\$?\w+)").unwrap();
for cap in heredoc_pattern.captures_iter(command) {
let delimiter = cap.get(1).unwrap().as_str();
if delimiter.starts_with('$') {
return ValidationResult::block(
CheckType::Heredoc,
format!("Variable heredoc delimiter: {}", delimiter),
);
}
}
ValidationResult::pass(CheckType::Heredoc)
}
2.3 危险删除检测
const CRITICAL_PATHS: &[&str] = &[
"/",
"/bin",
"/boot",
"/dev",
"/etc",
"/home",
"/lib",
"/lib64",
"/opt",
"/proc",
"/root",
"/sbin",
"/sys",
"/tmp",
"/usr",
"/var",
"~",
"$HOME",
];
pub fn check_dangerous_removal(command: &str) -> ValidationResult {
let rm_pattern = Regex::new(r"\brm\s+(-[rf]+\s+)*(.+)").unwrap();
if let Some(cap) = rm_pattern.captures(command) {
let target = cap.get(2).map(|m| m.as_str()).unwrap_or("");
for critical in CRITICAL_PATHS {
if target == *critical || target.starts_with(&format!("{}/", critical)) {
return ValidationResult::block(
CheckType::Removal,
format!("Critical path removal: {}", target),
);
}
}
}
ValidationResult::pass(CheckType::Removal)
}
2.4 Shell 元字符检测
const DANGEROUS_METACHARACTERS: &[(&str, &str)] = &[
(";", "Command chaining"),
("&&", "Conditional execution"),
("||", "Conditional execution"),
("|", "Pipe"),
("`", "Command substitution"),
("$(", "Command substitution"),
("${", "Variable expansion"),
(">(", "Process substitution"),
("<(", "Process substitution"),
];
pub fn check_shell_metacharacters(
command: &str,
context: &ValidationContext
) -> ValidationResult {
if context.strictness == ValidationStrictness::Permissive {
return ValidationResult::pass(CheckType::Metacharacter);
}
let mut warnings = Vec::new();
for (char, desc) in DANGEROUS_METACHARACTERS {
if command.contains(char) {
if context.strictness == ValidationStrictness::Strict {
return ValidationResult::block(
CheckType::Metacharacter,
format!("{}: {}", desc, char),
);
}
warnings.push(ValidationWarning::new(
WarningSeverity::Medium,
format!("{}: {}", desc, char),
));
}
}
ValidationResult::pass_with_warnings(CheckType::Metacharacter, warnings)
}
三、路径策略
3.1 敏感文件保护
const SENSITIVE_FILES: &[&str] = &[
".gitconfig", ".git/config", ".git/hooks/",
".bashrc", ".bash_profile", ".bash_history",
".zshrc", ".zsh_history", ".profile", ".zprofile",
".ssh/", ".aws/", ".docker/", ".kube/", ".gnupg/",
".npmrc", ".pypirc", ".netrc",
".cargo/credentials", ".cargo/credentials.toml",
".env", ".env.local", ".env.production",
"secrets.yaml", "secrets.json", ".secrets",
".vscode/settings.json", ".idea/",
];
const ALLOWED_TMP_PREFIXES: &[&str] = &[
"/tmp/sage/",
"/tmp/sage-agent/",
"/private/tmp/sage/",
"/private/tmp/sage-agent/",
];
3.2 路径检查逻辑
impl PathPolicy {
pub fn check_path(&self, path: &Path, write: bool) -> SandboxResult<()> {
if self.is_sensitive_file(path) && self.strict_sensitive_files {
return Err(SandboxError::PathAccessDenied {
path: path.to_string_lossy().into(),
});
}
if write {
if self.is_temp_path(path) && !self.is_allowed_temp_path(path) {
return Err(SandboxError::PathAccessDenied {
path: path.to_string_lossy().into(),
});
}
if !self.is_write_allowed(path) {
return Err(SandboxError::PathAccessDenied {
path: path.to_string_lossy().into(),
});
}
} else {
if !self.allow_all_reads && !self.is_read_allowed(path) {
return Err(SandboxError::PathAccessDenied {
path: path.to_string_lossy().into(),
});
}
}
Ok(())
}
}
四、OS 级沙箱
4.1 macOS sandbox-exec
pub fn apply_sandbox_exec(
cmd: &mut Command,
config: &OsSandboxConfig
) -> Result<(), SandboxError> {
let profile = generate_sandbox_profile(config);
let profile_path = save_profile(&profile)?;
let original_program = cmd.as_std().get_program().to_owned();
let original_args: Vec<_> = cmd.as_std().get_args().map(|s| s.to_owned()).collect();
cmd.args([
"-f".as_ref(),
profile_path.as_os_str(),
original_program.as_os_str(),
]);
cmd.args(&original_args);
*cmd = Command::new("/usr/bin/sandbox-exec");
Ok(())
}
fn generate_sandbox_profile(config: &OsSandboxConfig) -> String {
let mut profile = String::new();
profile.push_str("(version 1)\n");
match config.mode {
OsSandboxMode::Strict => {
profile.push_str("(deny default)\n");
profile.push_str("(allow process-exec)\n");
}
OsSandboxMode::Normal => {
profile.push_str("(deny default)\n");
profile.push_str("(allow process*)\n");
profile.push_str("(allow file-read*)\n");
}
_ => {}
}
profile
}
4.2 Linux seccomp (未来)
pub fn apply_seccomp(
cmd: &mut Command,
config: &OsSandboxConfig
) -> Result<(), SandboxError> {
tracing::warn!("Linux seccomp sandbox not yet implemented");
Ok(())
}
4.3 OS 沙箱可用性检查
pub fn is_os_sandbox_available() -> bool {
#[cfg(target_os = "macos")]
{
std::path::Path::new("/usr/bin/sandbox-exec").exists()
}
#[cfg(target_os = "linux")]
{
std::path::Path::new("/proc/sys/kernel/seccomp").exists()
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{
false
}
}
pub fn os_sandbox_name() -> &'static str {
#[cfg(target_os = "macos")]
{ "sandbox-exec (macOS)" }
#[cfg(target_os = "linux")]
{ "seccomp (Linux)" }
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{ "none" }
}
五、违规追踪
5.1 违规类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ViolationType {
HeredocInjection,
ShellMetacharacterAbuse,
VariableInjection,
DangerousPattern,
CriticalPathRemoval,
SensitiveFileAccess,
PathAccessDenied,
CommandBlocked,
DisallowedTempWrite,
NetworkViolation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ViolationSeverity {
Low,
Medium,
High,
Critical,
}
5.2 违规存储
pub struct ViolationStore {
violations: RwLock<Vec<Violation>>,
max_violations: usize,
}
impl ViolationStore {
pub fn record(&self, violation: Violation) {
let mut violations = self.violations.write();
if violations.len() >= self.max_violations {
violations.remove(0);
}
violations.push(violation);
}
pub fn count_by_type(&self, violation_type: ViolationType) -> usize {
self.violations.read()
.iter()
.filter(|v| v.violation_type == violation_type)
.count()
}
pub fn get_high_severity(&self) -> Vec<Violation> {
self.violations.read()
.iter()
.filter(|v| v.severity >= ViolationSeverity::High)
.cloned()
.collect()
}
}
六、沙箱配置
6.1 配置结构
#[derive(Debug, Clone)]
pub struct SandboxConfig {
pub enabled: bool,
pub mode: SandboxMode,
pub working_dir: Option<PathBuf>,
pub allowed_read_paths: Vec<PathBuf>,
pub allowed_write_paths: Vec<PathBuf>,
pub allowed_commands: Vec<String>,
pub blocked_commands: Vec<String>,
pub allow_network: bool,
pub timeout: Duration,
pub limits: ResourceLimits,
}
#[derive(Debug, Clone, Copy)]
pub enum SandboxMode {
Permissive,
Normal,
Restricted,
Strict,
}
6.2 预设配置
impl SandboxConfig {
pub fn permissive() -> Self {
Self {
enabled: true,
mode: SandboxMode::Permissive,
allowed_read_paths: vec![PathBuf::from("/")],
allowed_write_paths: vec![],
allow_network: true,
..Default::default()
}
}
pub fn strict(working_dir: PathBuf) -> Self {
Self {
enabled: true,
mode: SandboxMode::Strict,
working_dir: Some(working_dir.clone()),
allowed_read_paths: vec![working_dir.clone()],
allowed_write_paths: vec![working_dir],
allow_network: false,
..Default::default()
}
}
}
6.3 资源限制
#[derive(Debug, Clone)]
pub struct ResourceLimits {
pub max_memory_bytes: Option<u64>,
pub max_cpu_seconds: Option<u64>,
pub max_output_bytes: Option<u64>,
pub max_file_size_bytes: Option<u64>,
pub max_processes: Option<u32>,
pub max_open_files: Option<u32>,
}
七、构建器模式
pub struct SandboxBuilder {
config: SandboxConfig,
}
impl SandboxBuilder {
pub fn new() -> Self {
Self { config: SandboxConfig::default() }
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.config.enabled = enabled;
self
}
pub fn mode(mut self, mode: SandboxMode) -> Self {
self.config.mode = mode;
self
}
pub fn working_dir(mut self, path: impl Into<PathBuf>) -> Self {
self.config.working_dir = Some(path.into());
self
}
pub fn allow_read(mut self, path: impl Into<PathBuf>) -> Self {
self.config.allowed_read_paths.push(path.into());
self
}
pub fn allow_write(mut self, path: impl Into<PathBuf>) -> Self {
self.config.allowed_write_paths.push(path.into());
self
}
pub fn allow_command(mut self, cmd: impl Into<String>) -> Self {
self.config.allowed_commands.push(cmd.into());
self
}
pub fn block_command(mut self, cmd: impl Into<String>) -> Self {
self.config.blocked_commands.push(cmd.into());
self
}
pub fn memory_limit(mut self, bytes: u64) -> Self {
self.config.limits.max_memory_bytes = Some(bytes);
self
}
pub fn timeout(mut self, duration: Duration) -> Self {
self.config.timeout = duration;
self
}
pub fn build(self) -> SandboxResult<DefaultSandbox> {
DefaultSandbox::new(self.config)
}
}
let sandbox = SandboxBuilder::new()
.enabled(true)
.mode(SandboxMode::Restricted)
.working_dir("/tmp/sage/work")
.allow_read("/tmp")
.allow_write("/tmp/sage")
.allow_command("ls")
.block_command("rm -rf")
.timeout(Duration::from_secs(30))
.memory_limit(100 * 1024 * 1024)
.build()?;
八、错误类型
#[derive(Debug, Clone, thiserror::Error)]
pub enum SandboxError {
#[error("Resource limit exceeded: {resource} ({current}/{limit})")]
ResourceLimitExceeded { resource: String, current: u64, limit: u64 },
#[error("Path access denied: {path}")]
PathAccessDenied { path: String },
#[error("Command not allowed: {command}")]
CommandNotAllowed { command: String },
#[error("Network access denied: {host}")]
NetworkAccessDenied { host: String },
#[error("Sandbox execution timeout after {0:?}")]
Timeout(Duration),
#[error("Sandbox initialization failed: {0}")]
InitializationFailed(String),
#[error("Failed to spawn sandboxed process: {0}")]
SpawnFailed(String),
#[error("Invalid sandbox configuration: {0}")]
InvalidConfig(String),
#[error("Sandbox permission denied: {0}")]
PermissionDenied(String),
#[error("Sandbox internal error: {0}")]
Internal(String),
}
九、开发指南
9.1 添加新验证检查
pub fn check_new_pattern(command: &str) -> ValidationResult {
ValidationResult::pass(CheckType::NewCheck)
}
mod new_check;
pub use new_check::check_new_pattern;
pub fn validate_command(command: &str, context: &ValidationContext) -> ValidationResult {
let checks = [
check_new_pattern(command),
];
}
9.2 添加敏感文件
在 path_policy.rs 的 SENSITIVE_FILES 添加:
const SENSITIVE_FILES: &[&str] = &[
".new_sensitive_file",
];
9.3 安全最佳实践
- 默认拒绝: 使用白名单而非黑名单
- 最小权限: 只授予必要的权限
- 深度防御: 多层安全检查
- 审计日志: 记录所有违规
- 失败安全: 出错时选择安全选项
十、相关模块
sage-tool-development - 工具开发(使用沙箱执行)
sage-agent-execution - Agent 执行(沙箱集成)
sage-recovery-patterns - 恢复模式(违规处理)