mirror of
https://github.com/openai/codex.git
synced 2026-11-02 00:18:52 +00:00
fix: trust-gate project hooks and exec policies (#14718)
## Summary - trust-gate project `.codex` layers consistently, including repos that have `.codex/hooks.json` or `.codex/execpolicy/*.rules` but no `.codex/config.toml` - keep disabled project layers in the config stack so nested trusted project layers still resolve correctly, while preventing hooks and exec policies from loading until the project is trusted - update app-server/TUI onboarding copy to make the trust boundary explicit and add regressions for loader, hooks, exec-policy, and onboarding coverage ## Security Before this change, an untrusted repo could auto-load project hooks or exec policies from `.codex/` as long as `config.toml` was absent. This makes trust the single gate for project-local config, hooks, and exec policies. ## Stack - Parent of #15936 ## Test - cargo test -p codex-core without_config_toml --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -684,25 +684,21 @@ impl ConfigToml {
|
||||
resolved_cwd: &Path,
|
||||
repo_root: Option<&Path>,
|
||||
) -> Option<ProjectConfig> {
|
||||
let projects = self.projects.clone().unwrap_or_default();
|
||||
let projects = self.projects.as_ref()?;
|
||||
|
||||
let resolved_cwd_key = project_trust_key(resolved_cwd);
|
||||
let resolved_cwd_raw_key = resolved_cwd.to_string_lossy().to_string();
|
||||
if let Some(project_config) = projects
|
||||
.get(&resolved_cwd_key)
|
||||
.or_else(|| projects.get(&resolved_cwd_raw_key))
|
||||
{
|
||||
return Some(project_config.clone());
|
||||
for normalized_cwd in normalized_project_lookup_keys(resolved_cwd) {
|
||||
if let Some(project_config) = project_config_for_lookup_key(projects, &normalized_cwd) {
|
||||
return Some(project_config);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(repo_root) = repo_root {
|
||||
let repo_root_key = project_trust_key(repo_root);
|
||||
let repo_root_raw_key = repo_root.to_string_lossy().to_string();
|
||||
if let Some(project_config_for_root) = projects
|
||||
.get(&repo_root_key)
|
||||
.or_else(|| projects.get(&repo_root_raw_key))
|
||||
{
|
||||
return Some(project_config_for_root.clone());
|
||||
for normalized_repo_root in normalized_project_lookup_keys(repo_root) {
|
||||
if let Some(project_config_for_root) =
|
||||
project_config_for_lookup_key(projects, &normalized_repo_root)
|
||||
{
|
||||
return Some(project_config_for_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,11 +730,45 @@ impl ConfigToml {
|
||||
/// Canonicalize the path and convert it to a string to be used as a key in the
|
||||
/// projects trust map. On Windows, strips UNC, when possible, to try to ensure
|
||||
/// that different paths that point to the same location have the same key.
|
||||
fn project_trust_key(project_path: &Path) -> String {
|
||||
normalize_for_path_comparison(project_path)
|
||||
.unwrap_or_else(|_| project_path.to_path_buf())
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
fn normalized_project_lookup_keys(path: &Path) -> Vec<String> {
|
||||
let normalized_path = normalize_project_lookup_key(path.to_string_lossy().to_string());
|
||||
let normalized_canonical_path = normalize_project_lookup_key(
|
||||
normalize_for_path_comparison(path)
|
||||
.unwrap_or_else(|_| path.to_path_buf())
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
if normalized_path == normalized_canonical_path {
|
||||
vec![normalized_canonical_path]
|
||||
} else {
|
||||
vec![normalized_canonical_path, normalized_path]
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_project_lookup_key(key: String) -> String {
|
||||
if cfg!(windows) {
|
||||
key.to_ascii_lowercase()
|
||||
} else {
|
||||
key
|
||||
}
|
||||
}
|
||||
|
||||
fn project_config_for_lookup_key(
|
||||
projects: &HashMap<String, ProjectConfig>,
|
||||
lookup_key: &str,
|
||||
) -> Option<ProjectConfig> {
|
||||
if let Some(project_config) = projects.get(lookup_key) {
|
||||
return Some(project_config.clone());
|
||||
}
|
||||
|
||||
let mut normalized_matches: Vec<_> = projects
|
||||
.iter()
|
||||
.filter(|(key, _)| normalize_project_lookup_key((*key).clone()) == lookup_key)
|
||||
.collect();
|
||||
normalized_matches.sort_by(|(left, _), (right, _)| left.cmp(right));
|
||||
normalized_matches
|
||||
.first()
|
||||
.map(|(_, project_config)| (**project_config).clone())
|
||||
}
|
||||
|
||||
pub fn validate_reserved_model_provider_ids(
|
||||
|
||||
Reference in New Issue
Block a user