Skip to content

Container entrypoint rejects bulk-scan with a CSV whenever an option precedes it #45

Description

@mariohercules

Summary

The container entrypoint decides whether a repository CSV was supplied by looking only at $2. Because the CLI accepts options before positionals, any invocation that puts a flag first is rejected with a message stating the CSV is missing — even when it is present.

if [ "${1:-}" = bulk-scan ]; then
case "${2:-}" in
--help|-h)
;;
""|-*)
printf '%s\n' 'codex-security: bulk-scan requires a repository CSV; interactive discovery is not supported in this image.' >&2
exit 2
;;
esac
fi

if [ "${1:-}" = bulk-scan ]; then
    case "${2:-}" in
        --help|-h) ;;
        ""|-*)
            printf '%s\n' 'codex-security: bulk-scan requires a repository CSV; interactive discovery is not supported in this image.' >&2
            exit 2 ;;
    esac
fi

--output-dir matches -*, so the guard fires regardless of what appears later in the argument list.

Affected version and environment

  • Confirmed on current main at f22d4a36f26d16287bcdfd707b369116e02a08c3 (docker/entrypoint.sh is unchanged from 0.1.1)
  • macOS 26.5.2 (build 25F84), /bin/sh
  • Node.js v24.5.0

Steps to reproduce

First, that the CLI itself accepts either order — both invocations parse the positional CSV and fail on the same later validation:

$ codex-security bulk-scan --workers 8 /tmp/definitely-missing.csv
codex-security: --output-dir is required with a repository CSV.

$ codex-security bulk-scan /tmp/definitely-missing.csv --workers 8
codex-security: --output-dir is required with a repository CSV.

Then the entrypoint, run with a stub codex-security on PATH so the final exec is observable:

1) CSV first:
exit=0  out=STUB codex-security received: bulk-scan /input/repositories.csv --output-dir /output

2) flag first:
exit=2  err=codex-security: bulk-scan requires a repository CSV; interactive discovery is not supported in this image.

3) flag only:
exit=2  err=codex-security: bulk-scan requires a repository CSV; interactive discovery is not supported in this image.

4) no arguments:
exit=2  err=codex-security: bulk-scan requires a repository CSV; interactive discovery is not supported in this image.

5) --help:
exit=0  out=STUB codex-security received: bulk-scan --help

Cases 3 and 4 are the intended behavior. Case 2 is the defect: a CSV was supplied and the container refuses it.

Expected behavior

The guard should reject only genuinely CSV-less invocations, which are the ones that would fall back to interactive discovery. An invocation that includes a positional CSV anywhere should be passed through.

Actual behavior

$ docker run --rm \
    -v ./repositories.csv:/input/repositories.csv:ro \
    -v ./results:/output \
    codex-security bulk-scan --output-dir /output --workers 8 /input/repositories.csv
codex-security: bulk-scan requires a repository CSV; interactive discovery is not supported in this image.

Exit code 2, and the stated reason is factually wrong.

Impact

Low severity but high confusion: the diagnostic contradicts what the user typed, and the ordering constraint is not documented in README.md or anywhere else. compose.yaml happens to place the CSV first, and container-ci.yml only exercises the bare bulk-scan form, so nothing in the repository covers flag-before-positional.

Suggested direction

Scan all arguments for a non-option token rather than inspecting $2, for example:

if [ "${1:-}" = bulk-scan ]; then
    shift
    csv=
    for argument in "$@"; do
        case "$argument" in
            --help|-h) csv=help; break ;;
            -*) ;;
            *) csv=${csv:-yes} ;;
        esac
    done
    [ -n "$csv" ] || { printf '%s\n' 'codex-security: ...' >&2; exit 2; }
fi

Note this needs care with options that take a separate value (--workers 8 would otherwise let 8 count as the CSV), so matching on a .csv suffix or on the known option list would be more robust than the sketch above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions