mirror of
https://github.com/openai/codex.git
synced 2026-05-16 17:23:57 +00:00
## Why The split filesystem policy stack already supports exact and glob `access = none` read restrictions on macOS and Linux. Windows still needed subprocess handling for those deny-read policies without claiming enforcement from a backend that cannot provide it. ## Key finding The unelevated restricted-token backend cannot safely enforce deny-read overlays. Its `WRITE_RESTRICTED` token model is authoritative for write checks, not read denials, so this PR intentionally fails that backend closed when deny-read overrides are present instead of claiming unsupported enforcement. ## What changed This PR adds the Windows deny-read enforcement layer and makes the backend split explicit: - Resolves Windows deny-read filesystem policy entries into concrete ACL targets. - Preserves exact missing paths so they can be materialized and denied before an enforceable sandboxed process starts. - Snapshot-expands existing glob matches into ACL targets for Windows subprocess enforcement. - Honors `glob_scan_max_depth` when expanding Windows deny-read globs. - Plans both the configured lexical path and the canonical target for existing paths so reparse-point aliases are covered. - Threads deny-read overrides through the elevated/logon-user Windows sandbox backend and unified exec. - Applies elevated deny-read ACLs synchronously before command launch rather than delegating them to the background read-grant helper. - Reconciles persistent deny-read ACEs per sandbox principal so policy changes do not leave stale deny-read ACLs behind. - Fails closed on the unelevated restricted-token backend when deny-read overrides are present, because its `WRITE_RESTRICTED` token model is not authoritative for read denials. ## Landed prerequisites These prerequisite PRs are already on `main`: 1. #15979 `feat(permissions): add glob deny-read policy support` 2. #18096 `feat(sandbox): add glob deny-read platform enforcement` 3. #17740 `feat(config): support managed deny-read requirements` This PR targets `main` directly and contains only the Windows deny-read enforcement layer. ## Implementation notes - Exact deny-read paths remain enforceable on the elevated path even when they do not exist yet: Windows materializes the missing path before applying the deny ACE, so the sandboxed command cannot create and read it during the same run. - Existing exact deny paths are preserved lexically until the ACL planner, which then adds the canonical target as a second ACL target when needed. That keeps both the configured alias and the resolved object covered. - Windows ACLs do not consume Codex glob syntax directly, so glob deny-read entries are expanded to the concrete matches that exist before process launch. - Glob traversal deduplicates directory visits within each pattern walk to avoid cycles, without collapsing distinct lexical roots that happen to resolve to the same target. - Persistent deny-read ACL state is keyed by sandbox principal SID, so cleanup only removes ACEs owned by the same backend principal. - Deny-read ACEs are fail-closed on the elevated path: setup aborts if mandatory deny-read ACL application fails. - Unelevated restricted-token sessions reject deny-read overrides early instead of running with a silently unenforceable read policy. ## Verification - `cargo test -p codex-core windows_restricted_token_rejects_unreadable_split_carveouts` - `just fmt` - `just fix -p codex-core` - `just fix -p codex-windows-sandbox` - GitHub Actions rerun is in progress on the pushed head. --------- Co-authored-by: Codex <noreply@openai.com>
64 lines
2.0 KiB
Python
64 lines
2.0 KiB
Python
load("//:defs.bzl", "codex_rust_crate")
|
|
|
|
filegroup(
|
|
name = "model_availability_nux_fixtures",
|
|
srcs = [
|
|
"tests/cli_responses_fixture.sse",
|
|
],
|
|
visibility = ["//visibility:public"],
|
|
)
|
|
|
|
codex_rust_crate(
|
|
name = "core",
|
|
crate_name = "codex_core",
|
|
compile_data = glob(
|
|
include = ["**"],
|
|
exclude = [
|
|
"**/* *",
|
|
"BUILD.bazel",
|
|
"Cargo.toml",
|
|
],
|
|
allow_empty = True,
|
|
),
|
|
rustc_env = {
|
|
# Keep manifest-root path lookups inside the Bazel execroot for code
|
|
# that relies on env!("CARGO_MANIFEST_DIR").
|
|
"CARGO_MANIFEST_DIR": "codex-rs/core",
|
|
},
|
|
integration_compile_data_extra = [
|
|
"//codex-rs/apply-patch:apply_patch_tool_instructions.md",
|
|
"templates/realtime/backend_prompt.md",
|
|
],
|
|
integration_test_timeout = "long",
|
|
test_data_extra = [
|
|
"config.schema.json",
|
|
] + glob([
|
|
"src/**/snapshots/**",
|
|
]) + [
|
|
# This is a bit of a hack, but empirically, some of our integration tests
|
|
# are relying on the presence of this file as a repo root marker. When
|
|
# running tests locally, this "just works," but in remote execution,
|
|
# the working directory is different and so the file is not found unless it
|
|
# is explicitly added as test data.
|
|
#
|
|
# TODO(aibrahim): Update the tests so that `just bazel-remote-test`
|
|
# succeeds without this workaround.
|
|
"//:AGENTS.md",
|
|
],
|
|
test_shard_counts = {
|
|
"core-all-test": 16,
|
|
"core-unit-tests": 8,
|
|
},
|
|
test_tags = ["no-sandbox"],
|
|
unit_test_timeout = "long",
|
|
extra_binaries = [
|
|
"//codex-rs/bwrap:bwrap",
|
|
"//codex-rs/linux-sandbox:codex-linux-sandbox",
|
|
"//codex-rs/rmcp-client:test_stdio_server",
|
|
"//codex-rs/rmcp-client:test_streamable_http_server",
|
|
"//codex-rs/cli:codex",
|
|
"//codex-rs/windows-sandbox-rs:codex-command-runner",
|
|
"//codex-rs/windows-sandbox-rs:codex-windows-sandbox-setup",
|
|
],
|
|
)
|