Compare commits

..

3 Commits

Author SHA1 Message Date
celia-oai
5cce1f7345 changes 2026-03-27 16:49:23 -07:00
celia-oai
4222b6978f changes 2026-03-27 13:43:27 -07:00
Michael Bolin
95845cf6ce fix: disable plugins in SDK integration tests (#16036)
## Why

The TypeScript SDK tests create a fresh `CODEX_HOME` for each Jest case
and delete it during teardown. That cleanup has been flaking because the
real `codex` binary can still be doing background curated-plugin startup
sync under `.tmp/plugins-clone-*`, which races the test harness's
recursive delete and leaves `ENOTEMPTY` failures behind.

This path is unrelated to what the SDK tests are exercising, so letting
plugin startup run during these tests only adds nondeterministic
filesystem activity. This showed up recently in the `sdk` CI lane for
[#16031](https://github.com/openai/codex/pull/16031).

## What Changed

- updated `sdk/typescript/tests/testCodex.ts` to merge test config
through a single helper
- disabled `features.plugins` unconditionally for SDK integration tests
so the CLI does not start curated-plugin sync in the temporary
`CODEX_HOME`
- preserved other explicit feature overrides from individual tests while
forcing `plugins` back to `false`
- kept the existing mock-provider override behavior intact for
SSE-backed tests

## Verification

- `pnpm test --runInBand`
- `pnpm lint`
2026-03-27 13:04:34 -07:00
16 changed files with 549 additions and 446 deletions

View File

@@ -20,6 +20,9 @@ common:windows --host_platform=//:local_windows
common --@rules_cc//cc/toolchains/args/archiver_flags:use_libtool_on_macos=False
common --@llvm//config:experimental_stub_libgcc_s
# We need to use the sh toolchain on windows so we don't send host bash paths to the linux executor.
common:windows --@rules_rust//rust/settings:experimental_use_sh_toolchain_for_bootstrap_process_wrapper
# TODO(zbarsky): rules_rust doesn't implement this flag properly with remote exec...
# common --@rules_rust//rust/settings:pipelined_compilation

View File

@@ -57,11 +57,5 @@ runs:
if: runner.os == 'Windows'
shell: pwsh
run: |
# Use a very short path to reduce argv/path length issues, but avoid the
# drive root because some Windows test launchers mis-handle MANIFEST paths there.
"BAZEL_OUTPUT_USER_ROOT=D:\b" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Enable Git long paths (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: git config --global core.longpaths true
# Use a very short path to reduce argv/path length issues.
"BAZEL_OUTPUT_USER_ROOT=C:\" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

View File

@@ -17,7 +17,6 @@ concurrency:
cancel-in-progress: ${{ github.ref_name != 'main' }}
jobs:
test:
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
@@ -40,9 +39,9 @@ jobs:
# - os: ubuntu-24.04-arm
# target: aarch64-unknown-linux-gnu
# Windows
- os: windows-latest
target: x86_64-pc-windows-gnullvm
# TODO: Enable Windows once we fix the toolchain issues there.
#- os: windows-latest
# target: x86_64-pc-windows-gnullvm
runs-on: ${{ matrix.os }}
# Configure a human readable name for each job
@@ -68,84 +67,8 @@ jobs:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
shell: bash
run: |
bazel_targets=(
//...
# Keep V8 out of the ordinary Bazel CI path. Only the dedicated
# canary and release workflows should build `third_party/v8`.
-//third_party/v8:all
)
if [[ "${RUNNER_OS:-}" == "Windows" ]]; then
# This is intentionally a foothold rather than the final Windows
# Bazel suite. Keep a stable set of targets that are known not to
# pull in the unresolved V8 dependency so we can land meaningful CI
# coverage now and expand it once the V8 situation is understood.
bazel_targets=(
//codex-rs/analytics:analytics-unit-tests
//codex-rs/ansi-escape:ansi-escape-unit-tests
//codex-rs/app-server-protocol:app-server-protocol-schema_fixtures-test
//codex-rs/app-server-protocol:app-server-protocol-unit-tests
//codex-rs/apply-patch:apply-patch-all-test
//codex-rs/apply-patch:apply-patch-unit-tests
//codex-rs/async-utils:async-utils-unit-tests
//codex-rs/codex-api:codex-api-unit-tests
//codex-rs/codex-backend-openapi-models:codex-backend-openapi-models-unit-tests
//codex-rs/codex-client:codex-client-unit-tests
//codex-rs/codex-experimental-api-macros:codex-experimental-api-macros-unit-tests
//codex-rs/config:config-unit-tests
//codex-rs/connectors:connectors-unit-tests
//codex-rs/core-skills:core-skills-unit-tests
//codex-rs/exec-server:exec-server-unit-tests
//codex-rs/execpolicy-legacy:execpolicy-legacy-all-test
//codex-rs/execpolicy-legacy:execpolicy-legacy-unit-tests
//codex-rs/execpolicy:execpolicy-unit-tests
//codex-rs/features:features-unit-tests
//codex-rs/feedback:feedback-unit-tests
//codex-rs/file-search:file-search-unit-tests
//codex-rs/git-utils:git-utils-unit-tests
//codex-rs/hooks:hooks-unit-tests
//codex-rs/instructions:instructions-unit-tests
//codex-rs/keyring-store:keyring-store-unit-tests
//codex-rs/network-proxy:network-proxy-unit-tests
//codex-rs/otel:otel-unit-tests
//codex-rs/plugin:plugin-unit-tests
//codex-rs/process-hardening:process-hardening-unit-tests
//codex-rs/protocol:protocol-unit-tests
//codex-rs/responses-api-proxy:responses-api-proxy-unit-tests
//codex-rs/rmcp-client:rmcp-client-unit-tests
//codex-rs/rollout:rollout-unit-tests
//codex-rs/sandboxing:sandboxing-unit-tests
//codex-rs/secrets:secrets-unit-tests
//codex-rs/shell-escalation:shell-escalation-unit-tests
//codex-rs/skills:skills-unit-tests
//codex-rs/state:state-unit-tests
//codex-rs/stdio-to-uds:stdio-to-uds-unit-tests
//codex-rs/terminal-detection:terminal-detection-unit-tests
//codex-rs/tools:tools-unit-tests
//codex-rs/utils/absolute-path:absolute-path-unit-tests
//codex-rs/utils/approval-presets:approval-presets-unit-tests
//codex-rs/utils/cache:cache-unit-tests
//codex-rs/utils/cargo-bin:cargo-bin-unit-tests
//codex-rs/utils/cli:cli-unit-tests
//codex-rs/utils/elapsed:elapsed-unit-tests
//codex-rs/utils/fuzzy-match:fuzzy-match-unit-tests
//codex-rs/utils/home-dir:home-dir-unit-tests
//codex-rs/utils/image:image-unit-tests
//codex-rs/utils/json-to-toml:json-to-toml-unit-tests
//codex-rs/utils/output-truncation:output-truncation-unit-tests
//codex-rs/utils/path-utils:path-utils-unit-tests
//codex-rs/utils/plugins:plugins-unit-tests
//codex-rs/utils/pty:pty-unit-tests
//codex-rs/utils/readiness:readiness-unit-tests
//codex-rs/utils/rustls-provider:rustls-provider-unit-tests
//codex-rs/utils/sleep-inhibitor:sleep-inhibitor-unit-tests
//codex-rs/utils/stream-parser:stream-parser-unit-tests
//codex-rs/utils/string:string-unit-tests
//codex-rs/utils/template:template-unit-tests
//codex-rs/windows-sandbox-rs:windows-sandbox-rs-unit-tests
)
echo "Windows smoke target count: ${#bazel_targets[@]}"
fi
# Keep V8 out of the ordinary Bazel CI path. Only the dedicated
# canary and release workflows should build `third_party/v8`.
./.github/scripts/run-bazel-ci.sh \
--print-failed-test-logs \
--use-node-test-env \
@@ -154,7 +77,8 @@ jobs:
--test_verbose_timeout_warnings \
--build_metadata=COMMIT_SHA=${GITHUB_SHA} \
-- \
"${bazel_targets[@]}"
//... \
-//third_party/v8:all
# Save bazel repository cache explicitly; make non-fatal so cache uploading
# never fails the overall job. Only save when key wasn't hit.

View File

@@ -44,27 +44,8 @@ bazel_dep(name = "apple_support", version = "2.1.0")
bazel_dep(name = "rules_cc", version = "0.2.16")
bazel_dep(name = "rules_platform", version = "0.1.0")
bazel_dep(name = "rules_rs", version = "0.0.43")
# `rules_rs` 0.0.43 does not model `x86_64-pc-windows-gnullvm` as a distinct
# Windows exec platform, so patch it until upstream grows that support.
single_version_override(
module_name = "rules_rs",
patch_strip = 1,
patches = [
"//patches:rules_rs_windows_gnullvm_exec.patch",
],
version = "0.0.43",
)
rules_rust = use_extension("@rules_rs//rs/experimental:rules_rust.bzl", "rules_rust")
# Build-script probe binaries inherit CFLAGS/CXXFLAGS from Bazel's C++
# toolchain. On `windows-gnullvm`, llvm-mingw does not ship
# `libssp_nonshared`, so strip the forwarded stack-protector flags there.
rules_rust.patch(
patches = [
"//patches:rules_rust_windows_gnullvm_build_script.patch",
],
strip = 1,
)
use_repo(rules_rust, "rules_rust")
toolchains = use_extension("@rules_rs//rs/experimental/toolchains:module_extension.bzl", "toolchains")

4
MODULE.bazel.lock generated
View File

@@ -1579,14 +1579,12 @@
"cargo-1.93.0-aarch64-pc-windows-msvc.tar.xz": "155bff7a16aa7054e7ed7c3a82e362d4b302b3882d751b823e06ff63ae3f103d",
"cargo-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "5998940b8b97286bb67facb1a85535eeb3d4d7a61e36a85e386e5c0c5cfe5266",
"cargo-1.93.0-x86_64-apple-darwin.tar.xz": "95a47c5ed797c35419908f04188d8b7de09946e71073c4b72632b16f5b10dfae",
"cargo-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "f19766837559f90476508140cb95cc708220012ec00a854fa9f99187b1f246b6",
"cargo-1.93.0-x86_64-pc-windows-msvc.tar.xz": "e59c5e2baa9ec17261f2cda6676ebf7b68b21a860e3f7451c4d964728951da75",
"cargo-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "c23de3ae709ff33eed5e4ae59d1f9bcd75fa4dbaa9fb92f7b06bfb534b8db880",
"clippy-1.93.0-aarch64-apple-darwin.tar.xz": "0b6e943a8d12be0e68575acf59c9ea102daf795055fcbbf862b0bfd35ec40039",
"clippy-1.93.0-aarch64-pc-windows-msvc.tar.xz": "07bcf2edb88cdf5ead2f02e4a8493e9b0ef935a31253fac6f9f3378d8023f113",
"clippy-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "872ae6d68d625946d281b91d928332e6b74f6ab269b6af842338df4338805a60",
"clippy-1.93.0-x86_64-apple-darwin.tar.xz": "e6d0b1afb9607c14a1172d09ee194a032bbb3e48af913d55c5a473e0559eddde",
"clippy-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "b6f1f7264ed6943c59dedfb9531fbadcc3c0fcf273c940a63d58898b14a1060f",
"clippy-1.93.0-x86_64-pc-windows-msvc.tar.xz": "25fb103390bf392980b4689ac09b2ec2ab4beefb7022a983215b613ad05eab57",
"clippy-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "793108977514b15c0f45ade28ae35c58b05370cb0f22e89bd98fdfa61eabf55d",
"rust-std-1.93.0-aarch64-apple-darwin.tar.xz": "8603c63715349636ed85b4fe716c4e827a727918c840e54aff5b243cedadf19b",
@@ -1658,14 +1656,12 @@
"rustc-1.93.0-aarch64-pc-windows-msvc.tar.xz": "a3ac1a8e411de8470f71b366f89d187718c431526912b181692ed0a18c56c7ad",
"rustc-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "1a9045695892ec08d8e9751bf7cf7db71fe27a6202dd12ce13aca48d0602dbde",
"rustc-1.93.0-x86_64-apple-darwin.tar.xz": "594bb293f0a4f444656cf8dec2149fcb979c606260efee9e09bcf8c9c6ed6ae7",
"rustc-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "0cdaa8de66f5ce21d1ea73917efc5c64f408bda49f678ddde19465ced9d5ec63",
"rustc-1.93.0-x86_64-pc-windows-msvc.tar.xz": "fa17677eee0d83eb055b309953184bf87ba634923d8897f860cda65d55c6e350",
"rustc-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "00c6e6740ea6a795e33568cd7514855d58408a1180cd820284a7bbf7c46af715",
"rustfmt-1.93.0-aarch64-apple-darwin.tar.xz": "0dd1faedf0768ef362f4aae4424b34e8266f2b9cf5e76ea4fcaf780220b363a0",
"rustfmt-1.93.0-aarch64-pc-windows-msvc.tar.xz": "24eed108489567133bbfe40c8eacda1567be55fae4c526911b39eb33eb27a6cb",
"rustfmt-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "92e1acb45ae642136258b4dabb39302af2d53c83e56ebd5858bc969f9e5c141a",
"rustfmt-1.93.0-x86_64-apple-darwin.tar.xz": "c8453b4c5758eb39423042ffa9c23ed6128cbed2b15b581e5e1192c9cc0b1d4e",
"rustfmt-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "47167e9e78db9be4503a060dee02f4df2cda252da32175dbf44331f965a747b9",
"rustfmt-1.93.0-x86_64-pc-windows-msvc.tar.xz": "5becc7c2dba4b9ab5199012cad30829235a7f7fb5d85a238697e8f0e44cbd9af",
"rustfmt-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "7f81f6c17d11a7fda5b4e1b111942fb3b23d30dcec767e13e340ebfb762a5e33"
}

View File

@@ -586,13 +586,14 @@ fn permissions_profiles_require_default_permissions() -> std::io::Result<()> {
}
#[test]
fn permissions_profiles_reject_writes_outside_workspace_root() -> std::io::Result<()> {
fn permissions_profiles_allow_writes_outside_workspace_root_with_read_only_legacy_fallback()
-> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cwd = TempDir::new()?;
std::fs::write(cwd.path().join(".git"), "gitdir: nowhere")?;
let external_write_path = if cfg!(windows) { r"C:\temp" } else { "/tmp" };
let err = Config::load_from_base_config_with_overrides(
let config = Config::load_from_base_config_with_overrides(
ConfigToml {
default_permissions: Some("workspace".to_string()),
permissions: Some(PermissionsToml {
@@ -616,14 +617,45 @@ fn permissions_profiles_reject_writes_outside_workspace_root() -> std::io::Resul
..Default::default()
},
codex_home.path().to_path_buf(),
)
.expect_err("writes outside the workspace root should be rejected");
)?;
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert_eq!(
config.permissions.sandbox_policy.get(),
&SandboxPolicy::ReadOnly {
access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: Vec::new(),
},
network_access: false,
}
);
assert!(
err.to_string()
.contains("filesystem writes outside the workspace root"),
"{err}"
config
.permissions
.file_system_sandbox_policy
.can_write_path_with_cwd(Path::new(external_write_path), cwd.path())
);
assert!(
config
.permissions
.file_system_sandbox_policy
.needs_direct_runtime_enforcement(
config.permissions.network_sandbox_policy,
cwd.path(),
)
);
assert!(
config
.permissions
.file_system_sandbox_policy
.can_write_path_with_cwd(codex_home.path().join("memories").as_path(), cwd.path())
);
assert_eq!(config.approvals_reviewer, ApprovalsReviewer::User);
let warnings = &config.startup_warnings;
assert!(
warnings.is_empty(),
"external writes should no longer fail config load: {warnings:?}"
);
Ok(())
}
@@ -734,6 +766,133 @@ fn permissions_profiles_allow_unknown_special_paths() -> std::io::Result<()> {
Ok(())
}
#[test]
fn permissions_profiles_allow_tmpdir_write_with_read_only_legacy_fallback() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cwd = TempDir::new()?;
std::fs::write(cwd.path().join(".git"), "gitdir: nowhere")?;
let config = Config::load_from_base_config_with_overrides(
ConfigToml {
default_permissions: Some("workspace".to_string()),
permissions: Some(PermissionsToml {
entries: BTreeMap::from([(
"workspace".to_string(),
PermissionProfileToml {
filesystem: Some(FilesystemPermissionsToml {
entries: BTreeMap::from([(
":tmpdir".to_string(),
FilesystemPermissionToml::Access(FileSystemAccessMode::Write),
)]),
}),
network: None,
},
)]),
}),
..Default::default()
},
ConfigOverrides {
cwd: Some(cwd.path().to_path_buf()),
..Default::default()
},
codex_home.path().to_path_buf(),
)?;
let memories_root = codex_home.path().join("memories").abs();
assert!(
config
.permissions
.file_system_sandbox_policy
.entries
.iter()
.any(|entry| {
entry.access == FileSystemAccessMode::Write
&& entry.path
== FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
}
})
);
assert!(
config
.permissions
.file_system_sandbox_policy
.can_write_path_with_cwd(memories_root.as_path(), cwd.path())
);
assert_eq!(
config.permissions.sandbox_policy.get(),
&SandboxPolicy::ReadOnly {
access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: Vec::new(),
},
network_access: false,
}
);
Ok(())
}
#[cfg(unix)]
#[test]
fn permissions_profiles_allow_slash_tmp_write_with_read_only_legacy_fallback() -> std::io::Result<()>
{
let codex_home = TempDir::new()?;
let cwd = TempDir::new()?;
std::fs::write(cwd.path().join(".git"), "gitdir: nowhere")?;
let config = Config::load_from_base_config_with_overrides(
ConfigToml {
default_permissions: Some("workspace".to_string()),
permissions: Some(PermissionsToml {
entries: BTreeMap::from([(
"workspace".to_string(),
PermissionProfileToml {
filesystem: Some(FilesystemPermissionsToml {
entries: BTreeMap::from([(
"/tmp".to_string(),
FilesystemPermissionToml::Access(FileSystemAccessMode::Write),
)]),
}),
network: None,
},
)]),
}),
..Default::default()
},
ConfigOverrides {
cwd: Some(cwd.path().to_path_buf()),
..Default::default()
},
codex_home.path().to_path_buf(),
)?;
assert!(
config
.permissions
.file_system_sandbox_policy
.entries
.iter()
.any(|entry| {
entry.access == FileSystemAccessMode::Write
&& entry.path
== FileSystemPath::Path {
path: test_absolute_path("/tmp"),
}
})
);
assert_eq!(
config.permissions.sandbox_policy.get(),
&SandboxPolicy::ReadOnly {
access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: Vec::new(),
},
network_access: false,
}
);
Ok(())
}
#[test]
fn permissions_profiles_allow_unknown_special_paths_with_nested_entries() -> std::io::Result<()> {
let config = load_workspace_permission_profile(PermissionProfileToml {

View File

@@ -2154,17 +2154,18 @@ impl Config {
default_permissions,
&mut startup_warnings,
)?;
let mut sandbox_policy = file_system_sandbox_policy
.to_legacy_sandbox_policy(network_sandbox_policy, resolved_cwd.as_path())?;
if matches!(sandbox_policy, SandboxPolicy::WorkspaceWrite { .. }) {
if !file_system_sandbox_policy
.get_writable_roots_with_cwd(resolved_cwd.as_path())
.is_empty()
{
file_system_sandbox_policy = file_system_sandbox_policy
.with_additional_writable_roots(
resolved_cwd.as_path(),
&additional_writable_roots,
);
sandbox_policy = file_system_sandbox_policy
.to_legacy_sandbox_policy(network_sandbox_policy, resolved_cwd.as_path())?;
}
let sandbox_policy = file_system_sandbox_policy
.to_legacy_sandbox_policy(network_sandbox_policy, resolved_cwd.as_path())?;
(
configured_network_proxy_config,
sandbox_policy,

View File

@@ -83,7 +83,20 @@ pub enum FileSystemSpecialPath {
#[ts(optional)]
subpath: Option<PathBuf>,
},
/// Config-facing `:tmpdir` special path.
///
/// This is the broader restricted-policy temp bundle used by filesystem
/// permission profiles. On Unix it expands to the temp env roots plus
/// `/tmp` and `/tmp`'s canonical target. On Windows it expands to the
/// platform temp env roots.
Tmpdir,
/// Legacy env-backed temp roots used when bridging old
/// `SandboxPolicy::WorkspaceWrite` semantics into split filesystem policy.
///
/// Keep this narrower than [`FileSystemSpecialPath::Tmpdir`] so existing
/// `exclude_tmpdir_env_var = false` behavior does not silently widen to the
/// full temp bundle on Unix.
TmpdirEnvVar,
SlashTmp,
/// WARNING: `:special_path` tokens are part of config compatibility.
/// Do not make older runtimes reject newly introduced tokens.
@@ -644,50 +657,57 @@ impl FileSystemSandboxPolicy {
FileSystemSpecialPath::CurrentWorkingDirectory => {
if entry.access.can_write() {
workspace_root_writable = true;
} else if entry.access.can_read()
&& let Some(path) = resolve_file_system_special_path(
} else if entry.access.can_read() {
readable_roots.extend(resolve_file_system_special_paths(
value,
cwd_absolute.as_ref(),
)
{
readable_roots.push(path);
));
}
}
FileSystemSpecialPath::ProjectRoots { subpath } => {
if subpath.is_none() && entry.access.can_write() {
workspace_root_writable = true;
} else if let Some(path) =
resolve_file_system_special_path(value, cwd_absolute.as_ref())
{
} else {
let resolved_paths = resolve_file_system_special_paths(
value,
cwd_absolute.as_ref(),
);
if entry.access.can_write() {
writable_roots.push(path);
writable_roots.extend(resolved_paths);
} else if entry.access.can_read() {
readable_roots.push(path);
readable_roots.extend(resolved_paths);
}
}
}
FileSystemSpecialPath::Tmpdir => {
if entry.access.can_write() {
tmpdir_writable = true;
} else if entry.access.can_read()
&& let Some(path) = resolve_file_system_special_path(
slash_tmp_writable = true;
} else if entry.access.can_read() {
readable_roots.extend(resolve_file_system_special_paths(
value,
cwd_absolute.as_ref(),
)
{
readable_roots.push(path);
));
}
}
FileSystemSpecialPath::TmpdirEnvVar => {
if entry.access.can_write() {
tmpdir_writable = true;
} else if entry.access.can_read() {
readable_roots.extend(resolve_file_system_special_paths(
value,
cwd_absolute.as_ref(),
));
}
}
FileSystemSpecialPath::SlashTmp => {
if entry.access.can_write() {
slash_tmp_writable = true;
} else if entry.access.can_read()
&& let Some(path) = resolve_file_system_special_path(
} else if entry.access.can_read() {
readable_roots.extend(resolve_file_system_special_paths(
value,
cwd_absolute.as_ref(),
)
{
readable_roots.push(path);
));
}
}
FileSystemSpecialPath::Unknown { .. } => {}
@@ -728,11 +748,6 @@ impl FileSystemSandboxPolicy {
exclude_tmpdir_env_var: !tmpdir_writable,
exclude_slash_tmp: !slash_tmp_writable,
}
} else if !writable_roots.is_empty() || tmpdir_writable || slash_tmp_writable {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"permissions profile requests filesystem writes outside the workspace root, which is not supported until the runtime enforces FileSystemSandboxPolicy directly",
));
} else {
SandboxPolicy::ReadOnly {
access: read_only_access,
@@ -747,13 +762,13 @@ impl FileSystemSandboxPolicy {
let cwd_absolute = AbsolutePathBuf::from_absolute_path(cwd).ok();
self.entries
.iter()
.filter_map(|entry| {
resolve_entry_path(&entry.path, cwd_absolute.as_ref()).map(|path| {
ResolvedFileSystemEntry {
.flat_map(|entry| {
resolve_entry_paths(&entry.path, cwd_absolute.as_ref())
.into_iter()
.map(|path| ResolvedFileSystemEntry {
path,
access: entry.access,
}
})
})
})
.collect()
}
@@ -875,7 +890,7 @@ impl From<&SandboxPolicy> for FileSystemSandboxPolicy {
if !exclude_tmpdir_env_var {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
value: FileSystemSpecialPath::TmpdirEnvVar,
},
access: FileSystemAccessMode::Write,
});
@@ -898,21 +913,21 @@ impl From<&SandboxPolicy> for FileSystemSandboxPolicy {
fn resolve_file_system_path(
path: &FileSystemPath,
cwd: Option<&AbsolutePathBuf>,
) -> Option<AbsolutePathBuf> {
) -> Vec<AbsolutePathBuf> {
match path {
FileSystemPath::Path { path } => Some(path.clone()),
FileSystemPath::Special { value } => resolve_file_system_special_path(value, cwd),
FileSystemPath::Path { path } => vec![path.clone()],
FileSystemPath::Special { value } => resolve_file_system_special_paths(value, cwd),
}
}
fn resolve_entry_path(
fn resolve_entry_paths(
path: &FileSystemPath,
cwd: Option<&AbsolutePathBuf>,
) -> Option<AbsolutePathBuf> {
) -> Vec<AbsolutePathBuf> {
match path {
FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
} => cwd.map(absolute_root_path_for_cwd),
} => cwd.map(absolute_root_path_for_cwd).into_iter().collect(),
_ => resolve_file_system_path(path, cwd),
}
}
@@ -957,6 +972,7 @@ fn special_paths_share_target(left: &FileSystemSpecialPath, right: &FileSystemSp
FileSystemSpecialPath::CurrentWorkingDirectory,
)
| (FileSystemSpecialPath::Tmpdir, FileSystemSpecialPath::Tmpdir)
| (FileSystemSpecialPath::TmpdirEnvVar, FileSystemSpecialPath::TmpdirEnvVar)
| (FileSystemSpecialPath::SlashTmp, FileSystemSpecialPath::SlashTmp) => true,
(
FileSystemSpecialPath::CurrentWorkingDirectory,
@@ -993,11 +1009,13 @@ fn special_path_matches_absolute_path(
value: &FileSystemSpecialPath,
path: &AbsolutePathBuf,
) -> bool {
match value {
FileSystemSpecialPath::Root => path.as_path().parent().is_none(),
FileSystemSpecialPath::SlashTmp => path.as_path() == Path::new("/tmp"),
_ => false,
if matches!(value, FileSystemSpecialPath::Root) {
return path.as_path().parent().is_none();
}
resolve_cwd_independent_special_paths(value)
.into_iter()
.any(|candidate| candidate == *path)
}
/// Orders resolved entries so the most specific path wins first, then applies
@@ -1017,44 +1035,106 @@ fn absolute_root_path_for_cwd(cwd: &AbsolutePathBuf) -> AbsolutePathBuf {
.unwrap_or_else(|err| panic!("cwd root must be an absolute path: {err}"))
}
fn resolve_file_system_special_path(
fn resolve_file_system_special_paths(
value: &FileSystemSpecialPath,
cwd: Option<&AbsolutePathBuf>,
) -> Option<AbsolutePathBuf> {
) -> Vec<AbsolutePathBuf> {
match value {
FileSystemSpecialPath::Root
| FileSystemSpecialPath::Minimal
| FileSystemSpecialPath::Unknown { .. } => None,
| FileSystemSpecialPath::Unknown { .. } => Vec::new(),
FileSystemSpecialPath::CurrentWorkingDirectory => {
let cwd = cwd?;
Some(cwd.clone())
let Some(cwd) = cwd else {
return Vec::new();
};
vec![cwd.clone()]
}
FileSystemSpecialPath::ProjectRoots { subpath } => {
let cwd = cwd?;
let Some(cwd) = cwd else {
return Vec::new();
};
match subpath.as_ref() {
Some(subpath) => {
AbsolutePathBuf::resolve_path_against_base(subpath, cwd.as_path()).ok()
}
None => Some(cwd.clone()),
Some(subpath) => AbsolutePathBuf::resolve_path_against_base(subpath, cwd.as_path())
.into_iter()
.collect(),
None => vec![cwd.clone()],
}
}
FileSystemSpecialPath::Tmpdir => {
let tmpdir = std::env::var_os("TMPDIR")?;
if tmpdir.is_empty() {
None
} else {
let tmpdir = AbsolutePathBuf::from_absolute_path(PathBuf::from(tmpdir)).ok()?;
Some(tmpdir)
}
FileSystemSpecialPath::Tmpdir => resolve_tmpdir_paths(),
FileSystemSpecialPath::TmpdirEnvVar => resolve_temp_env_paths(),
FileSystemSpecialPath::SlashTmp => resolve_slash_tmp_path().into_iter().collect(),
}
}
fn resolve_cwd_independent_special_paths(value: &FileSystemSpecialPath) -> Vec<AbsolutePathBuf> {
match value {
FileSystemSpecialPath::Root => Vec::new(),
FileSystemSpecialPath::Minimal
| FileSystemSpecialPath::CurrentWorkingDirectory
| FileSystemSpecialPath::ProjectRoots { .. }
| FileSystemSpecialPath::Unknown { .. } => Vec::new(),
FileSystemSpecialPath::Tmpdir => resolve_tmpdir_paths(),
FileSystemSpecialPath::TmpdirEnvVar => resolve_temp_env_paths(),
FileSystemSpecialPath::SlashTmp => resolve_slash_tmp_path().into_iter().collect(),
}
}
fn resolve_tmpdir_paths() -> Vec<AbsolutePathBuf> {
let mut paths = resolve_temp_env_paths();
if let Some(slash_tmp) = resolve_slash_tmp_path() {
paths.push(slash_tmp.clone());
if let Ok(realpath) = slash_tmp.as_path().canonicalize()
&& let Ok(realpath) = AbsolutePathBuf::from_absolute_path(realpath)
{
paths.push(realpath);
}
FileSystemSpecialPath::SlashTmp => {
#[allow(clippy::expect_used)]
let slash_tmp = AbsolutePathBuf::from_absolute_path("/tmp").expect("/tmp is absolute");
if !slash_tmp.as_path().is_dir() {
return None;
}
Some(slash_tmp)
}
dedup_absolute_paths(paths, /*normalize_effective_paths*/ false)
}
pub(crate) fn resolve_temp_env_paths() -> Vec<AbsolutePathBuf> {
dedup_absolute_paths(
temp_env_var_keys()
.iter()
.filter_map(|key| resolve_temp_env_path(key))
.collect(),
/*normalize_effective_paths*/ false,
)
}
#[cfg(windows)]
const fn temp_env_var_keys() -> &'static [&'static str] {
&["TEMP", "TMP"]
}
#[cfg(not(windows))]
const fn temp_env_var_keys() -> &'static [&'static str] {
&["TMPDIR"]
}
fn resolve_temp_env_path(key: &str) -> Option<AbsolutePathBuf> {
let value = std::env::var_os(key)?;
if value.is_empty() {
None
} else {
AbsolutePathBuf::from_absolute_path(PathBuf::from(value)).ok()
}
}
fn resolve_slash_tmp_path() -> Option<AbsolutePathBuf> {
#[cfg(not(unix))]
{
None
}
#[cfg(unix)]
{
#[allow(clippy::expect_used)]
let slash_tmp = AbsolutePathBuf::from_absolute_path("/tmp").expect("/tmp is absolute");
if !slash_tmp.as_path().is_dir() {
return None;
}
Some(slash_tmp)
}
}
@@ -1760,12 +1840,14 @@ mod tests {
#[cfg(unix)]
#[test]
fn tmpdir_special_path_canonicalizes_symlinked_tmpdir() {
fn tmpdir_env_var_special_path_canonicalizes_symlinked_tmpdir() {
if std::env::var_os(SYMLINKED_TMPDIR_TEST_ENV).is_none() {
let output = std::process::Command::new(std::env::current_exe().expect("test binary"))
.env(SYMLINKED_TMPDIR_TEST_ENV, "1")
.arg("--exact")
.arg("permissions::tests::tmpdir_special_path_canonicalizes_symlinked_tmpdir")
.arg(
"permissions::tests::tmpdir_env_var_special_path_canonicalizes_symlinked_tmpdir",
)
.output()
.expect("run tmpdir subprocess test");
@@ -1812,7 +1894,7 @@ mod tests {
let policy = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
value: FileSystemSpecialPath::TmpdirEnvVar,
},
access: FileSystemAccessMode::Write,
},
@@ -1842,6 +1924,143 @@ mod tests {
);
}
#[cfg(unix)]
#[test]
fn tmpdir_special_path_includes_tmpdir_env_and_tmp_namespace() {
if std::env::var_os(SYMLINKED_TMPDIR_TEST_ENV).is_none() {
let output = std::process::Command::new(std::env::current_exe().expect("test binary"))
.env(SYMLINKED_TMPDIR_TEST_ENV, "1")
.arg("--exact")
.arg(
"permissions::tests::tmpdir_special_path_includes_tmpdir_env_and_tmp_namespace",
)
.output()
.expect("run tmpdir subprocess test");
assert!(
output.status.success(),
"tmpdir subprocess test failed\nstdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
return;
}
let cwd = TempDir::new().expect("tempdir");
let real_tmpdir = cwd.path().join("real-tmpdir");
let link_tmpdir = cwd.path().join("link-tmpdir");
fs::create_dir_all(&real_tmpdir).expect("create real tmpdir");
symlink_dir(&real_tmpdir, &link_tmpdir).expect("create symlinked tmpdir");
unsafe {
std::env::set_var("TMPDIR", &link_tmpdir);
}
let slash_tmp =
AbsolutePathBuf::from_absolute_path("/tmp").expect("/tmp should be absolute");
let slash_tmp_realpath = AbsolutePathBuf::from_absolute_path(
slash_tmp
.as_path()
.canonicalize()
.expect("canonicalize /tmp"),
)
.expect("absolute canonical /tmp");
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
}]);
assert!(policy.can_write_path_with_cwd(link_tmpdir.as_path(), cwd.path()));
assert!(policy.can_write_path_with_cwd(Path::new("/tmp/codex-protocol"), cwd.path()));
assert!(
policy.can_write_path_with_cwd(
slash_tmp_realpath
.join("codex-protocol")
.expect("valid tmp child")
.as_path(),
cwd.path(),
)
);
let writable_roots = policy.get_writable_roots_with_cwd(cwd.path());
assert!(writable_roots.iter().any(|root| {
root.root
== AbsolutePathBuf::from_absolute_path(
real_tmpdir.canonicalize().expect("canonicalize tmpdir"),
)
.expect("absolute canonical tmpdir")
}));
assert!(
writable_roots
.iter()
.any(|root| root.root == slash_tmp_realpath)
);
}
#[test]
fn tmpdir_write_without_workspace_root_falls_back_to_read_only_legacy_policy()
-> std::io::Result<()> {
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
}]);
let sandbox_policy = policy.to_legacy_sandbox_policy(
NetworkSandboxPolicy::Restricted,
Path::new("/tmp/workspace"),
)?;
assert_eq!(
sandbox_policy,
SandboxPolicy::ReadOnly {
access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: Vec::new(),
},
network_access: false,
}
);
assert!(policy.needs_direct_runtime_enforcement(
NetworkSandboxPolicy::Restricted,
Path::new("/tmp/workspace"),
));
Ok(())
}
#[test]
fn legacy_workspace_write_uses_tmpdir_env_var_special_path() {
let file_system_policy = FileSystemSandboxPolicy::from(&SandboxPolicy::WorkspaceWrite {
writable_roots: Vec::new(),
read_only_access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: Vec::new(),
},
network_access: false,
exclude_tmpdir_env_var: false,
exclude_slash_tmp: true,
});
assert!(file_system_policy.entries.iter().any(|entry| {
entry.access == FileSystemAccessMode::Write
&& entry.path
== FileSystemPath::Special {
value: FileSystemSpecialPath::TmpdirEnvVar,
}
}));
assert!(!file_system_policy.entries.iter().any(|entry| {
entry.access == FileSystemAccessMode::Write
&& entry.path
== FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
}
}));
}
#[test]
fn resolve_access_with_cwd_uses_most_specific_entry() {
let cwd = TempDir::new().expect("tempdir");

View File

@@ -79,6 +79,7 @@ pub use crate::permissions::FileSystemSandboxKind;
pub use crate::permissions::FileSystemSandboxPolicy;
pub use crate::permissions::FileSystemSpecialPath;
pub use crate::permissions::NetworkSandboxPolicy;
use crate::permissions::resolve_temp_env_paths;
pub use crate::request_permissions::RequestPermissionsArgs;
pub use crate::request_user_input::RequestUserInputEvent;
@@ -1047,28 +1048,12 @@ impl SandboxPolicy {
}
}
// Include $TMPDIR unless explicitly excluded. On macOS, TMPDIR
// is per-user, so writes to TMPDIR should not be readable by
// other users on the system.
//
// By comparison, TMPDIR is not guaranteed to be defined on
// Linux or Windows, but supporting it here gives users a way to
// provide the model with their own temporary directory without
// having to hardcode it in the config.
if !exclude_tmpdir_env_var
&& let Some(tmpdir) = std::env::var_os("TMPDIR")
&& !tmpdir.is_empty()
{
match AbsolutePathBuf::from_absolute_path(PathBuf::from(&tmpdir)) {
Ok(tmpdir_path) => {
roots.push(tmpdir_path);
}
Err(e) => {
error!(
"Ignoring invalid TMPDIR value {tmpdir:?} for sandbox writable root: {e}",
);
}
}
// Include platform temp env roots unless explicitly excluded.
// On Unix, this keeps the legacy TMPDIR behavior. On Windows,
// this picks up TEMP/TMP so legacy workspace-write policies
// keep matching the host temp directory.
if !exclude_tmpdir_env_var {
roots.extend(resolve_temp_env_paths());
}
// For each root, compute subpaths that should remain read-only.
@@ -4181,7 +4166,7 @@ mod tests {
}
#[test]
fn file_system_policy_rejects_legacy_bridge_for_non_workspace_writes() {
fn file_system_policy_falls_back_to_read_only_legacy_bridge_for_non_workspace_writes() {
let cwd = if cfg!(windows) {
Path::new(r"C:\workspace")
} else {
@@ -4199,14 +4184,19 @@ mod tests {
access: FileSystemAccessMode::Write,
}]);
let err = policy
let legacy_policy = policy
.to_legacy_sandbox_policy(NetworkSandboxPolicy::Restricted, cwd)
.expect_err("non-workspace writes should be rejected");
.expect("non-workspace writes should fall back to read-only");
assert!(
err.to_string()
.contains("filesystem writes outside the workspace root"),
"{err}"
assert_eq!(
legacy_policy,
SandboxPolicy::ReadOnly {
access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: Vec::new(),
},
network_access: false,
}
);
}

View File

@@ -90,12 +90,6 @@
(allow file-read* file-test-existence file-write-data file-ioctl
(literal "/dev/dtracehelper"))
; Scratch space so tools can create temp files.
(allow file-read* file-test-existence file-write* (subpath "/tmp"))
(allow file-read* file-write* (subpath "/private/tmp"))
(allow file-read* file-write* (subpath "/var/tmp"))
(allow file-read* file-write* (subpath "/private/var/tmp"))
; Allow reading standard config directories.
(allow file-read* (subpath "/etc"))
(allow file-read* (subpath "/private/etc"))

View File

@@ -60,6 +60,31 @@ fn base_policy_allows_node_cpu_sysctls() {
);
}
#[test]
fn restricted_platform_defaults_do_not_include_tmp_write_rules() {
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
}]);
let args = create_seatbelt_command_args_for_policies(
vec!["/bin/true".to_string()],
&file_system_policy,
NetworkSandboxPolicy::Restricted,
Path::new("/"),
false,
None,
);
let policy = seatbelt_policy_arg(&args);
assert!(!policy.contains("(subpath \"/tmp\")"));
assert!(!policy.contains("(subpath \"/private/tmp\")"));
assert!(!policy.contains("(subpath \"/var/tmp\")"));
assert!(!policy.contains("(subpath \"/private/var/tmp\")"));
}
#[test]
fn create_seatbelt_args_routes_network_through_proxy_ports() {
let policy = dynamic_network_policy(

View File

@@ -1,7 +1,5 @@
exports_files([
"aws-lc-sys_memcmp_check.patch",
"rules_rust_windows_gnullvm_build_script.patch",
"rules_rs_windows_gnullvm_exec.patch",
"rusty_v8_prebuilt_out_dir.patch",
"v8_bazel_rules.patch",
"v8_module_deps.patch",

View File

@@ -1,147 +0,0 @@
diff --git a/rs/experimental/platforms/triples.bzl b/rs/experimental/platforms/triples.bzl
--- a/rs/experimental/platforms/triples.bzl
+++ b/rs/experimental/platforms/triples.bzl
@@ -30,6 +30,7 @@
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
+ "x86_64-pc-windows-gnullvm",
"aarch64-pc-windows-msvc",
"x86_64-apple-darwin",
"aarch64-apple-darwin",
diff --git a/rs/experimental/toolchains/declare_rustc_toolchains.bzl b/rs/experimental/toolchains/declare_rustc_toolchains.bzl
--- a/rs/experimental/toolchains/declare_rustc_toolchains.bzl
+++ b/rs/experimental/toolchains/declare_rustc_toolchains.bzl
@@ -10,6 +10,11 @@
return "beta"
return "stable"
+def _exec_triple_suffix(exec_triple):
+ if exec_triple.system == "windows":
+ return "{}_{}_{}".format(exec_triple.system, exec_triple.arch, exec_triple.abi)
+ return "{}_{}".format(exec_triple.system, exec_triple.arch)
+
def declare_rustc_toolchains(
*,
version,
@@ -23,15 +28,14 @@
for triple in execs:
exec_triple = _parse_triple(triple)
- triple_suffix = exec_triple.system + "_" + exec_triple.arch
+ triple_suffix = _exec_triple_suffix(exec_triple)
rustc_repo_label = "@rustc_{}_{}//:".format(triple_suffix, version_key)
cargo_repo_label = "@cargo_{}_{}//:".format(triple_suffix, version_key)
clippy_repo_label = "@clippy_{}_{}//:".format(triple_suffix, version_key)
- rust_toolchain_name = "{}_{}_{}_rust_toolchain".format(
- exec_triple.system,
- exec_triple.arch,
+ rust_toolchain_name = "{}_{}_rust_toolchain".format(
+ triple_suffix,
version_key,
)
@@ -90,11 +94,8 @@
target_key = sanitize_triple(target_triple)
native.toolchain(
- name = "{}_{}_to_{}_{}".format(exec_triple.system, exec_triple.arch, target_key, version_key),
- exec_compatible_with = [
- "@platforms//os:" + exec_triple.system,
- "@platforms//cpu:" + exec_triple.arch,
- ],
+ name = "{}_to_{}_{}".format(triple_suffix, target_key, version_key),
+ exec_compatible_with = triple_to_constraint_set(triple),
target_compatible_with = triple_to_constraint_set(target_triple),
target_settings = [
"@rules_rust//rust/toolchain/channel:" + channel,
diff --git a/rs/experimental/toolchains/declare_rustfmt_toolchains.bzl b/rs/experimental/toolchains/declare_rustfmt_toolchains.bzl
--- a/rs/experimental/toolchains/declare_rustfmt_toolchains.bzl
+++ b/rs/experimental/toolchains/declare_rustfmt_toolchains.bzl
@@ -1,6 +1,6 @@
load("@rules_rust//rust:toolchain.bzl", "rustfmt_toolchain")
load("@rules_rust//rust/platform:triple.bzl", _parse_triple = "triple")
-load("//rs/experimental/platforms:triples.bzl", "SUPPORTED_EXEC_TRIPLES")
+load("//rs/experimental/platforms:triples.bzl", "SUPPORTED_EXEC_TRIPLES", "triple_to_constraint_set")
load("//rs/experimental/toolchains:toolchain_utils.bzl", "sanitize_version")
def _channel(version):
@@ -10,6 +10,11 @@
return "beta"
return "stable"
+def _exec_triple_suffix(exec_triple):
+ if exec_triple.system == "windows":
+ return "{}_{}_{}".format(exec_triple.system, exec_triple.arch, exec_triple.abi)
+ return "{}_{}".format(exec_triple.system, exec_triple.arch)
+
def declare_rustfmt_toolchains(
*,
version,
@@ -22,14 +27,13 @@
for triple in execs:
exec_triple = _parse_triple(triple)
- triple_suffix = exec_triple.system + "_" + exec_triple.arch
+ triple_suffix = _exec_triple_suffix(exec_triple)
rustc_repo_label = "@rustc_{}_{}//:".format(triple_suffix, version_key)
rustfmt_repo_label = "@rustfmt_{}_{}//:".format(triple_suffix, rustfmt_version_key)
- rustfmt_toolchain_name = "{}_{}_{}_rustfmt_toolchain".format(
- exec_triple.system,
- exec_triple.arch,
+ rustfmt_toolchain_name = "{}_{}_rustfmt_toolchain".format(
+ triple_suffix,
version_key,
)
@@ -43,11 +47,8 @@
)
native.toolchain(
- name = "{}_{}_rustfmt_{}".format(exec_triple.system, exec_triple.arch, version_key),
- exec_compatible_with = [
- "@platforms//os:" + exec_triple.system,
- "@platforms//cpu:" + exec_triple.arch,
- ],
+ name = "{}_rustfmt_{}".format(triple_suffix, version_key),
+ exec_compatible_with = triple_to_constraint_set(triple),
target_compatible_with = [],
target_settings = [
"@rules_rust//rust/toolchain/channel:" + channel,
diff --git a/rs/experimental/toolchains/module_extension.bzl b/rs/experimental/toolchains/module_extension.bzl
--- a/rs/experimental/toolchains/module_extension.bzl
+++ b/rs/experimental/toolchains/module_extension.bzl
@@ -37,6 +37,11 @@
return "aarch64"
return arch
+def _exec_triple_suffix(exec_triple):
+ if exec_triple.system == "windows":
+ return "{}_{}_{}".format(exec_triple.system, exec_triple.arch, exec_triple.abi)
+ return "{}_{}".format(exec_triple.system, exec_triple.arch)
+
def _sanitize_path_fragment(path):
return path.replace("/", "_").replace(":", "_")
@@ -181,7 +186,7 @@
for triple in SUPPORTED_EXEC_TRIPLES:
exec_triple = _parse_triple(triple)
- triple_suffix = exec_triple.system + "_" + exec_triple.arch
+ triple_suffix = _exec_triple_suffix(exec_triple)
rustc_name = "rustc_{}_{}".format(triple_suffix, version_key)
rustc_repository(
@@ -230,7 +235,7 @@
for triple in SUPPORTED_EXEC_TRIPLES:
exec_triple = _parse_triple(triple)
- triple_suffix = exec_triple.system + "_" + exec_triple.arch
+ triple_suffix = _exec_triple_suffix(exec_triple)
rustfmt_repository(
name = "rustfmt_{}_{}".format(triple_suffix, version_key),

View File

@@ -1,38 +0,0 @@
diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl
--- a/cargo/private/cargo_build_script.bzl
+++ b/cargo/private/cargo_build_script.bzl
@@ -120,6 +120,25 @@
executable = True,
)
+def _strip_stack_protector_for_windows_llvm_mingw(toolchain, args):
+ """Drop stack protector flags unsupported by llvm-mingw build-script probes."""
+ if "windows-gnullvm" not in toolchain.target_flag_value:
+ return args
+
+ uses_llvm_mingw = False
+ for arg in args:
+ if "mingw-w64-" in arg:
+ uses_llvm_mingw = True
+ break
+
+ if not uses_llvm_mingw:
+ return args
+
+ # llvm-mingw does not ship libssp_nonshared, so forwarding stack-protector
+ # flags through CFLAGS/CXXFLAGS breaks build.rs probe binaries compiled via
+ # cc-rs.
+ return [arg for arg in args if not arg.startswith("-fstack-protector")]
+
def get_cc_compile_args_and_env(cc_toolchain, feature_configuration):
"""Gather cc environment variables from the given `cc_toolchain`
@@ -503,6 +522,8 @@
if not env["AR"]:
env["AR"] = cc_toolchain.ar_executable
+ cc_c_args = _strip_stack_protector_for_windows_llvm_mingw(toolchain, cc_c_args)
+ cc_cxx_args = _strip_stack_protector_for_windows_llvm_mingw(toolchain, cc_cxx_args)
# Populate CFLAGS and CXXFLAGS that cc-rs relies on when building from source, in particular
# to determine the deployment target when building for apple platforms (`macosx-version-min`
# for example, itself derived from the `macos_minimum_os` Bazel argument).

View File

@@ -44,33 +44,43 @@ export function createTestClient(options: CreateTestClientOptions = {}): TestCli
codexPathOverride: codexExecPath,
baseUrl: options.baseUrl,
apiKey: options.apiKey,
config: mergeTestProviderConfig(options.baseUrl, options.config),
config: mergeTestConfig(options.baseUrl, options.config),
env,
}),
};
}
function mergeTestProviderConfig(
function mergeTestConfig(
baseUrl: string | undefined,
config: CodexConfigObject | undefined,
): CodexConfigObject | undefined {
if (!baseUrl || hasExplicitProviderConfig(config)) {
return config;
}
const mergedConfig: CodexConfigObject | undefined =
!baseUrl || hasExplicitProviderConfig(config)
? config
: {
...config,
// Built-in providers are merged before user config, so tests need a
// custom provider entry to force SSE against the local mock server.
model_provider: "mock",
model_providers: {
mock: {
name: "Mock provider for test",
base_url: baseUrl,
wire_api: "responses",
supports_websockets: false,
},
},
};
const featureOverrides = mergedConfig?.features;
// Built-in providers are merged before user config, so tests need a custom
// provider entry to force SSE against the local mock server.
return {
...config,
model_provider: "mock",
model_providers: {
mock: {
name: "Mock provider for test",
base_url: baseUrl,
wire_api: "responses",
supports_websockets: false,
},
},
...mergedConfig,
// Disable plugins in SDK integration tests so background curated-plugin
// sync does not race temp CODEX_HOME cleanup.
features:
featureOverrides && typeof featureOverrides === "object" && !Array.isArray(featureOverrides)
? { ...featureOverrides, plugins: false }
: { plugins: false },
};
}

View File

@@ -41,17 +41,11 @@ if not defined manifest if exist "%~dpn0.runfiles_manifest" set "manifest=%~dpn0
if not defined manifest if exist "%~f0.exe.runfiles_manifest" set "manifest=%~f0.exe.runfiles_manifest"
if defined manifest if exist "%manifest%" (
rem Read the manifest directly instead of shelling out to findstr. In the
rem GitHub Windows runner, the nested `findstr` path produced
rem `FINDSTR: Cannot open D:MANIFEST`, which then broke runfile resolution for
rem Bazel tests even though the manifest file was present.
for /f "usebackq tokens=1,* delims= " %%A in ("%manifest%") do (
if "%%A"=="%logical_path%" (
endlocal & set "%~1=%%B" & exit /b 0
)
if "%%A"=="%workspace_logical_path%" (
endlocal & set "%~1=%%B" & exit /b 0
)
for /f "usebackq tokens=1,* delims= " %%A in (`findstr /b /c:"%logical_path% " "%manifest%"`) do (
endlocal & set "%~1=%%B" & exit /b 0
)
for /f "usebackq tokens=1,* delims= " %%A in (`findstr /b /c:"%workspace_logical_path% " "%manifest%"`) do (
endlocal & set "%~1=%%B" & exit /b 0
)
)