Skip to content

Focus text in adversarial-review is re-tokenized; --FLAG VALUE substrings leak into CLI args #333

@liuzl

Description

@liuzl

Summary

When /codex:adversarial-review (or the underlying node scripts/codex-companion.mjs adversarial-review …) is invoked with a focus text argument, the focus text is re-tokenized by normalizeArgv + splitRawArgumentString. Any literal --FLAG VALUE substring inside the focus text is then consumed by the option parser and forwarded as if the caller had set that flag — including --model X, which gets sent to the OpenAI API as the model selection.

The result is a 400 from the OpenAI API:

{"type":"error","status":400,"error":{"type":"invalid_request_error","message":"The 'pm_v6_2' model is not supported when using Codex with a ChatGPT account."}}

Reproduced on plugin version 1.0.3 and verified by reading origin/main (1.0.4) — the bug is still present upstream.

Reproduction

node ~/.claude/plugins/cache/openai-codex/codex/1.0.3/scripts/codex-companion.mjs \
  adversarial-review "Review the build script. The build command is: build_v6_artifact.py --variant v6.2 --model pm_v6_2"

Expected: review runs, focus text is treated as opaque prose.
Actual: companion picks pm_v6_2 as the model, OpenAI rejects it as an unknown model name.

Any focus text that mentions a flag-with-value example (which is common for design reviews of CLI tools) hits this. Using a placeholder like <registry_name> also fails (The '<registry_name>' model is not supported).

Root cause

scripts/lib/args.mjs:

  • splitRawArgumentString(raw) does a shell-like tokenization of a single string into argv-style tokens.

scripts/codex-companion.mjs:

  • normalizeArgv(argv) (line 127-134) calls splitRawArgumentString when argv.length === 1.
  • main() (line 980-988) destructures: const [subcommand, ...argv] = process.argv.slice(2);. When the caller passes a single quoted focus argument, argv has exactly one element — normalizeArgv then re-tokenizes the user's whole focus prose.
  • handleReviewCommand (line 683-687) uses parseCommandInput with valueOptions: ["base", "scope", "model", "cwd"] and aliasMap { m: "model" }. Any --model XXX (or -m XXX) substring in the re-tokenized prose is consumed as the OpenAI model selection. The remaining tokens become positionals (focus text), so the visible failure mode is "review runs against the wrong model name" rather than "command rejected with a syntax error".

The same vulnerability applies to --base, --scope, --cwd, --effort, --prompt-file, etc., wherever the corresponding subcommand registers them as value options. --model happens to be the most visible one because the OpenAI API returns an explicit error.

Suggested fix directions

Several options, in increasing strictness:

  1. Stop re-tokenizing focus text. Treat positionals after the subcommand as opaque — don't run splitRawArgumentString on them. Callers who want CLI options in the same call must pass them as separate argv elements. This is what most Unix tools do (grep "--FLAG value" does not parse the quoted string).
  2. Tokenize only the option-prefix portion of argv. Stop tokenizing as soon as the parser sees a non-option positional. Concretely: split the raw string, then on the first non-- token, glue all remaining tokens back into a single positional. This preserves the node companion.mjs review --background "focus" ergonomics without leaking flags from inside "focus".
  3. Require an explicit -- separator before focus text when the caller wants opaque content. Document node companion.mjs adversarial-review --background -- "focus text". This is the lowest-risk change but pushes burden onto callers.
  4. Reject --model (and similar) embedded in focus text at the validateNativeReviewRequest / handleReviewCommand layer when both a positional --model token and a focus body are present. Hostile but explicit.

Option 1 or 2 seems right: focus text should never silently become CLI options.

Impact

  • Affects any review where the focus text contains a CLI example involving --model, --base, --scope, --effort etc. Very common when reviewing CLI tooling, model-deploy scripts, or anything that takes a --model flag.
  • Workaround: rewrite the focus prose to avoid literal --FLAG VALUE patterns. Discoverable only by reading the companion source.

Environment

  • Plugin: openai-codex v1.0.3 (also confirmed present on origin/main at v1.0.4)
  • Claude Code on macOS (darwin 25.4.0)
  • ChatGPT account auth (the surfaced error is account-tier specific, but the root cause is general)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions