mirror of
https://github.com/openai/codex.git
synced 2026-04-28 02:11:08 +03:00
changes
This commit is contained in:
@@ -29,6 +29,8 @@ use anyhow::Context as _;
|
||||
#[cfg(unix)]
|
||||
use codex_utils_pty::process_group::kill_child_process_group;
|
||||
#[cfg(unix)]
|
||||
use dunce::canonicalize as canonicalize_path;
|
||||
#[cfg(unix)]
|
||||
use serde::Deserialize;
|
||||
#[cfg(unix)]
|
||||
use serde::Serialize;
|
||||
@@ -37,6 +39,10 @@ use std::io::Read;
|
||||
#[cfg(unix)]
|
||||
use std::io::Write;
|
||||
#[cfg(unix)]
|
||||
use std::path::Component;
|
||||
#[cfg(unix)]
|
||||
use std::path::Path;
|
||||
#[cfg(unix)]
|
||||
use std::time::Instant;
|
||||
#[cfg(unix)]
|
||||
use tokio::io::AsyncReadExt;
|
||||
@@ -354,11 +360,12 @@ impl ZshExecBridge {
|
||||
} else {
|
||||
argv.clone()
|
||||
};
|
||||
let normalized_exec_program = normalize_wrapper_executable_for_skill_matching(&file, &cwd);
|
||||
|
||||
// Check exec policy against the resolved executable path, not the original argv[0].
|
||||
let mut command_for_execpolicy = command_for_approval.clone();
|
||||
if let Some(program) = command_for_execpolicy.first_mut() {
|
||||
*program = file.clone();
|
||||
*program = normalized_exec_program.clone();
|
||||
}
|
||||
|
||||
// Collect skill-provided approval overlays and any matching skill permission profile.
|
||||
@@ -367,9 +374,9 @@ impl ZshExecBridge {
|
||||
.skill_prefix_permissions
|
||||
.read()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let matched_skill_permissions = command_for_execpolicy
|
||||
.first()
|
||||
.and_then(|program| registry.get(std::slice::from_ref(program)).cloned());
|
||||
let matched_skill_permissions = registry
|
||||
.get(std::slice::from_ref(&normalized_exec_program))
|
||||
.cloned();
|
||||
let overlay_prompt_prefixes = registry.keys().cloned().collect::<Vec<_>>();
|
||||
(overlay_prompt_prefixes, matched_skill_permissions)
|
||||
};
|
||||
@@ -602,6 +609,40 @@ fn parse_wrapper_request_line(request_line: &str) -> Result<WrapperIpcRequest, T
|
||||
.map_err(|err| ToolError::Rejected(format!("parse wrapper request payload: {err}")))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn normalize_wrapper_executable_for_skill_matching(file: &str, cwd: &str) -> String {
|
||||
let file_path = Path::new(file);
|
||||
if !file_path.is_absolute() && !file.contains('/') {
|
||||
return file.to_string();
|
||||
}
|
||||
|
||||
let absolute = if file_path.is_absolute() {
|
||||
file_path.to_path_buf()
|
||||
} else {
|
||||
Path::new(cwd).join(file_path)
|
||||
};
|
||||
let normalized = normalize_lexically(&absolute);
|
||||
let canonicalized = canonicalize_path(&normalized).unwrap_or(normalized);
|
||||
canonicalized.to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn normalize_lexically(path: &Path) -> PathBuf {
|
||||
let mut normalized = PathBuf::new();
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
normalized.pop();
|
||||
}
|
||||
Component::RootDir | Component::Prefix(_) | Component::Normal(_) => {
|
||||
normalized.push(component.as_os_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
normalized
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn write_json_line<W: tokio::io::AsyncWrite + Unpin, T: Serialize>(
|
||||
writer: &mut W,
|
||||
@@ -624,6 +665,8 @@ async fn write_json_line<W: tokio::io::AsyncWrite + Unpin, T: Serialize>(
|
||||
#[cfg(all(test, unix))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn parse_wrapper_request_line_rejects_malformed_json() {
|
||||
@@ -633,4 +676,38 @@ mod tests {
|
||||
};
|
||||
assert!(message.starts_with("parse wrapper request payload:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_wrapper_executable_for_skill_matching_resolves_relative_paths_against_cwd() {
|
||||
let tempdir = tempfile::tempdir().expect("tempdir");
|
||||
let repo_root = tempdir.path().join("repo");
|
||||
let cwd = repo_root.join("codex-rs");
|
||||
let skill_script =
|
||||
repo_root.join(".agents/skills/sandbox-approval-demo/scripts/skill_action.sh");
|
||||
fs::create_dir_all(
|
||||
skill_script
|
||||
.parent()
|
||||
.expect("skill script should have a parent directory"),
|
||||
)
|
||||
.expect("create skill scripts dir");
|
||||
fs::create_dir_all(&cwd).expect("create cwd");
|
||||
fs::write(&skill_script, "#!/bin/sh\n").expect("write skill script");
|
||||
|
||||
let normalized = normalize_wrapper_executable_for_skill_matching(
|
||||
"../.agents/skills/sandbox-approval-demo/scripts/./skill_action.sh",
|
||||
&cwd.to_string_lossy(),
|
||||
);
|
||||
|
||||
let expected = dunce::canonicalize(&skill_script)
|
||||
.expect("canonicalize skill script")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
assert_eq!(normalized, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_wrapper_executable_for_skill_matching_keeps_bare_command_names() {
|
||||
let normalized = normalize_wrapper_executable_for_skill_matching("bash", "/tmp");
|
||||
assert_eq!(normalized, "bash");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user