Don't ask again should use prefix of first command in pipe, not last command

Open 💬 2 comments Opened May 26, 2026 by brewba

What version of Codex CLI is running?

codex-cli 0.133.0

What subscription do you have?

Enterprise - NVIDIA

Which model were you using?

gpt-5.5

What platform is your computer?

Linux 6.17.0-23-generic x86_64 x86_64

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

kitty terminal emulator; nomachine for remote persistent access

Codex doctor report

{
  "schemaVersion": 1,
  "generatedAt": "1779807014s since unix epoch",
  "overallStatus": "warning",
  "codexVersion": "0.133.0",
  "checks": {
    "app_server.status": {
      "id": "app_server.status",
      "category": "app-server",
      "status": "ok",
      "summary": "background server is not running",
      "details": {
        "control socket": "/home/bewbank/.codex/app-server-control/app-server-control.sock",
        "daemon state dir": "/home/bewbank/.codex/app-server-daemon",
        "mode": "ephemeral",
        "pid file": "/home/bewbank/.codex/app-server-daemon/app-server.pid (missing)",
        "settings": "/home/bewbank/.codex/app-server-daemon/settings.json (missing)",
        "status": "not running",
        "update-loop pid file": "/home/bewbank/.codex/app-server-daemon/app-server-updater.pid (missing)"
      },
      "remediation": null,
      "durationMs": 0
    },
    "auth.credentials": {
      "id": "auth.credentials",
      "category": "auth",
      "status": "ok",
      "summary": "auth is configured",
      "details": {
        "auth file": "/home/bewbank/.codex/auth.json",
        "auth storage mode": "File",
        "stored API key": "false",
        "stored ChatGPT tokens": "true",
        "stored agent identity": "false",
        "stored auth mode": "chatgpt"
      },
      "remediation": null,
      "durationMs": 0
    },
    "config.load": {
      "id": "config.load",
      "category": "config",
      "status": "ok",
      "summary": "config loaded",
      "details": {
        "CODEX_HOME": "/home/bewbank/.codex",
        "config.toml": "/home/bewbank/.codex/config.toml",
        "config.toml parse": "ok",
        "cwd": "/home/bewbank/pproot/ndas3",
        "enabled feature flags": "shell_tool, unified_exec, shell_snapshot, terminal_resize_reflow, sqlite, memories, hooks, enable_request_compression, multi_agent, apps, tool_suggest, plugins, plugin_hooks, in_app_browser, browser_use, browser_use_external, computer_use, plugin_sharing, image_generation, skill_mcp_dependency_install, steer, guardian_approval, goals, collaboration_modes, tool_call_mcp_elicitation, personality, fast_mode, tui_app_server, workspace_dependencies",
        "feature flag overrides": "memories=true",
        "feature flags enabled": "29",
        "log dir": "/home/bewbank/.codex/log",
        "mcp servers": "0",
        "model": "<default>",
        "model provider": "openai",
        "sqlite home": "/home/bewbank/.codex"
      },
      "remediation": null,
      "durationMs": 0
    },
    "installation": {
      "id": "installation",
      "category": "install",
      "status": "ok",
      "summary": "installation looks consistent",
      "details": {
        "PATH codex #1": "/usr/local/bin/codex",
        "current executable": "/usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin/codex",
        "install context": "npm (package /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl, bin /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin, resources /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-resources, path /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-path)",
        "managed by bun": "false",
        "managed by npm": "true",
        "managed package root": "/usr/local/lib/node_modules/@openai/codex",
        "npm update target": "/usr/local/lib/node_modules/@openai/codex"
      },
      "remediation": null,
      "durationMs": 300
    },
    "mcp.config": {
      "id": "mcp.config",
      "category": "mcp",
      "status": "ok",
      "summary": "no MCP servers configured",
      "details": {},
      "remediation": null,
      "durationMs": 0
    },
    "network.env": {
      "id": "network.env",
      "category": "network",
      "status": "ok",
      "summary": "network-related environment looks readable",
      "details": {
        "proxy env vars": "none"
      },
      "remediation": null,
      "durationMs": 0
    },
    "network.provider_reachability": {
      "id": "network.provider_reachability",
      "category": "reachability",
      "status": "ok",
      "summary": "active provider endpoints are reachable over HTTP",
      "details": {
        "ChatGPT base URL": "https://chatgpt.com/backend-api/ reachable (HTTP 403)",
        "reachability mode": "ChatGPT auth"
      },
      "remediation": null,
      "durationMs": 77
    },
    "network.websocket_reachability": {
      "id": "network.websocket_reachability",
      "category": "websocket",
      "status": "ok",
      "summary": "Responses WebSocket handshake succeeded",
      "details": {
        "DNS": "2 IPv4, 2 IPv6, first IPv4",
        "auth mode": "chatgpt",
        "connect timeout": "15000 ms",
        "endpoint": "wss://chatgpt.com/backend-api/<redacted>",
        "handshake result": "HTTP 101 Switching Protocols",
        "model provider": "openai",
        "models etag present": "true",
        "provider name": "OpenAI",
        "proxy env vars": "none",
        "reasoning header": "false",
        "server model present": "false",
        "supports websockets": "true",
        "wire API": "responses"
      },
      "remediation": null,
      "durationMs": 732
    },
    "runtime.provenance": {
      "id": "runtime.provenance",
      "category": "runtime",
      "status": "ok",
      "summary": "running npm on linux-x86_64",
      "details": {
        "commit": "unknown",
        "current executable": "/usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin/codex",
        "install method": "npm (package /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl, bin /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin, resources /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-resources, path /usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-path)",
        "platform": "linux-x86_64",
        "version": "0.133.0"
      },
      "remediation": null,
      "durationMs": 0
    },
    "runtime.search": {
      "id": "runtime.search",
      "category": "search",
      "status": "ok",
      "summary": "search is OK (bundled)",
      "details": {
        "search command": "/usr/local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-path/rg",
        "search command readiness": "file exists",
        "search provider": "bundled"
      },
      "remediation": null,
      "durationMs": 0
    },
    "sandbox.helpers": {
      "id": "sandbox.helpers",
      "category": "sandbox",
      "status": "ok",
      "summary": "sandbox configuration is readable",
      "details": {
        "approval policy": "OnRequest",
        "codex-linux-sandbox helper": "/home/bewbank/.codex/tmp/arg0/codex-arg0wAuY1K/codex-linux-sandbox",
        "execve wrapper helper": "/home/bewbank/.codex/tmp/arg0/codex-arg0wAuY1K/codex-execve-wrapper",
        "filesystem sandbox": "restricted",
        "network sandbox": "restricted"
      },
      "remediation": null,
      "durationMs": 0
    },
    "state.paths": {
      "id": "state.paths",
      "category": "state",
      "status": "ok",
      "summary": "state paths and databases are inspectable",
      "details": {
        "CODEX_HOME": "/home/bewbank/.codex (dir)",
        "active rollout files": "114 files, 87468301 total bytes, 767265 average bytes",
        "archived rollout files": "0 files, 0 total bytes, 0 average bytes",
        "goals DB": "/home/bewbank/.codex/goals_1.sqlite (file)",
        "goals DB integrity": "ok",
        "log DB": "/home/bewbank/.codex/logs_2.sqlite (file)",
        "log DB integrity": "ok",
        "log dir": "/home/bewbank/.codex/log (dir)",
        "sqlite home": "/home/bewbank/.codex (dir)",
        "state DB": "/home/bewbank/.codex/state_5.sqlite (file)",
        "state DB integrity": "ok"
      },
      "remediation": null,
      "durationMs": 337
    },
    "terminal.env": {
      "id": "terminal.env",
      "category": "terminal",
      "status": "warning",
      "summary": "locale is not UTF-8 - unicode glyphs may render incorrectly",
      "details": {
        "COLORTERM": "truecolor",
        "COLUMNS": "166",
        "DISPLAY": "present",
        "LINES": "41",
        "TERMINFO": "/home/bewbank/.local/kitty.app/lib/kitty/terminfo (dir)",
        "WAYLAND_DISPLAY": "present",
        "color output": "disabled (stdout is not a terminal)",
        "effective locale": "C",
        "stderr is terminal": "true",
        "stdin is terminal": "true",
        "stdout is terminal": "false",
        "terminal": "kitty",
        "terminal size": "166x41"
      },
      "issues": [
        {
          "severity": "warning",
          "cause": "locale is not UTF-8 - unicode glyphs may render incorrectly",
          "measured": "C",
          "expected": "UTF-8 locale, for example en_US.UTF-8",
          "remedy": "export LANG=en_US.UTF-8 or another UTF-8 locale",
          "fields": [
            "effective locale"
          ]
        }
      ],
      "remediation": null,
      "durationMs": 0
    },
    "updates.status": {
      "id": "updates.status",
      "category": "updates",
      "status": "ok",
      "summary": "update configuration is locally consistent",
      "details": {
        "cached latest version": "0.133.0",
        "check for update on startup": "true",
        "dismissed version": "0.130.0",
        "last checked at": "2026-05-24T20:22:12.109422197Z",
        "latest version": "0.133.0",
        "latest version status": "current version is not older",
        "npm update target": "/usr/local/lib/node_modules/@openai/codex",
        "update action": "npm install -g @openai/codex",
        "version cache": "/home/bewbank/.codex/version.json"
      },
      "remediation": null,
      "durationMs": 438
    }
  }
}

What issue are you seeing?

See uploaded thread

What steps can reproduce the bug?

Uploaded thread: 019e5653-b920-7a70-adae-9fd1a58b4ec9

What is the expected behavior?

See uploaded thread

Additional information

See uploaded thread

View original on GitHub ↗

2 Comments

JunJieLiu51520 · 3 months ago

Root cause analysis

I traced the bug through the exec policy evaluation flow.

How the permission key is derived

When a piped command like bash -lc "cmd1 | cmd2" is evaluated:

  1. commands_for_exec_policy() calls parse_shell_lc_plain_commands(), which parses the script into individual commands in source order: [["cmd1"], ["cmd2"]]
  2. ExecPolicy::check_multiple_with_options() evaluates each command individually and collects RuleMatch results in order
  3. try_derive_execpolicy_amendment_for_prompt_rules() iterates through matched_rules using find_map looking for the first HeuristicsRuleMatch with Decision::Prompt

Where the bug is

File: codex-rs/core/src/exec_policy.rs, function try_derive_execpolicy_amendment_for_prompt_rules (~line 832-840):

matched_rules
    .iter()
    .find_map(|rule_match| match rule_match {
        RuleMatch::HeuristicsRuleMatch {
            command,
            decision: Decision::Prompt,
        } => Some(ExecPolicyAmendment::from(command.clone())),
        _ => None,
    })

When the first command (cmd1) is already allowed by policy and the second command (cmd2) triggers a Prompt, find_map picks cmd2's tokens as the amendment prefix — because it's the first (and only) HeuristicsRuleMatch with Decision::Prompt.

Existing test confirms the behavior

The test heuristics_apply_when_other_commands_match_policy (~line 1570-1603 in exec_policy_tests.rs) demonstrates this: for command "apple | orange" where apple is policy-allowed, the proposed amendment is ["orange"] — the last command.

Proposed fix

Modify try_derive_execpolicy_amendment_for_prompt_rules (and the analogous try_derive_execpolicy_amendment_for_allow_rules) to accept the parsed commands list, and use commands[0] (the first command's tokens) as the amendment prefix instead of the prompting command's tokens. The first command in a pipe represents the user's primary intent — approving apple | orange should create a "don't ask again" rule for apple, not blanket-allow orange in unrelated contexts.

The test assertion would also need updating to expect ["apple"] instead of ["orange"].

Happy to submit a PR if the team would like me to implement this fix.

peterbecich · 2 months ago

I also observe this issue.

Brief example:

$ foo | bar | baz
1. Yes, proceed
2. Yes, and don't ask again for commands that start with baz
3. ...

It should be

...
2. Yes, and don't ask again for commands that start with foo
...