mirror of
https://github.com/openai/codex.git
synced 2026-11-14 21:40:39 +00:00
[codex] support home-relative host executable paths
This commit is contained in:
@@ -37,6 +37,16 @@ host_executable(
|
||||
)
|
||||
```
|
||||
|
||||
- Paths may use `~/...` to identify executables installed beneath the current
|
||||
user's home directory without hard-coding a username:
|
||||
|
||||
```starlark
|
||||
host_executable(
|
||||
name = "az",
|
||||
paths = ["~/.openai/bin/az"],
|
||||
)
|
||||
```
|
||||
|
||||
- Matching semantics:
|
||||
- execpolicy always tries exact first-token matches first.
|
||||
- With host-executable resolution disabled, `/usr/bin/git status` only matches a rule whose first token is `/usr/bin/git`.
|
||||
|
||||
@@ -219,15 +219,13 @@ fn parse_examples<'v>(examples: UnpackList<Value<'v>>) -> Result<Vec<Vec<String>
|
||||
examples.items.into_iter().map(parse_example).collect()
|
||||
}
|
||||
|
||||
fn parse_literal_absolute_path(raw: &str) -> Result<AbsolutePathBuf> {
|
||||
if !Path::new(raw).is_absolute() {
|
||||
return Err(Error::InvalidRule(format!(
|
||||
"host_executable paths must be absolute (got {raw})"
|
||||
)));
|
||||
}
|
||||
|
||||
AbsolutePathBuf::try_from(raw.to_string())
|
||||
.map_err(|error| Error::InvalidRule(format!("invalid absolute path `{raw}`: {error}")))
|
||||
fn parse_host_executable_path(raw: &str) -> Result<AbsolutePathBuf> {
|
||||
AbsolutePathBuf::from_absolute_path_checked(raw).map_err(|error| {
|
||||
Error::InvalidRule(format!(
|
||||
"host_executable paths must be absolute or use `~/...` home-relative syntax \
|
||||
(got {raw}): {error}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_host_executable_name(name: &str) -> Result<()> {
|
||||
@@ -448,7 +446,7 @@ fn policy_builtins(builder: &mut GlobalsBuilder) {
|
||||
value.get_type()
|
||||
))
|
||||
})?;
|
||||
let path = parse_literal_absolute_path(raw)?;
|
||||
let path = parse_host_executable_path(raw)?;
|
||||
let Some(path_name) = executable_path_lookup_key(path.as_path()) else {
|
||||
return Err(Error::InvalidRule(format!(
|
||||
"host_executable path `{raw}` must have basename `{name}`"
|
||||
|
||||
@@ -695,6 +695,56 @@ host_executable(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn home_relative_host_executable_path_is_expanded_and_used_for_resolution() -> Result<()> {
|
||||
let executable_name = host_executable_name("az");
|
||||
let home_relative_path = format!("~/.openai/bin/{executable_name}");
|
||||
let expected_path = AbsolutePathBuf::from_absolute_path_checked(&home_relative_path)?;
|
||||
let policy_src = format!(
|
||||
r#"
|
||||
prefix_rule(pattern = ["az", "account", "show"], decision = "prompt")
|
||||
host_executable(name = "az", paths = ["{home_relative_path}"])
|
||||
"#
|
||||
);
|
||||
let mut parser = PolicyParser::new();
|
||||
parser.parse("test.rules", &policy_src)?;
|
||||
let policy = parser.build();
|
||||
|
||||
assert_eq!(
|
||||
policy
|
||||
.host_executables()
|
||||
.get("az")
|
||||
.expect("missing az host executable")
|
||||
.as_ref(),
|
||||
[expected_path.clone()]
|
||||
);
|
||||
|
||||
let evaluation = policy.check_with_options(
|
||||
&[
|
||||
expected_path.to_string_lossy().into_owned(),
|
||||
"account".to_string(),
|
||||
"show".to_string(),
|
||||
],
|
||||
&allow_all,
|
||||
&MatchOptions {
|
||||
resolve_host_executables: true,
|
||||
},
|
||||
);
|
||||
assert_eq!(
|
||||
evaluation,
|
||||
Evaluation {
|
||||
decision: Decision::Prompt,
|
||||
matched_rules: vec![RuleMatch::PrefixRuleMatch {
|
||||
matched_prefix: tokens(&["az", "account", "show"]),
|
||||
decision: Decision::Prompt,
|
||||
resolved_program: Some(expected_path),
|
||||
justification: None,
|
||||
}],
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_executable_rejects_non_absolute_path() {
|
||||
let policy_src = r#"
|
||||
@@ -710,6 +760,21 @@ host_executable(name = "git", paths = ["git"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_executable_rejects_named_user_home_path() {
|
||||
let policy_src = r#"
|
||||
host_executable(name = "git", paths = ["~someone/bin/git"])
|
||||
"#;
|
||||
let mut parser = PolicyParser::new();
|
||||
let err = parser
|
||||
.parse("test.rules", policy_src)
|
||||
.expect_err("expected parse error");
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("host_executable paths must be absolute")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_executable_rejects_name_with_path_separator() {
|
||||
let git_path = host_absolute_path(&["usr", "bin", "git"]);
|
||||
|
||||
Reference in New Issue
Block a user