Windows execpolicy false positive when Start-Process and an unrelated URL appear in the same PowerShell script

Open 💬 1 comment Opened Aug 22, 2026 by keyou

What version of Codex CLI is running?

codex-cli 0.146.0

The failure was reproduced on 0.146.0. I also checked the latest stable release, 0.149.0, and the current main branch; both still contain the same relevant classifier logic described below.

What subscription do you have?

Pro

Which model were you using?

gpt-5.6-sol

What platform is your computer?

Microsoft Windows NT 10.0.26100.0 x64 (Windows 11 Professional)

What terminal emulator and version are you using (if applicable)?

Windows Terminal, PowerShell 7.6.3

Codex doctor report

The full report contains private local paths, so the relevant redacted fields are included below. All relevant runtime, config, auth, sandbox, and installation checks were OK; the overall warning was an unrelated update-status warning.

{
  "schemaVersion": 1,
  "overallStatus": "warning",
  "codexVersion": "0.146.0",
  "runtime": {
    "platform": "windows-x86_64",
    "installMethod": "npm"
  },
  "config": {
    "model": "gpt-5.6-sol",
    "modelProvider": "openai"
  },
  "system": {
    "os": "Windows 10.0.26100 (Windows 11 Professional) [64-bit]"
  },
  "terminal": {
    "terminal": "Windows Terminal",
    "powershell": "7.6.3"
  }
}

What issue are you seeing?

On Windows, execpolicy falsely classifies a benign compound PowerShell script as a dangerous URL launch when the script contains both:

  1. a local Start-Process invocation with no URL argument, and
  2. an unrelated Invoke-RestMethod call to an HTTP URL later in the script.

With approval policy never, Codex rejects the command before PowerShell starts:

"C:\Program Files\PowerShell\7\pwsh.exe" -Command '...'
rejected: blocked by policy

The rejection is immediate (Wall time 0.0 seconds). Running the local Start-Process command by itself succeeds, and running the HTTP probe by itself succeeds. Combining them in one PowerShell script triggers the rejection.

This affects a common local-development pattern: start a local application or development server, then poll its loopback health/CDP endpoint until it is ready.

What steps can reproduce the bug?

  1. Run Codex on Windows with approvals disabled, for example:

``text
codex -a never -s danger-full-access
``

  1. Ask Codex to execute this single PowerShell tool command:

``powershell
$process = Start-Process

-FilePath "$env:SystemRoot\System32\cmd.exe"
-ArgumentList @('/c', 'exit', '0')

-WindowStyle Hidden `
-PassThru

try {
Invoke-RestMethod
-Uri 'http://127.0.0.1:9/health'

-TimeoutSec 1
} catch {}

$process.Id
```

  1. Observe that Codex rejects the entire command with rejected: blocked by policy before pwsh.exe runs.
  1. Execute the Start-Process portion and the Invoke-RestMethod portion as two separate Codex tool calls. Both are allowed, confirming that the combination is what triggers the false positive.

What is the expected behavior?

Execpolicy should only classify Start-Process as a dangerous URL/ShellExecute launch when an HTTP/HTTPS URL is an argument of that Start-Process invocation (or otherwise belongs to the same command expression).

An unrelated URL used by a separate Invoke-RestMethod command should not cause a local executable launch to be rejected.

The truly dangerous case must remain blocked:

Start-Process 'https://example.com'

Additional information

The apparent root cause is in is_dangerous_powershell_words. It computes has_url across the full flattened token list and independently checks whether any token contains start-process:

https://github.com/openai/codex/blob/rust-v0.149.0/codex-rs/shell-command/src/command_safety/windows_dangerous_commands.rs#L36-L52

As a result, the URL and Start-Process token do not need to belong to the same command. The same implementation is still present on main:

https://github.com/openai/codex/blob/main/codex-rs/shell-command/src/command_safety/windows_dangerous_commands.rs#L36-L52

When the heuristic marks the command dangerous and approval_policy is never, the decision becomes Forbidden:

https://github.com/openai/codex/blob/rust-v0.149.0/codex-rs/core/src/exec_policy.rs#L728-L741

A regression test could assert that this is not dangerous:

Start-Process local.exe; Invoke-RestMethod http://127.0.0.1:1234/health

while preserving the existing test that Start-Process 'https://example.com' is dangerous. A robust fix would associate URL arguments with the specific PowerShell command/AST node rather than scanning the whole script token set.

Related PowerShell execpolicy/parser reports, but not duplicates of this false positive:

View original on GitHub ↗

1 Comment

Generoustandard · 1 day ago

Additional Windows desktop occurrence, observed on 2026-08-27. This appears closely related, with one variation: the unrelated HTTPS URL was an environment configuration value, not a health-probe URL.

Environment

  • Installed desktop package: OpenAI.Codex 26.820.9563.0.
  • Version reported by the executable of the running desktop app-server process: codex-cli 0.150.0-alpha.8.
  • Separately installed npm CLI: codex-cli 0.117.0. This is not the running desktop app-server version above.
  • Microsoft Windows NT 10.0.26200.0, x64; tool shell PowerShell 7.6.4.
  • Task permission context and relevant user/project config entries: sandbox_mode = "danger-full-access", approval_policy = "never".
  • Existing local checkout; no new branch or worktree.

Observed operation

The user authorized an isolated loopback QA backend while preserving the existing development server and user data. One PowerShell tool invocation combined unused-port/non-existing-directory checks, isolated QA storage creation, process-local test environment assignments, and a local Python/Uvicorn launch with Start-Process -WindowStyle Hidden and redirected logs.

Abbreviated, redacted command shape below. This is not a verified minimal reproduction: paths and the API-base value are placeholders; the original invocation also contained the checks and other assignments described above.

$env:LLM_API_BASE_URL = 'https://example.invalid/v1'

Start-Process `
  -FilePath '<project>\.venv\Scripts\python.exe' `
  -ArgumentList @('-m', 'uvicorn', 'backend.app.main:app',
                  '--host', '127.0.0.1', '--port', '8167', '--no-access-log') `
  -WorkingDirectory '<project>' `
  -WindowStyle Hidden `
  -RedirectStandardOutput '<isolated-qa>\backend.stdout.log' `
  -RedirectStandardError '<isolated-qa>\backend.stderr.log' `
  -PassThru

The URL was not an argument to Start-Process. It was child-backend configuration; no URL/ShellExecute launch was intended. No API key literal was present in the command.

Actual result

The entire tool invocation was rejected before PowerShell was created:

CreateProcess { message: "Rejected(... rejected: blocked by policy)" }
Wall time 0.0 seconds

Explicit user re-authorization and repetition of the same launch request did not change the result. No QA runtime directory or QA listeners were created. The existing server was unchanged; frontend launch and browser acceptance tests were not attempted.

An additional diagnostic placed the original command in a single-quoted here-string and passed it as data to codex execpolicy check --rules ... -- pwsh.exe -Command <command>. That outer tool invocation was also rejected before execution. This is not an execpolicy check verdict: the checker never ran and no matched-rule information was obtained.

Successful controls and limits

  • File reads/edits, git status, Python tests and production frontend builds succeeded in the same task; version-only CLI invocations also succeeded.
  • No permission/rule files were changed and no alternate execution path was used to get around the refusal.
  • Fresh-task reproduction, minimized positive/negative controls, and split-command comparison have not been performed for this occurrence.
  • The effective host-side rejection reason is unknown. The classifier code linked in this issue is consistent with our command shape, but this comment does not establish which classifier/rule the running desktop build actually used.

Please check whether this is the same whole-script Start-Process + unrelated-URL heuristic in the desktop app-server version above. An actionable denial reason/rule identifier would help distinguish an intended restriction from a false positive without changing safety settings. Protection against actual URL/ShellExecute launches should remain intact.

No private project name, username, real local path, credentials, customer data, full config file, or session transcript is included.