mirror of
https://github.com/openai/codex.git
synced 2026-09-11 20:36:49 +00:00
fix: tolerate windows trust path aliases
Co-authored-by: Codex noreply@openai.com
This commit is contained in:
@@ -85,11 +85,11 @@ use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_rmcp_client::OAuthCredentialsStoreMode;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_absolute_path::AbsolutePathBufGuard;
|
||||
use dunce::canonicalize as normalize_path;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Deserializer;
|
||||
use serde::Serialize;
|
||||
use similar::DiffableStr;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind;
|
||||
@@ -1696,25 +1696,56 @@ impl ConfigToml {
|
||||
/// Resolves the cwd to an existing project, or returns None if ConfigToml
|
||||
/// does not contain a project corresponding to cwd or a git repo for cwd
|
||||
pub fn get_active_project(&self, resolved_cwd: &Path) -> Option<ProjectConfig> {
|
||||
let projects = self.projects.clone().unwrap_or_default();
|
||||
let mut projects = HashMap::new();
|
||||
for (key, project_config) in self.projects.clone().unwrap_or_default() {
|
||||
for normalized_key in Self::normalized_project_lookup_keys(Path::new(&key)) {
|
||||
projects.insert(normalized_key, project_config.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(project_config) = projects.get(&resolved_cwd.to_string_lossy().to_string()) {
|
||||
return Some(project_config.clone());
|
||||
for normalized_cwd in Self::normalized_project_lookup_keys(resolved_cwd) {
|
||||
if let Some(project_config) = projects.get(&normalized_cwd) {
|
||||
return Some(project_config.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// If cwd lives inside a git repo/worktree, check whether the root git project
|
||||
// (the primary repository working directory) is trusted. This lets
|
||||
// worktrees inherit trust from the main project.
|
||||
if let Some(repo_root) = resolve_root_git_project_for_trust(resolved_cwd)
|
||||
&& let Some(project_config_for_root) =
|
||||
projects.get(&repo_root.to_string_lossy().to_string_lossy().to_string())
|
||||
{
|
||||
return Some(project_config_for_root.clone());
|
||||
if let Some(repo_root) = resolve_root_git_project_for_trust(resolved_cwd) {
|
||||
for normalized_repo_root in Self::normalized_project_lookup_keys(&repo_root) {
|
||||
if let Some(project_config_for_root) = projects.get(&normalized_repo_root) {
|
||||
return Some(project_config_for_root.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn normalized_project_lookup_keys(path: &Path) -> Vec<String> {
|
||||
let normalized_path =
|
||||
Self::normalize_project_lookup_key_string(path.to_string_lossy().to_string());
|
||||
let normalized_canonical_path = Self::normalize_project_lookup_key_string(
|
||||
normalize_path(path)
|
||||
.unwrap_or_else(|_| path.to_path_buf())
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
if normalized_path == normalized_canonical_path {
|
||||
vec![normalized_path]
|
||||
} else {
|
||||
vec![normalized_path, normalized_canonical_path]
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_project_lookup_key_string(key: String) -> String {
|
||||
if cfg!(windows) {
|
||||
key.to_ascii_lowercase()
|
||||
} else {
|
||||
key
|
||||
}
|
||||
}
|
||||
pub fn get_config_profile(
|
||||
&self,
|
||||
override_profile: Option<String>,
|
||||
|
||||
@@ -536,7 +536,9 @@ async fn load_requirements_from_legacy_scheme(
|
||||
struct ProjectTrustContext {
|
||||
project_root: AbsolutePathBuf,
|
||||
project_root_key: String,
|
||||
project_root_lookup_keys: Vec<String>,
|
||||
repo_root_key: Option<String>,
|
||||
repo_root_lookup_keys: Option<Vec<String>>,
|
||||
projects_trust: std::collections::HashMap<String, TrustLevel>,
|
||||
user_config_file: AbsolutePathBuf,
|
||||
}
|
||||
@@ -559,28 +561,33 @@ impl ProjectTrustDecision {
|
||||
|
||||
impl ProjectTrustContext {
|
||||
fn decision_for_dir(&self, dir: &AbsolutePathBuf) -> ProjectTrustDecision {
|
||||
let dir_key = dir.as_path().to_string_lossy().to_string();
|
||||
if let Some(trust_level) = self.projects_trust.get(&dir_key).copied() {
|
||||
return ProjectTrustDecision {
|
||||
trust_level: Some(trust_level),
|
||||
trust_key: dir_key,
|
||||
};
|
||||
for dir_key in normalized_project_trust_keys(dir.as_path()) {
|
||||
if let Some(trust_level) = self.projects_trust.get(&dir_key).copied() {
|
||||
return ProjectTrustDecision {
|
||||
trust_level: Some(trust_level),
|
||||
trust_key: dir_key,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(trust_level) = self.projects_trust.get(&self.project_root_key).copied() {
|
||||
return ProjectTrustDecision {
|
||||
trust_level: Some(trust_level),
|
||||
trust_key: self.project_root_key.clone(),
|
||||
};
|
||||
for project_root_key in &self.project_root_lookup_keys {
|
||||
if let Some(trust_level) = self.projects_trust.get(project_root_key).copied() {
|
||||
return ProjectTrustDecision {
|
||||
trust_level: Some(trust_level),
|
||||
trust_key: project_root_key.clone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(repo_root_key) = self.repo_root_key.as_ref()
|
||||
&& let Some(trust_level) = self.projects_trust.get(repo_root_key).copied()
|
||||
{
|
||||
return ProjectTrustDecision {
|
||||
trust_level: Some(trust_level),
|
||||
trust_key: repo_root_key.clone(),
|
||||
};
|
||||
if let Some(repo_root_lookup_keys) = self.repo_root_lookup_keys.as_ref() {
|
||||
for repo_root_key in repo_root_lookup_keys {
|
||||
if let Some(trust_level) = self.projects_trust.get(repo_root_key).copied() {
|
||||
return ProjectTrustDecision {
|
||||
trust_level: Some(trust_level),
|
||||
trust_key: repo_root_key.clone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProjectTrustDecision {
|
||||
@@ -645,26 +652,84 @@ async fn project_trust_context(
|
||||
let project_root = find_project_root(cwd, project_root_markers).await?;
|
||||
let projects = project_trust_config.projects.unwrap_or_default();
|
||||
|
||||
let project_root_key = project_root.as_path().to_string_lossy().to_string();
|
||||
let project_root_lookup_keys = normalized_project_trust_keys(project_root.as_path());
|
||||
let project_root_key = project_root_lookup_keys
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| normalized_project_trust_key(project_root.as_path()));
|
||||
let repo_root = resolve_root_git_project_for_trust(cwd.as_path());
|
||||
let repo_root_key = repo_root
|
||||
let repo_root_lookup_keys = repo_root
|
||||
.as_ref()
|
||||
.map(|root| root.to_string_lossy().to_string());
|
||||
.map(|root| normalized_project_trust_keys(root));
|
||||
let repo_root_key = repo_root_lookup_keys
|
||||
.as_ref()
|
||||
.and_then(|keys| keys.first().cloned());
|
||||
|
||||
let projects_trust = projects
|
||||
.into_iter()
|
||||
.filter_map(|(key, project)| project.trust_level.map(|trust_level| (key, trust_level)))
|
||||
.flat_map(|(key, project)| {
|
||||
project
|
||||
.trust_level
|
||||
.into_iter()
|
||||
.flat_map(move |trust_level| {
|
||||
normalized_project_trust_key_strs(&key)
|
||||
.into_iter()
|
||||
.map(move |normalized_key| (normalized_key, trust_level))
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(ProjectTrustContext {
|
||||
project_root,
|
||||
project_root_key,
|
||||
project_root_lookup_keys,
|
||||
repo_root_key,
|
||||
repo_root_lookup_keys,
|
||||
projects_trust,
|
||||
user_config_file: user_config_file.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn normalized_project_trust_key(path: &Path) -> String {
|
||||
normalized_project_trust_keys(path)
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| normalize_project_trust_lookup_key(path.to_string_lossy().to_string()))
|
||||
}
|
||||
|
||||
fn normalized_project_trust_keys(path: &Path) -> Vec<String> {
|
||||
let normalized_path = normalize_project_trust_lookup_key(path.to_string_lossy().to_string());
|
||||
let normalized_canonical_path = normalize_project_trust_lookup_key(
|
||||
normalize_path(path)
|
||||
.unwrap_or_else(|_| path.to_path_buf())
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
if normalized_path == normalized_canonical_path {
|
||||
vec![normalized_path]
|
||||
} else {
|
||||
vec![normalized_path, normalized_canonical_path]
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized_project_trust_key_strs(path: &str) -> Vec<String> {
|
||||
let path = Path::new(path);
|
||||
if path.is_absolute() {
|
||||
normalized_project_trust_keys(path)
|
||||
} else {
|
||||
vec![normalize_project_trust_lookup_key(
|
||||
path.to_string_lossy().to_string(),
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_project_trust_lookup_key(key: String) -> String {
|
||||
if cfg!(windows) {
|
||||
key.to_ascii_lowercase()
|
||||
} else {
|
||||
key
|
||||
}
|
||||
}
|
||||
/// Takes a `toml::Value` parsed from a config.toml file and walks through it,
|
||||
/// resolving any `AbsolutePathBuf` fields against `base_dir`, returning a new
|
||||
/// `toml::Value` with the same shape but with paths resolved.
|
||||
|
||||
Reference in New Issue
Block a user