Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions codex-rs/core-plugins/src/git_policy.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::marketplace_staging::MarketplaceStagingDir;
use crate::marketplace_staging::cleanup_marketplace_staging;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::path::Path;
use std::process::Command;
use tempfile::TempDir;

// These variables can redirect Git to an untrusted repository or inject command-scoped settings.
pub(crate) const REPOSITORY_LOCAL_GIT_ENVIRONMENT_VARIABLES: &[&str] = &[
Expand Down Expand Up @@ -56,7 +57,7 @@ impl PluginGitMode {
pub(crate) fn configure_trusted_git_repository(
command: &mut Command,
codex_home: &Path,
) -> Result<TempDir, String> {
) -> Result<MarketplaceStagingDir, String> {
let canonical_home = AbsolutePathBuf::from_absolute_path(codex_home)
.and_then(|path| path.canonicalize())
.map_err(|err| format!("failed to resolve trusted Codex home: {err}"))?;
Expand All @@ -70,12 +71,15 @@ pub(crate) fn configure_trusted_git_repository(
return Err("trusted Git repository root escapes Codex home".to_string());
}

let repository = tempfile::Builder::new()
.prefix("git-")
.tempdir_in(staging_root.as_path())
// Keep the explicit GIT_DIR inside Codex home. The staging cleaner skips symlinks.
if staging_root.join(".staging").as_path().is_symlink() {
return Err("trusted Git staging directory is a symlink".to_string());
}
cleanup_marketplace_staging(staging_root.as_path());
let repository = MarketplaceStagingDir::new(staging_root.as_path())
.map_err(|err| format!("failed to create trusted Git repository: {err}"))?;
for directory in ["objects", "refs"] {
std::fs::create_dir(repository.path().join(directory))
std::fs::create_dir_all(repository.path().join(directory))
.map_err(|err| format!("failed to initialize trusted Git repository: {err}"))?;
}
std::fs::write(repository.path().join("HEAD"), "ref: refs/heads/main\n")
Expand Down
22 changes: 16 additions & 6 deletions codex-rs/core-plugins/src/marketplace_staging_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ use std::process::Stdio;
fn cleanup_preserves_live_process_and_reclaims_its_abandoned_checkout() -> Result<()> {
const CHILD_HOME: &str = "CODEX_MARKETPLACE_STAGING_CHILD_HOME";
if let Some(home) = std::env::var_os(CHILD_HOME) {
let staging = MarketplaceStagingDir::new(Path::new(&home))?;
fs::create_dir_all(staging.path())?;
let staging = crate::configure_trusted_git_repository(
&mut std::process::Command::new("git"),
Path::new(&home),
)
.map_err(anyhow::Error::msg)?;
fs::write(staging.path().join("payload"), "in-flight clone")?;
println!("staging-ready");
std::io::stdout().flush()?;
Expand Down Expand Up @@ -42,21 +45,28 @@ fn cleanup_preserves_live_process_and_reclaims_its_abandoned_checkout() -> Resul
}
}
assert!(ready, "child should publish its checkout before cleanup");
let staging_root = home.path().join(".staging");
let tmp_root = home.path().join(".tmp");
let staging_root = tmp_root.join(".staging");
let active = fs::read_dir(&staging_root)?.next().unwrap()?.path();
let payload = active.join("checkout/payload");

// Age must never override the lifetime lock, even across processes.
remove_stale_staging_dirs(home.path(), SystemTime::now() + LEGACY_ADD_MAX_AGE)?;
remove_stale_staging_dirs(&tmp_root, SystemTime::now() + LEGACY_ADD_MAX_AGE)?;
assert_eq!(fs::read_to_string(&payload)?, "in-flight clone");
let concurrent = MarketplaceStagingDir::new(home.path())?;
let concurrent = MarketplaceStagingDir::new(&tmp_root)?;
fs::create_dir_all(concurrent.path())?;
fs::write(concurrent.path().join("payload"), "second clone")?;

child.stdin.take().unwrap().write_all(b"x")?;
assert!(child.wait()?.success());
assert!(payload.exists(), "process exit must bypass TempDir cleanup");
cleanup_marketplace_staging(home.path());
drop(
crate::configure_trusted_git_repository(
&mut std::process::Command::new("git"),
home.path(),
)
.map_err(anyhow::Error::msg)?,
);
assert!(!active.exists());
assert_eq!(
fs::read_to_string(concurrent.path().join("payload"))?,
Expand Down
Loading