mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
Merge 92a0893e4f into sapling-pr-archive-bolinfest
This commit is contained in:
Generated
+1
@@ -2321,6 +2321,7 @@ dependencies = [
|
||||
"codex-login",
|
||||
"codex-otel",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
"hmac",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
|
||||
@@ -17,6 +17,7 @@ codex-core = { workspace = true }
|
||||
codex-login = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
hmac = "0.12.1"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -25,11 +25,13 @@ use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::RefreshTokenError;
|
||||
use codex_protocol::account::PlanType;
|
||||
use codex_utils_absolute_path::AbsolutePathBufGuard;
|
||||
use hmac::Hmac;
|
||||
use hmac::Mac;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use sha2::Sha256;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
@@ -138,10 +140,12 @@ struct CloudRequirementsCacheSignedPayload {
|
||||
}
|
||||
|
||||
impl CloudRequirementsCacheSignedPayload {
|
||||
fn requirements(&self) -> Option<ConfigRequirementsToml> {
|
||||
self.contents
|
||||
.as_deref()
|
||||
.and_then(|contents| parse_cloud_requirements(contents).ok().flatten())
|
||||
fn requirements(&self, requirements_base_dir: &Path) -> Option<ConfigRequirementsToml> {
|
||||
self.contents.as_deref().and_then(|contents| {
|
||||
parse_cloud_requirements(contents, requirements_base_dir)
|
||||
.ok()
|
||||
.flatten()
|
||||
})
|
||||
}
|
||||
}
|
||||
fn sign_cache_payload(payload_bytes: &[u8]) -> Option<String> {
|
||||
@@ -258,6 +262,7 @@ impl RequirementsFetcher for BackendRequirementsFetcher {
|
||||
struct CloudRequirementsService {
|
||||
auth_manager: Arc<AuthManager>,
|
||||
fetcher: Arc<dyn RequirementsFetcher>,
|
||||
requirements_base_dir: PathBuf,
|
||||
cache_path: PathBuf,
|
||||
timeout: Duration,
|
||||
}
|
||||
@@ -272,6 +277,7 @@ impl CloudRequirementsService {
|
||||
Self {
|
||||
auth_manager,
|
||||
fetcher,
|
||||
requirements_base_dir: codex_home.clone(),
|
||||
cache_path: codex_home.join(CLOUD_REQUIREMENTS_CACHE_FILENAME),
|
||||
timeout,
|
||||
}
|
||||
@@ -351,7 +357,7 @@ impl CloudRequirementsService {
|
||||
path = %self.cache_path.display(),
|
||||
"Using cached cloud requirements"
|
||||
);
|
||||
return Ok(signed_payload.requirements());
|
||||
return Ok(signed_payload.requirements(&self.requirements_base_dir));
|
||||
}
|
||||
Err(cache_load_status) => {
|
||||
self.log_cache_load_status(&cache_load_status);
|
||||
@@ -482,24 +488,26 @@ impl CloudRequirementsService {
|
||||
};
|
||||
|
||||
let requirements = match contents.as_deref() {
|
||||
Some(contents) => match parse_cloud_requirements(contents) {
|
||||
Ok(requirements) => requirements,
|
||||
Err(err) => {
|
||||
tracing::error!(error = %err, "Failed to parse cloud requirements");
|
||||
emit_fetch_final_metric(
|
||||
trigger,
|
||||
"error",
|
||||
"parse_error",
|
||||
attempt,
|
||||
last_status_code,
|
||||
);
|
||||
return Err(CloudRequirementsLoadError::new(
|
||||
CloudRequirementsLoadErrorCode::Parse,
|
||||
/*status_code*/ None,
|
||||
format_cloud_requirements_parse_failed_message(contents, &err),
|
||||
));
|
||||
Some(contents) => {
|
||||
match parse_cloud_requirements(contents, &self.requirements_base_dir) {
|
||||
Ok(requirements) => requirements,
|
||||
Err(err) => {
|
||||
tracing::error!(error = %err, "Failed to parse cloud requirements");
|
||||
emit_fetch_final_metric(
|
||||
trigger,
|
||||
"error",
|
||||
"parse_error",
|
||||
attempt,
|
||||
last_status_code,
|
||||
);
|
||||
return Err(CloudRequirementsLoadError::new(
|
||||
CloudRequirementsLoadErrorCode::Parse,
|
||||
/*status_code*/ None,
|
||||
format_cloud_requirements_parse_failed_message(contents, &err),
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -738,11 +746,13 @@ pub async fn cloud_requirements_loader_for_storage(
|
||||
|
||||
fn parse_cloud_requirements(
|
||||
contents: &str,
|
||||
requirements_base_dir: &Path,
|
||||
) -> Result<Option<ConfigRequirementsToml>, toml::de::Error> {
|
||||
if contents.trim().is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let _guard = AbsolutePathBufGuard::new(requirements_base_dir);
|
||||
let requirements: ConfigRequirementsToml = toml::from_str(contents)?;
|
||||
if requirements.is_empty() {
|
||||
Ok(None)
|
||||
@@ -1038,7 +1048,11 @@ mod tests {
|
||||
}
|
||||
|
||||
fn parse_for_fetch(contents: Option<&str>) -> Option<ConfigRequirementsToml> {
|
||||
contents.and_then(|contents| parse_cloud_requirements(contents).ok().flatten())
|
||||
contents.and_then(|contents| {
|
||||
parse_cloud_requirements(contents, &std::env::temp_dir())
|
||||
.ok()
|
||||
.flatten()
|
||||
})
|
||||
}
|
||||
|
||||
fn request_error() -> FetchAttemptError {
|
||||
@@ -1391,6 +1405,35 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_cloud_requirements_resolves_relative_deny_read_globs_from_codex_home() {
|
||||
let codex_home = tempdir().expect("tempdir");
|
||||
let service = CloudRequirementsService::new(
|
||||
auth_manager_with_plan("enterprise").await,
|
||||
Arc::new(StaticFetcher {
|
||||
contents: Some(
|
||||
r#"
|
||||
[permissions.filesystem]
|
||||
deny_read = ["./sensitive/**/*.txt"]
|
||||
"#
|
||||
.to_string(),
|
||||
),
|
||||
}),
|
||||
codex_home.path().to_path_buf(),
|
||||
CLOUD_REQUIREMENTS_TIMEOUT,
|
||||
);
|
||||
let deny_read = format!("{}/sensitive/**/*.txt", codex_home.path().display());
|
||||
let expected = toml::from_str::<ConfigRequirementsToml>(&format!(
|
||||
r#"
|
||||
[permissions.filesystem]
|
||||
deny_read = [{deny_read:?}]
|
||||
"#
|
||||
))
|
||||
.expect("parse expected cloud requirements");
|
||||
|
||||
assert_eq!(service.fetch().await, Ok(Some(expected)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_cloud_requirements_parses_apps_requirements_toml() {
|
||||
let result = parse_for_fetch(Some(
|
||||
@@ -2234,7 +2277,11 @@ command = "sample-mcp"
|
||||
.signed_payload
|
||||
.contents
|
||||
.as_deref()
|
||||
.and_then(|contents| parse_cloud_requirements(contents).ok().flatten()),
|
||||
.and_then(|contents| {
|
||||
parse_cloud_requirements(contents, codex_home.path())
|
||||
.ok()
|
||||
.flatten()
|
||||
}),
|
||||
Some(ConfigRequirementsToml {
|
||||
allowed_approval_policies: Some(vec![AskForApproval::Never]),
|
||||
allowed_approvals_reviewers: None,
|
||||
@@ -2358,7 +2405,11 @@ command = "sample-mcp"
|
||||
.signed_payload
|
||||
.contents
|
||||
.as_deref()
|
||||
.and_then(|contents| parse_cloud_requirements(contents).ok().flatten()),
|
||||
.and_then(|contents| {
|
||||
parse_cloud_requirements(contents, codex_home.path())
|
||||
.ok()
|
||||
.flatten()
|
||||
}),
|
||||
Some(ConfigRequirementsToml {
|
||||
allowed_approval_policies: Some(vec![AskForApproval::OnRequest]),
|
||||
allowed_approvals_reviewers: None,
|
||||
|
||||
@@ -16,8 +16,8 @@ use crate::logging::log_note;
|
||||
use crate::sandbox_bin_dir;
|
||||
|
||||
const DEV_BUILD_VERSION_SENTINEL: &str = "0.0.0";
|
||||
const BIN_DIRNAME: &str = "bin";
|
||||
const RESOURCES_DIRNAME: &str = "codex-resources";
|
||||
pub(crate) const BIN_DIRNAME: &str = "bin";
|
||||
pub(crate) const RESOURCES_DIRNAME: &str = "codex-resources";
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub(crate) enum HelperExecutable {
|
||||
@@ -52,7 +52,7 @@ pub(crate) fn helper_bin_dir(codex_home: &Path) -> PathBuf {
|
||||
|
||||
pub(crate) fn legacy_lookup(kind: HelperExecutable) -> PathBuf {
|
||||
if let Ok(exe) = std::env::current_exe()
|
||||
&& let Some(candidate) = source_path_for_exe(&exe, kind.file_name())
|
||||
&& let Some(candidate) = bundled_executable_path_for_exe(&exe, kind.file_name())
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
@@ -179,7 +179,7 @@ fn store_helper_path(cache_key: String, path: PathBuf) {
|
||||
|
||||
fn sibling_source_path(kind: HelperExecutable) -> Result<PathBuf> {
|
||||
let exe = std::env::current_exe().context("resolve current executable for helper lookup")?;
|
||||
source_path_for_exe(&exe, kind.file_name()).ok_or_else(|| {
|
||||
bundled_executable_path_for_exe(&exe, kind.file_name()).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"helper not found next to current executable or under {RESOURCES_DIRNAME}: {}",
|
||||
exe.display()
|
||||
@@ -187,7 +187,7 @@ fn sibling_source_path(kind: HelperExecutable) -> Result<PathBuf> {
|
||||
})
|
||||
}
|
||||
|
||||
fn source_path_for_exe(exe: &Path, file_name: &str) -> Option<PathBuf> {
|
||||
pub(crate) fn bundled_executable_path_for_exe(exe: &Path, file_name: &str) -> Option<PathBuf> {
|
||||
let dir = exe.parent()?;
|
||||
let direct_candidate = dir.join(file_name);
|
||||
if direct_candidate.is_file() {
|
||||
@@ -361,13 +361,13 @@ mod tests {
|
||||
use super::DEV_BUILD_VERSION_SENTINEL;
|
||||
use super::HelperExecutable;
|
||||
use super::RESOURCES_DIRNAME;
|
||||
use super::bundled_executable_path_for_exe;
|
||||
use super::copy_from_source_if_needed;
|
||||
use super::destination_is_fresh;
|
||||
use super::dev_build_suffix;
|
||||
use super::helper_bin_dir;
|
||||
use super::helper_version_suffix;
|
||||
use super::materialized_file_name;
|
||||
use super::source_path_for_exe;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
@@ -469,8 +469,9 @@ mod tests {
|
||||
fs::write(&exe, b"codex").expect("write exe");
|
||||
fs::write(&helper, b"runner").expect("write helper");
|
||||
|
||||
let resolved = source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
let resolved =
|
||||
bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
|
||||
assert_eq!(resolved, helper);
|
||||
}
|
||||
@@ -488,8 +489,9 @@ mod tests {
|
||||
fs::write(&exe, b"codex").expect("write exe");
|
||||
fs::write(&helper, b"runner").expect("write helper");
|
||||
|
||||
let resolved = source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
let resolved =
|
||||
bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
|
||||
assert_eq!(resolved, helper);
|
||||
}
|
||||
@@ -510,8 +512,9 @@ mod tests {
|
||||
fs::write(&package_helper, b"package runner").expect("write package helper");
|
||||
fs::write(&bin_helper, b"bin runner").expect("write bin helper");
|
||||
|
||||
let resolved = source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
let resolved =
|
||||
bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
|
||||
assert_eq!(resolved, package_helper);
|
||||
}
|
||||
@@ -529,8 +532,9 @@ mod tests {
|
||||
fs::write(&sibling_helper, b"sibling runner").expect("write sibling helper");
|
||||
fs::write(&resource_helper, b"resource runner").expect("write resource helper");
|
||||
|
||||
let resolved = source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
let resolved =
|
||||
bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")
|
||||
.expect("helper path");
|
||||
|
||||
assert_eq!(resolved, sibling_helper);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ use serde::Serialize;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::c_void;
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::path::Path;
|
||||
@@ -13,6 +12,7 @@ use std::process::Stdio;
|
||||
|
||||
use crate::allow::AllowDenyPaths;
|
||||
use crate::allow::compute_allow_paths;
|
||||
use crate::helper_materialization::bundled_executable_path_for_exe;
|
||||
use crate::helper_materialization::helper_bin_dir;
|
||||
use crate::logging::log_note;
|
||||
use crate::path_normalization::canonical_path_key;
|
||||
@@ -40,11 +40,10 @@ use windows_sys::Win32::Security::SECURITY_NT_AUTHORITY;
|
||||
pub const SETUP_VERSION: u32 = 5;
|
||||
pub const OFFLINE_USERNAME: &str = "CodexSandboxOffline";
|
||||
pub const ONLINE_USERNAME: &str = "CodexSandboxOnline";
|
||||
const BIN_DIRNAME: &str = "bin";
|
||||
const ERROR_CANCELLED: u32 = 1223;
|
||||
const SECURITY_BUILTIN_DOMAIN_RID: u32 = 0x0000_0020;
|
||||
const DOMAIN_ALIAS_RID_ADMINS: u32 = 0x0000_0220;
|
||||
const RESOURCES_DIRNAME: &str = "codex-resources";
|
||||
const SETUP_EXE_FILENAME: &str = "codex-windows-sandbox-setup.exe";
|
||||
const USERPROFILE_ROOT_EXCLUSIONS: &[&str] = &[
|
||||
".ssh",
|
||||
".tsh",
|
||||
@@ -586,34 +585,11 @@ fn find_setup_exe() -> PathBuf {
|
||||
{
|
||||
return setup_exe;
|
||||
}
|
||||
PathBuf::from("codex-windows-sandbox-setup.exe")
|
||||
PathBuf::from(SETUP_EXE_FILENAME)
|
||||
}
|
||||
|
||||
fn find_setup_exe_for_current_exe(exe: &Path) -> Option<PathBuf> {
|
||||
let dir = exe.parent()?;
|
||||
let candidate = dir.join("codex-windows-sandbox-setup.exe");
|
||||
if candidate.is_file() {
|
||||
return Some(candidate);
|
||||
}
|
||||
|
||||
if dir.file_name() == Some(OsStr::new(BIN_DIRNAME))
|
||||
&& let Some(package_dir) = dir.parent()
|
||||
{
|
||||
let package_resource_candidate = package_dir
|
||||
.join(RESOURCES_DIRNAME)
|
||||
.join("codex-windows-sandbox-setup.exe");
|
||||
if package_resource_candidate.is_file() {
|
||||
return Some(package_resource_candidate);
|
||||
}
|
||||
}
|
||||
|
||||
// Older standalone installs keep Windows helper binaries under
|
||||
// `codex-resources/` next to `codex.exe`, so elevation still probes that
|
||||
// sibling folder before falling back to PATH.
|
||||
let resource_candidate = dir
|
||||
.join(RESOURCES_DIRNAME)
|
||||
.join("codex-windows-sandbox-setup.exe");
|
||||
resource_candidate.is_file().then_some(resource_candidate)
|
||||
bundled_executable_path_for_exe(exe, SETUP_EXE_FILENAME)
|
||||
}
|
||||
|
||||
fn report_helper_failure(
|
||||
@@ -976,8 +952,6 @@ fn filter_sensitive_write_roots(mut roots: Vec<PathBuf>, codex_home: &Path) -> V
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::BIN_DIRNAME;
|
||||
use super::RESOURCES_DIRNAME;
|
||||
use super::WINDOWS_PLATFORM_DEFAULT_READ_ROOTS;
|
||||
use super::build_payload_roots;
|
||||
use super::find_setup_exe_for_current_exe;
|
||||
@@ -987,6 +961,8 @@ mod tests {
|
||||
use super::offline_proxy_settings_from_env;
|
||||
use super::profile_read_roots;
|
||||
use super::proxy_ports_from_env;
|
||||
use crate::helper_materialization::BIN_DIRNAME;
|
||||
use crate::helper_materialization::RESOURCES_DIRNAME;
|
||||
use crate::helper_materialization::helper_bin_dir;
|
||||
use crate::policy::SandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
Reference in New Issue
Block a user