share trusted Desktop distribution discovery

This commit is contained in:
Abhinav Vedmala
2026-06-15 04:04:43 +00:00
parent dfd03ea01b
commit 4633e26cff
12 changed files with 403 additions and 25 deletions
+11
View File
@@ -2291,6 +2291,7 @@ dependencies = [
"codex-config",
"codex-core",
"codex-core-plugins",
"codex-desktop-distribution",
"codex-exec",
"codex-exec-server",
"codex-execpolicy",
@@ -2782,6 +2783,16 @@ dependencies = [
"zip 2.4.2",
]
[[package]]
name = "codex-desktop-distribution"
version = "0.0.0"
dependencies = [
"codex-utils-absolute-path",
"pretty_assertions",
"tempfile",
"thiserror 2.0.18",
]
[[package]]
name = "codex-exec"
version = "0.0.0"
+2
View File
@@ -33,6 +33,7 @@ members = [
"connectors",
"config",
"context-fragments",
"desktop-distribution",
"shell-command",
"shell-escalation",
"skills",
@@ -165,6 +166,7 @@ codex-home = { path = "codex-home" }
codex-config = { path = "config" }
codex-connectors = { path = "connectors" }
codex-context-fragments = { path = "context-fragments" }
codex-desktop-distribution = { path = "desktop-distribution" }
codex-core = { path = "core" }
codex-core-api = { path = "core-api" }
codex-core-plugins = { path = "core-plugins" }
+1
View File
@@ -31,6 +31,7 @@ codex-chatgpt = { workspace = true }
codex-cloud-tasks = { path = "../cloud-tasks" }
codex-utils-cli = { workspace = true }
codex-config = { workspace = true }
codex-desktop-distribution = { workspace = true }
codex-core = { workspace = true }
codex-core-plugins = { workspace = true }
codex-home = { workspace = true }
+7 -13
View File
@@ -1,4 +1,5 @@
use anyhow::Context as _;
use codex_desktop_distribution::discover_installed_distribution;
use std::ffi::CString;
use std::path::Path;
use std::path::PathBuf;
@@ -13,7 +14,7 @@ pub async fn run_mac_app_open_or_install(
workspace: PathBuf,
download_url_override: Option<String>,
) -> anyhow::Result<()> {
if let Some(app_path) = find_existing_codex_app_path() {
if let Some(app_path) = find_existing_codex_app_path().await? {
eprintln!(
"Opening Codex Desktop at {app_path}...",
app_path = app_path.display()
@@ -63,18 +64,11 @@ fn is_apple_silicon_mac() -> bool {
|| macos_sysctl_flag("hw.optional.arm64").unwrap_or(false)
}
fn find_existing_codex_app_path() -> Option<PathBuf> {
candidate_codex_app_paths()
.into_iter()
.find(|candidate| candidate.is_dir())
}
fn candidate_codex_app_paths() -> Vec<PathBuf> {
let mut paths = vec![PathBuf::from("/Applications/Codex.app")];
if let Some(home) = std::env::var_os("HOME") {
paths.push(PathBuf::from(home).join("Applications").join("Codex.app"));
}
paths
async fn find_existing_codex_app_path() -> anyhow::Result<Option<PathBuf>> {
let distribution = tokio::task::spawn_blocking(discover_installed_distribution)
.await
.context("Desktop discovery task failed")??;
Ok(distribution.map(|distribution| distribution.app_root().to_path_buf()))
}
async fn open_codex_app(app_path: &Path, workspace: &Path) -> anyhow::Result<()> {
+4 -12
View File
@@ -1,4 +1,5 @@
use anyhow::Context as _;
use codex_desktop_distribution::discover_installed_distribution;
use std::path::Path;
use std::path::PathBuf;
use tokio::process::Command;
@@ -31,19 +32,10 @@ pub async fn run_windows_app_open_or_install(
}
async fn codex_app_is_installed() -> anyhow::Result<bool> {
let output = Command::new("powershell.exe")
.arg("-NoProfile")
.arg("-Command")
.arg("Get-StartApps -Name 'Codex' | Select-Object -First 1 -ExpandProperty AppID")
.output()
Ok(tokio::task::spawn_blocking(discover_installed_distribution)
.await
.context("failed to invoke `powershell.exe`")?;
if !output.status.success() {
return Ok(false);
}
Ok(!String::from_utf8_lossy(&output.stdout).trim().is_empty())
.context("Desktop discovery task failed")??
.is_some())
}
async fn open_url(url: &str) -> anyhow::Result<()> {
@@ -0,0 +1,6 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "desktop-distribution",
crate_name = "codex_desktop_distribution",
)
+21
View File
@@ -0,0 +1,21 @@
[package]
name = "codex-desktop-distribution"
version.workspace = true
edition.workspace = true
license.workspace = true
[lib]
name = "codex_desktop_distribution"
path = "src/lib.rs"
doctest = false
[lints]
workspace = true
[dependencies]
codex-utils-absolute-path = { workspace = true }
thiserror = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }
tempfile = { workspace = true }
+187
View File
@@ -0,0 +1,187 @@
//! Locates the trusted Codex Desktop resources bundled with the installed app.
use std::fs;
use std::io;
use std::path::Component;
use std::path::Path;
use std::path::PathBuf;
use codex_utils_absolute_path::AbsolutePathBuf;
#[cfg(target_os = "macos")]
#[path = "macos.rs"]
mod platform;
#[cfg(not(any(target_os = "macos", windows)))]
#[path = "unsupported.rs"]
mod platform;
#[cfg(windows)]
#[path = "windows.rs"]
mod platform;
// Resource override for trusted Desktop development builds and external CLI launchers.
const DESKTOP_RESOURCES_PATH_ENV_VAR: &str = "CODEX_DESKTOP_RESOURCES_PATH";
#[derive(Debug, thiserror::Error)]
pub enum DesktopDistributionError {
/// No launcher hint was provided and no installed Codex Desktop app was found. This can
/// happen, for example, when Desktop was uninstalled after installing a bundled plugin.
#[error("no installed Codex Desktop distribution was found")]
NotFound,
/// The platform app-discovery mechanism could not be queried successfully.
#[error("Codex Desktop discovery failed: {0}")]
Discovery(String),
/// An expected app resource could not be read or canonicalized.
#[error("Codex Desktop filesystem validation failed during {stage}: {source}")]
Filesystem {
stage: &'static str,
#[source]
source: io::Error,
},
/// A requested resource was absolute, contained `..`, escaped through a symlink, or had the
/// wrong file kind, so it was not strictly contained beneath Desktop's resources directory.
#[error("Codex Desktop resource containment failed: {0}")]
Containment(String),
}
#[derive(Debug)]
pub struct DesktopResources {
root: AbsolutePathBuf,
}
impl DesktopResources {
pub fn contained_file(
&self,
relative_path: impl AsRef<Path>,
) -> Result<AbsolutePathBuf, DesktopDistributionError> {
contained_path(&self.root, relative_path.as_ref(), ResourceKind::File)
}
pub fn contained_directory(
&self,
relative_path: impl AsRef<Path>,
) -> Result<AbsolutePathBuf, DesktopDistributionError> {
contained_path(&self.root, relative_path.as_ref(), ResourceKind::Directory)
}
/// Uses a resources directory supplied by the trusted Desktop launcher.
pub fn from_trusted_path(root: PathBuf) -> Result<Self, DesktopDistributionError> {
let root = canonical(&root, "resources root")?;
if !root.as_path().is_dir() {
return Err(containment("expected the resources root to be a directory"));
}
Ok(Self { root })
}
}
#[derive(Debug)]
pub struct InstalledDesktop {
app_root: AbsolutePathBuf,
resources: DesktopResources,
}
impl InstalledDesktop {
pub fn app_root(&self) -> &Path {
self.app_root.as_path()
}
#[cfg(any(target_os = "macos", windows, test))]
fn from_paths(
app_root: PathBuf,
resources_root: PathBuf,
) -> Result<Self, DesktopDistributionError> {
let app_root = canonical(&app_root, "application root")?;
let resources = DesktopResources::from_trusted_path(resources_root)?;
if resources.root == app_root || !resources.root.as_path().starts_with(app_root.as_path()) {
return Err(containment(
"resources root is not strictly below the discovered application root",
));
}
Ok(Self {
app_root,
resources,
})
}
}
/// Uses resources supplied by Desktop, otherwise discovers the installed stable app.
pub fn locate_current_or_installed_resources() -> Result<DesktopResources, DesktopDistributionError>
{
if let Some(resources_root) = std::env::var_os(DESKTOP_RESOURCES_PATH_ENV_VAR) {
return DesktopResources::from_trusted_path(PathBuf::from(resources_root));
}
discover_installed_distribution()?
.map(|distribution| distribution.resources)
.ok_or(DesktopDistributionError::NotFound)
}
/// Discovers an installed Desktop app without consulting the launcher resources hint.
pub fn discover_installed_distribution()
-> Result<Option<InstalledDesktop>, DesktopDistributionError> {
platform::discover()
}
#[derive(Clone, Copy)]
enum ResourceKind {
Directory,
File,
}
/// Resolves a resource path beneath the trusted Desktop resources root.
///
/// Rejects empty paths and non-normal components, canonicalizes the joined path to catch symlink
/// escapes, requires the result to remain strictly below `root`, and verifies that it has the
/// requested file kind.
fn contained_path(
root: &AbsolutePathBuf,
relative_path: &Path,
kind: ResourceKind,
) -> Result<AbsolutePathBuf, DesktopDistributionError> {
if relative_path.as_os_str().is_empty()
|| relative_path
.components()
.any(|component| !matches!(component, Component::Normal(_)))
{
return Err(containment(
"resource paths must contain only normal relative components",
));
}
let path = canonical(root.join(relative_path).as_path(), "resource path")?;
if path == *root || !path.as_path().starts_with(root.as_path()) {
return Err(containment(
"resource must remain strictly below the Desktop resources root",
));
}
let metadata =
fs::metadata(path.as_path()).map_err(|source| DesktopDistributionError::Filesystem {
stage: "resource metadata",
source,
})?;
let expected_kind = match kind {
ResourceKind::Directory => metadata.is_dir(),
ResourceKind::File => metadata.is_file(),
};
if !expected_kind {
return Err(containment(match kind {
ResourceKind::Directory => "expected a directory",
ResourceKind::File => "expected a regular file",
}));
}
Ok(path)
}
fn canonical(
path: &Path,
stage: &'static str,
) -> Result<AbsolutePathBuf, DesktopDistributionError> {
AbsolutePathBuf::from_absolute_path_checked(path)
.and_then(|path| path.canonicalize())
.map_err(|source| DesktopDistributionError::Filesystem { stage, source })
}
fn containment(message: impl Into<String>) -> DesktopDistributionError {
DesktopDistributionError::Containment(message.into())
}
#[cfg(test)]
#[path = "lib_tests.rs"]
mod tests;
@@ -0,0 +1,81 @@
use pretty_assertions::assert_eq;
use std::fs;
use super::DesktopDistributionError;
use super::DesktopResources;
#[cfg(unix)]
use super::InstalledDesktop;
use super::canonical;
#[test]
fn resolves_strictly_contained_files_and_directories() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path().join("resources");
let directory = root.join("plugins/demo");
fs::create_dir_all(&directory).expect("create directory");
let file = directory.join("hook.json");
fs::write(&file, "{}").expect("write file");
let resources = DesktopResources::from_trusted_path(root).expect("Desktop resources");
assert_eq!(
resources
.contained_directory("plugins/demo")
.expect("contained directory"),
canonical(&directory, "test directory").expect("canonical directory")
);
assert_eq!(
resources
.contained_file("plugins/demo/hook.json")
.expect("contained file"),
canonical(&file, "test file").expect("canonical file")
);
}
#[test]
fn rejects_non_normal_and_wrong_kind_paths() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path().join("resources");
fs::create_dir_all(root.join("plugins")).expect("create directory");
let resources = DesktopResources::from_trusted_path(root).expect("Desktop resources");
for path in ["", ".", "../resources/plugins", "/tmp"] {
assert!(matches!(
resources.contained_directory(path),
Err(DesktopDistributionError::Containment(_))
));
}
assert!(matches!(
resources.contained_file("plugins"),
Err(DesktopDistributionError::Containment(_))
));
}
#[cfg(unix)]
#[test]
fn rejects_symlink_traversal() {
use std::os::unix::fs::symlink;
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path().join("resources");
let outside = temp.path().join("outside");
fs::create_dir_all(&root).expect("create root");
fs::create_dir_all(&outside).expect("create outside");
fs::write(outside.join("hook.json"), "{}").expect("write file");
symlink(&outside, root.join("plugins")).expect("create symlink");
let resources = DesktopResources::from_trusted_path(root).expect("Desktop resources");
assert!(matches!(
resources.contained_file("plugins/hook.json"),
Err(DesktopDistributionError::Containment(_))
));
let app_root = temp.path().join("Codex.app");
let resources_link = app_root.join("Contents/Resources");
fs::create_dir_all(resources_link.parent().expect("resources parent"))
.expect("create app contents");
symlink(&outside, &resources_link).expect("create resources symlink");
assert!(matches!(
InstalledDesktop::from_paths(app_root, resources_link),
Err(DesktopDistributionError::Containment(_))
));
}
@@ -0,0 +1,41 @@
use std::path::PathBuf;
use std::process::Command;
use crate::DesktopDistributionError;
use crate::InstalledDesktop;
const BUNDLE_IDENTIFIER: &str = "com.openai.codex";
const TEAM_IDENTIFIER: &str = "2DC432GLL2";
/// Asks LaunchServices for the signed stable Codex app, independent of name or location.
pub(crate) fn discover() -> Result<Option<InstalledDesktop>, DesktopDistributionError> {
let script = format!(r#"POSIX path of (path to application id "{BUNDLE_IDENTIFIER}")"#);
let output = Command::new("/usr/bin/osascript")
.args(["-e", &script])
.output()
.map_err(|error| DesktopDistributionError::Discovery(error.to_string()))?;
if !output.status.success() {
return Ok(None);
}
let app_root = PathBuf::from(String::from_utf8_lossy(&output.stdout).trim());
let resources_root = app_root.join("Contents/Resources");
if app_root.extension().is_some_and(|value| value == "app")
&& resources_root.is_dir()
&& has_openai_signature(&app_root)?
{
return InstalledDesktop::from_paths(app_root, resources_root).map(Some);
}
Ok(None)
}
fn has_openai_signature(app_root: &std::path::Path) -> Result<bool, DesktopDistributionError> {
let requirement = format!(
r#"=identifier "{BUNDLE_IDENTIFIER}" and anchor apple generic and certificate leaf[subject.OU] = "{TEAM_IDENTIFIER}""#
);
let verification = Command::new("/usr/bin/codesign")
.args(["--verify", "--strict", "-R", &requirement])
.arg(app_root)
.status()
.map_err(|error| DesktopDistributionError::Discovery(error.to_string()))?;
Ok(verification.success())
}
@@ -0,0 +1,6 @@
use crate::DesktopDistributionError;
use crate::InstalledDesktop;
pub(crate) fn discover() -> Result<Option<InstalledDesktop>, DesktopDistributionError> {
Ok(None)
}
@@ -0,0 +1,36 @@
use std::path::PathBuf;
use std::process::Command;
use crate::DesktopDistributionError;
use crate::InstalledDesktop;
const STORE_PUBLISHER_ID: &str = "2p2nqsd0c76g0";
/// Queries the signed stable MSIX identity and uses the package location as the app root.
pub(crate) fn discover() -> Result<Option<InstalledDesktop>, DesktopDistributionError> {
let script = format!(
r#"
$location = Get-AppxPackage -Name 'OpenAI.Codex' -ErrorAction SilentlyContinue |
Where-Object {{ $_.PublisherId -eq '{STORE_PUBLISHER_ID}' }} |
Select-Object -First 1 -ExpandProperty InstallLocation
if ($location) {{ $location }}
"#
);
let output = Command::new("powershell.exe")
.arg("-NoProfile")
.arg("-NonInteractive")
.arg("-Command")
.arg(script)
.output()
.map_err(|error| DesktopDistributionError::Discovery(error.to_string()))?;
if !output.status.success() {
return Ok(None);
}
let install_location = String::from_utf8_lossy(&output.stdout).trim().to_string();
if install_location.is_empty() {
return Ok(None);
}
let app_root = PathBuf::from(install_location);
let resources_root = app_root.join("app/resources");
InstalledDesktop::from_paths(app_root, resources_root).map(Some)
}