Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .codex_yolo.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ ARG CODEX_YOLO_WRAPPER_VERSION=unknown
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
gh \
git \
gosu \
openssh-client \
passwd \
ripgrep \
sudo \
&& rm -rf /var/lib/apt/lists/*

Expand Down
30 changes: 30 additions & 0 deletions .codex_yolo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ REPO="${CODEX_YOLO_REPO:-laurenceputra/codex_yolo}"
BRANCH="${CODEX_YOLO_BRANCH:-main}"
VERBOSE="${CODEX_VERBOSE:-0}"
MOUNT_SSH=0
MOUNT_GH=0
WRAPPER_VERSION="unknown"
if [[ -f "${SCRIPT_DIR}/VERSION" ]]; then
WRAPPER_VERSION="$(tr -d '\n ' < "${SCRIPT_DIR}/VERSION")"
Expand Down Expand Up @@ -178,6 +179,10 @@ for arg in "$@"; do
MOUNT_SSH=1
continue
;;
--gh)
MOUNT_GH=1
continue
;;
esac
pass_args+=("${arg}")
done
Expand Down Expand Up @@ -282,6 +287,31 @@ if [[ -f "${HOME}/.gitconfig" ]]; then
docker_args+=("-v" "${HOME}/.gitconfig:${CONTAINER_HOME}/.gitconfig:ro")
fi

# Mount .copilot directory if explicitly enabled for GitHub CLI workflows.
if [[ "${MOUNT_GH}" == "1" ]]; then
if ! command -v gh >/dev/null 2>&1; then
log_error "--gh requires GitHub CLI (gh) installed on the host."
log_info "Install gh, authenticate on host, and retry: gh auth login"
exit 1
fi

if ! gh auth status >/dev/null 2>&1; then
log_error "--gh requires host GitHub authentication."
log_info "Run on host first: gh auth login"
exit 1
fi

if [[ -d "${HOME}/.copilot" ]]; then
docker_args+=("-v" "${HOME}/.copilot:${CONTAINER_HOME}/.copilot")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mount host .copilot directory as read-only

When --gh is used, this bind mount is currently read-write, so processes inside the container can modify or delete files in the host ~/.copilot directory. In --yolo workflows this expands host-state risk and can corrupt local GitHub/Copilot state, unlike other sensitive mounts here (~/.gitconfig, ~/.ssh) that are intentionally mounted read-only. Adding :ro to this mount would preserve access while reducing credential/state mutation risk.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex, fix this

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

  • Updated the --gh mount to bind host ~/.copilot as read-only (:ro) and updated the runtime warning message accordingly, addressing the security feedback on the PR thread. .codex_yolo.shL304-L307
  • Strengthened integration coverage so the --gh dry-run assertion now explicitly checks for .copilot:ro (not just .copilot). tests/integration_tests.shL377-L383
  • Updated documentation/examples to state that the ~/.copilot mount is read-only when --gh is enabled. README.mdL153-L158 EXAMPLES.mdL99-L104

Testing

  • bash -n .codex_yolo.sh tests/integration_tests.sh README.md EXAMPLES.md
  • ./tests/integration_tests.sh
  • ⚠️ python - <<'PY' ... urllib.request.urlopen('https://duckduckgo.com/html/?q=docker bind mount read-only :ro') ... PY (web search attempt failed due network proxy/tunnel 403 Forbidden in this environment)

Committed as:

  • 0736767Mount --gh copilot volume read-only

I also created the follow-up PR with title:

  • Harden --gh mount by making ~/.copilot read-only

View task →

log_info "Warning: ${HOME}/.copilot is now mounted inside the container."
log_info "This enables gh workflows and GitHub Copilot-related host context access."
else
log_error "--gh enabled but ${HOME}/.copilot does not exist or is not a directory."
log_info "Ensure host Copilot data exists after logging in with gh, then retry."
exit 1
fi
fi

# Mount .ssh directory if explicitly enabled
if [[ "${MOUNT_SSH}" == "1" ]]; then
if [[ -d "${HOME}/.ssh" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion .codex_yolo_completion.bash
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ _codex_yolo_complete() {
prev="${COMP_WORDS[COMP_CWORD-1]}"

# codex_yolo specific commands and flags
local codex_yolo_opts="diagnostics doctor health version --version --verbose -v --pull --help"
local codex_yolo_opts="diagnostics doctor health version --version --verbose -v --pull --gh --help"

# Common codex CLI commands (pass-through)
local codex_opts="login --help --yolo --search --device-auth"
Expand Down
1 change: 1 addition & 0 deletions .codex_yolo_completion.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ _codex_yolo() {
'--verbose[Enable verbose output]'
'-v[Enable verbose output]'
'--pull[Pull base image before building]'
'--gh[Mount host ~/.copilot after verifying host gh auth]'
'--help[Show help information]'
)

Expand Down
11 changes: 11 additions & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ CODEX_BUILD_NO_CACHE=1 codex_yolo
CODEX_BUILD_NO_CACHE=1 codex_yolo --pull
```

### Enable GitHub CLI Copilot Mount
Use this when you need `gh` workflows that depend on host Copilot state:
```bash
codex_yolo --gh
```

Prerequisites:
- Host has `gh` installed
- Host is already authenticated (`gh auth login`)
- Host `~/.copilot` directory exists

### Dry Run
Preview Docker commands without executing:
```bash
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Version 1.1.0 brings major improvements to usability, troubleshooting, and devel
- Docker (Desktop or Engine)
- Bash (macOS/Linux; Windows via WSL recommended)
- Docker Buildx (recommended for reliable builds): https://docs.docker.com/build/buildx/
- Host `gh` login (`gh auth login`) only if you plan to use `--gh`

## Install

Expand Down Expand Up @@ -120,6 +121,7 @@ are shared between runs.

For security reasons, `codex_yolo` **does not** mount by default:
- `~/.ssh` - SSH keys are not available inside the container by default
- `~/.copilot` - GitHub Copilot/GitHub CLI related state is not available unless explicitly enabled
- SSH agent forwarding is disabled
- No other host directories are mounted by default

Expand All @@ -140,6 +142,21 @@ codex_yolo --mount-ssh
- **You should protect critical branches** in your repository settings (e.g., require pull requests, enable branch protection rules)
- Only enable this if you trust the Codex agent and understand the security implications

### Optional: Enable GitHub CLI Copilot Mount

If you need `gh` workflows that rely on host Copilot state, enable `--gh`:

```bash
codex_yolo --gh
```

Requirements for `--gh`:
- `gh` must be installed on the host.
- You must already be logged in on the host via `gh auth login`.
- `~/.copilot` must exist on the host.

When enabled, `~/.copilot` is mounted into the container at `~/.copilot`.

## Troubleshooting

Run diagnostics to check your setup:
Expand Down Expand Up @@ -185,6 +202,7 @@ Available options:
- `--pull` flag to force a pull when running `./.codex_yolo.sh`
- `--verbose` or `-v` flag to enable verbose output
- `--mount-ssh` flag to enable SSH key mounting for git push access; see security warning above
- `--gh` flag to mount host `~/.copilot` after validating host `gh` auth
- Each run checks npm for the latest `@openai/codex` version (unless skipped)
and rebuilds the image if it is out of date.
- Each run checks for codex_yolo script updates (unless skipped with `CODEX_SKIP_UPDATE_CHECK=1`)
Expand All @@ -206,7 +224,7 @@ Add these lines to your `.bashrc` or `.zshrc` for persistent completion.

## Security note

`codex_yolo` deliberately limits what gets mounted from the host. See the "What gets mounted from the host" section above for details. By default, your SSH agent is not forwarded and `~/.ssh` is not mounted, keeping the blast radius smaller when running in `--yolo` mode. This comes at the cost of private repo access from inside the container unless you explicitly enable SSH mounting with the `--mount-ssh` flag (see above for security considerations).
`codex_yolo` deliberately limits what gets mounted from the host. See the "What gets mounted from the host" section above for details. By default, your SSH agent is not forwarded and `~/.ssh`/`~/.copilot` are not mounted, keeping the blast radius smaller when running in `--yolo` mode. This comes at the cost of private repo access from inside the container unless you explicitly enable SSH mounting with `--mount-ssh` and GitHub Copilot state mounting with `--gh`.

The container enables passwordless `sudo` for the mapped user to allow system installs. Use with care; `sudo` writes into `/workspace` are cleaned up via a chown on exit, but they still run as root inside the container.

Expand Down
62 changes: 59 additions & 3 deletions tests/integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ else
log_info "Output: ${output}"
fi

# Test 15: SSH mounting with --mount-ssh flag
# Test 16: SSH mounting with --mount-ssh flag
log_test "SSH mounting with --mount-ssh flag"
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
# Create a temporary home directory for testing
Expand Down Expand Up @@ -308,7 +308,7 @@ else
log_skip "Docker not available, skipping --mount-ssh flag test"
fi

# Test 16: Wrapper version metadata is embedded in Dockerfile
# Test 17: Wrapper version metadata is embedded in Dockerfile
log_test "Dockerfile embeds wrapper version metadata"
dockerfile="${SCRIPT_DIR}/../.codex_yolo.Dockerfile"
if grep -q 'ARG CODEX_YOLO_WRAPPER_VERSION=' "${dockerfile}" && \
Expand All @@ -318,7 +318,7 @@ else
log_fail "Dockerfile missing wrapper version metadata support"
fi

# Test 17: Main script rebuild logic includes wrapper version mismatch checks
# Test 18: Main script rebuild logic includes wrapper version mismatch checks
log_test "Main script rebuilds when wrapper VERSION changes"
if grep -q 'CODEX_YOLO_WRAPPER_VERSION=' "${CODEX_YOLO_SH}" && \
grep -q '/opt/codex-yolo-version' "${CODEX_YOLO_SH}" && \
Expand All @@ -328,6 +328,62 @@ else
log_fail "Wrapper version mismatch rebuild logic missing"
fi

# Test 19: Dockerfile includes rg and gh packages
log_test "Dockerfile installs rg and gh"
if grep -q 'gh' "${dockerfile}" && grep -q 'ripgrep' "${dockerfile}"; then
log_pass "Dockerfile includes gh and ripgrep packages"
else
log_fail "Dockerfile missing gh and/or ripgrep package install"
fi

# Test 20: --gh mounting in dry run mode
log_test "GitHub mount with --gh flag"
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
test_home=$(mktemp -d)
fake_bin=$(mktemp -d)
original_home="${HOME}"
original_path="${PATH}"

cleanup_test_20() {
rm -rf "${test_home}" "${fake_bin}"
export HOME="${original_home}"
export PATH="${original_path}"
unset CODEX_DRY_RUN
unset CODEX_SKIP_UPDATE_CHECK
unset CODEX_SKIP_VERSION_CHECK
}
trap cleanup_test_20 EXIT

mkdir -p "${test_home}/.copilot" "${test_home}/.codex"
cat > "${fake_bin}/gh" <<'TESTEOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "auth" ]] && [[ "${2:-}" == "status" ]]; then
exit 0
fi
exit 0
TESTEOF
chmod +x "${fake_bin}/gh"

export HOME="${test_home}"
export PATH="${fake_bin}:${PATH}"
export CODEX_DRY_RUN=1
export CODEX_SKIP_UPDATE_CHECK=1
export CODEX_SKIP_VERSION_CHECK=1

output=$("${CODEX_YOLO_SH}" --gh 2>&1 || true)
cleanup_test_20
trap - EXIT

if echo "${output}" | grep -q "\.copilot" && echo "${output}" | grep -q "Dry run"; then
log_pass "--gh flag mounts ~/.copilot in dry run output"
else
log_fail "--gh flag did not mount ~/.copilot as expected"
log_info "Output snippet: $(echo "${output}" | grep -i copilot | head -5)"
fi
else
log_skip "Docker not available, skipping --gh flag test"
fi

# Summary
echo ""
echo "=== Test Summary ==="
Expand Down