Files
codex/codex-rs/windows-sandbox-rs/src/cap.rs
T
7c9731c9af Enable --deny-warnings for cargo shear (#21616)
## Summary

In https://github.com/openai/codex/pull/21584, we disabled doctests for
crates that lack any doctests. We can enforce that property via `cargo
shear --deny-warnings`: crates that lack doctests will be flagged if
doctests are enabled, and crates with doctests will be flagged if
doctests are disabled.

A few additional notes:

- By adding `--deny-warnings`, `cargo shear` also flagged a number of
modules that were not reachable at all. Some of those have been removed.
- This PR removes a usage of `windows_modules!` (since `cargo shear` and
`rustfmt` couldn't see through it) in favor of simple `#[cfg(target_os =
"windows")]` macros. As a consequence, many of these files exhibit churn
in this PR, since they weren't being formatted by `rustfmt` at all on
main.
- Again, to make the code more analyzable, this PR also removes some
usages of `#[path = "cwd_junction.rs"]` in favor of a more standard
module structure. The bin sidecar structure is still retained, but,
e.g., `windows-sandbox-rs/src/bin/command_runner.rs‎` was moved to
`windows-sandbox-rs/src/bin/command_runner/main.rs`, and so on.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-05-08 20:29:00 +00:00

127 lines
4.2 KiB
Rust

use crate::path_normalization::canonical_path_key;
use anyhow::Context;
use anyhow::Result;
use rand::RngCore;
use rand::SeedableRng;
use rand::rngs::SmallRng;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CapSids {
pub workspace: String,
pub readonly: String,
/// Per-workspace capability SIDs keyed by canonicalized CWD string.
///
/// This is used to isolate workspaces from other workspace sandbox writes and to
/// apply per-workspace denies (e.g. protect `CWD/.codex`)
/// without permanently affecting other workspaces.
#[serde(default)]
pub workspace_by_cwd: HashMap<String, String>,
}
pub fn cap_sid_file(codex_home: &Path) -> PathBuf {
codex_home.join("cap_sid")
}
fn make_random_cap_sid_string() -> String {
let mut rng = SmallRng::from_entropy();
let a = rng.next_u32();
let b = rng.next_u32();
let c = rng.next_u32();
let d = rng.next_u32();
format!("S-1-5-21-{a}-{b}-{c}-{d}")
}
fn persist_caps(path: &Path, caps: &CapSids) -> Result<()> {
if let Some(dir) = path.parent() {
fs::create_dir_all(dir).with_context(|| format!("create cap sid dir {}", dir.display()))?;
}
let json = serde_json::to_string(caps)?;
fs::write(path, json).with_context(|| format!("write cap sid file {}", path.display()))?;
Ok(())
}
pub fn load_or_create_cap_sids(codex_home: &Path) -> Result<CapSids> {
let path = cap_sid_file(codex_home);
if path.exists() {
let txt = fs::read_to_string(&path)
.with_context(|| format!("read cap sid file {}", path.display()))?;
let t = txt.trim();
if t.starts_with('{') && t.ends_with('}') {
if let Ok(obj) = serde_json::from_str::<CapSids>(t) {
return Ok(obj);
}
} else if !t.is_empty() {
let caps = CapSids {
workspace: t.to_string(),
readonly: make_random_cap_sid_string(),
workspace_by_cwd: HashMap::new(),
};
persist_caps(&path, &caps)?;
return Ok(caps);
}
}
let caps = CapSids {
workspace: make_random_cap_sid_string(),
readonly: make_random_cap_sid_string(),
workspace_by_cwd: HashMap::new(),
};
persist_caps(&path, &caps)?;
Ok(caps)
}
/// Returns the workspace-specific capability SID for `cwd`, creating and persisting it if missing.
pub fn workspace_cap_sid_for_cwd(codex_home: &Path, cwd: &Path) -> Result<String> {
let path = cap_sid_file(codex_home);
let mut caps = load_or_create_cap_sids(codex_home)?;
let key = canonical_path_key(cwd);
if let Some(sid) = caps.workspace_by_cwd.get(&key) {
return Ok(sid.clone());
}
let sid = make_random_cap_sid_string();
caps.workspace_by_cwd.insert(key, sid.clone());
persist_caps(&path, &caps)?;
Ok(sid)
}
#[cfg(test)]
mod tests {
use super::load_or_create_cap_sids;
use super::workspace_cap_sid_for_cwd;
use pretty_assertions::assert_eq;
use std::path::PathBuf;
#[test]
fn equivalent_cwd_spellings_share_workspace_sid_key() {
let temp = tempfile::tempdir().expect("tempdir");
let codex_home = temp.path().join("codex-home");
std::fs::create_dir_all(&codex_home).expect("create codex home");
let workspace = temp.path().join("WorkspaceRoot");
std::fs::create_dir_all(&workspace).expect("create workspace root");
let canonical = dunce::canonicalize(&workspace).expect("canonical workspace root");
let alt_spelling = PathBuf::from(
canonical
.to_string_lossy()
.replace('\\', "/")
.to_ascii_uppercase(),
);
let first_sid =
workspace_cap_sid_for_cwd(&codex_home, canonical.as_path()).expect("first sid");
let second_sid =
workspace_cap_sid_for_cwd(&codex_home, alt_spelling.as_path()).expect("second sid");
assert_eq!(first_sid, second_sid);
let caps = load_or_create_cap_sids(&codex_home).expect("load caps");
assert_eq!(caps.workspace_by_cwd.len(), 1);
}
}