Skip to content
Open
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
43 changes: 43 additions & 0 deletions .codex/REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Codex project environment review

## Applied

- `.codex/config.toml` uses project-scoped, approval-gated workspace access with
agent shell network access disabled.
- `.codex/environments/environment.toml` replaces the former no-op setup with a
repository-local Python virtual environment and exposes the checks documented
by the repository and CI.

## Evidence

- `requirements.txt` is the repository's pinned pip dependency input.
- `AGENTS.md`, `README.md`, and `.github/workflows/ci.yml` define the unit,
hygiene, Compose-render, and Compose-security checks.
- The repository is a single Python service; no nested repositories, package
workspaces, database migrations, or private package registries were detected.

## Boundaries and assumptions

- Initial setup needs Python 3 and outbound access to the public Python package
index. Agent shell network access remains disabled by default and should be
enabled deliberately only for dependency installation or other reviewed work.
- Docker is required only for the Compose actions and image build; setup does
not start containers, services, databases, or deployments.
- `.env` and `data/` are intentionally not copied or generated. They can contain
the wrapper secret and a live container-scoped Codex login.
- The setup and actions use repository-relative paths and assume a POSIX shell.
Windows users should configure an app-generated platform-specific override.
- No hooks, rules, MCP servers, additional writable roots, or user-level Codex
settings are introduced.

## Validation

The project setup validator passed with no warnings. The setup command completed
successfully on macOS using Python 3.14, and the unit, hygiene, Compose-render,
and Compose-security actions passed. CI remains the authoritative Python 3.12
and Linux verification surface.

## Next safe action

Review this configuration in the Codex app and confirm the generated environment
remains selected for new worktrees.
6 changes: 6 additions & 0 deletions .codex/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@
#
# Keep credentials and runtime state out of this file. Codex/ChatGPT auth for
# the provider container belongs in data/codex-home, mounted at /root/.codex.

approval_policy = "on-request"
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = false
26 changes: 26 additions & 0 deletions .codex/environments/environment.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# THIS IS AUTOGENERATED. REVIEW CHANGES IN THE CODEX APP.
version = 1
name = "codex-cli-provider"

[setup]
script = "python3 -m venv .venv && .venv/bin/python -m pip install --requirement requirements.txt"

[[actions]]
name = "Unit tests"
icon = "tool"
command = "PYTHONPATH=. .venv/bin/python -m pytest -q"

[[actions]]
name = "Repository hygiene"
icon = "tool"
command = ".venv/bin/python scripts/check_repo_hygiene.py"

[[actions]]
name = "Compose security"
icon = "tool"
command = ".venv/bin/python scripts/check_compose_security.py"

[[actions]]
name = "Compose render"
icon = "tool"
command = "docker compose config"
20 changes: 0 additions & 20 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,12 @@ on:

permissions:
contents: read
security-events: write

concurrency:
group: security-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
codeql:
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
security-events: write

steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5

- name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
languages: python

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2

dependency-audit:
runs-on: ubuntu-latest
timeout-minutes: 15
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ history.jsonl
# Track reviewed project-level Codex config without exposing Codex home state.
.codex/*
!.codex/config.toml
!.codex/REVIEW.md
!.codex/environments/
.codex/environments/*
!.codex/environments/environment.toml
!.codex/rules/
.codex/rules/*
!.codex/rules/*.rules
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ docker compose up --build -d

Use `docker-compose.image.yml` only when pulling a published image.

## Codex Worktrees

- `.codex/environments/environment.toml` creates a repository-local `.venv`;
its setup needs public package-index access but must not start Docker or copy
ignored runtime state.
- Keep `.codex/config.toml` approval-gated and repository-scoped. Do not add
models, providers, credentials, user profiles, external writable roots, or
default agent-shell network access.
- Never copy `.env`, `data/`, Codex auth files, or registry credentials between
worktrees.

## Verification

Before handing off changes, run:
Expand All @@ -68,3 +79,6 @@ The Dockerfile currently copies only `requirements.txt` and `src/`. Keep it that
way unless there is a specific reason to widen the build context. `.dockerignore`
excludes `.env`, `data/`, Codex auth files, virtualenvs, logs, and other local
state that must not be published in images.

Publishing an image, pushing a release tag, changing registry configuration,
or widening the service beyond loopback requires separate release approval.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.12.11-slim-bookworm
FROM python:3.12.13-slim-trixie

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
Expand Down
14 changes: 9 additions & 5 deletions scripts/check_compose_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


FORBIDDEN_ENV = {"OPENAI_API_KEY"}
FORBIDDEN_MOUNT_FRAGMENTS = ("/var/run/docker.sock", ".cli-proxy-api")


def fail(message: str) -> None:
Expand Down Expand Up @@ -36,6 +37,13 @@ def as_list(value: Any) -> list[Any]:
return [value]


def check_forbidden_mount(source: str, target: str) -> None:
if any(fragment in source or fragment in target for fragment in FORBIDDEN_MOUNT_FRAGMENTS):
fail(f"forbidden mount detected: {source}:{target}")
if target in {"/home", "/root"} or source in {"/home", "/root"}:
fail(f"forbidden home/root mount detected: {source}:{target}")


def main() -> None:
os.chdir(Path(__file__).resolve().parents[1])
config = load_config()
Expand Down Expand Up @@ -92,7 +100,6 @@ def main() -> None:
codex_home_mount_ok = False
codex_work_mount_ok = False
secret_mount_ok = False
forbidden_fragments = ("/var/run/docker.sock", ".cli-proxy-api", "/runner")
for volume in volumes:
source = str(volume.get("source", ""))
target = str(volume.get("target", ""))
Expand All @@ -102,10 +109,7 @@ def main() -> None:
codex_work_mount_ok = True
elif target == "/run/secrets/proxy_api_key" and source.endswith("/data/secrets/proxy_api_key") and volume.get("read_only"):
secret_mount_ok = True
if any(fragment in source or fragment in target for fragment in forbidden_fragments):
fail(f"forbidden mount detected: {source}:{target}")
if target in {"/home", "/root"} or source in {"/home", "/root"}:
fail(f"forbidden home/root mount detected: {source}:{target}")
check_forbidden_mount(source, target)
if not codex_home_mount_ok:
fail("expected project data/codex-home mount at /root/.codex")
if not codex_work_mount_ok:
Expand Down
2 changes: 2 additions & 0 deletions scripts/check_repo_hygiene.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

ALLOWED_CODEX_PATHS = {
".codex/config.toml",
".codex/environments/environment.toml",
".codex/REVIEW.md",
}

FORBIDDEN_STAGEABLE_PATHS = (
Expand Down
24 changes: 24 additions & 0 deletions tests/test_compose_security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest

from scripts.check_compose_security import check_forbidden_mount


def test_github_runner_checkout_is_not_treated_as_a_forbidden_mount():
check_forbidden_mount(
"/home/runner/work/codex-cli-provider/codex-cli-provider/data/codex-home",
"/root/.codex",
)


@pytest.mark.parametrize(
("source", "target"),
[
("/var/run/docker.sock", "/var/run/docker.sock"),
("/tmp/.cli-proxy-api", "/workspace"),
("/root", "/workspace"),
("/tmp/project", "/home"),
],
)
def test_dangerous_mounts_remain_forbidden(source, target):
with pytest.raises(SystemExit):
check_forbidden_mount(source, target)
12 changes: 12 additions & 0 deletions tests/test_repo_hygiene.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ def test_codex_project_config_is_stageable():
check_paths([".codex/config.toml"])


@pytest.mark.parametrize(
"path",
[
".codex/environments/environment.toml",
".codex/REVIEW.md",
],
)
def test_reviewed_codex_project_files_are_stageable(path):
assert is_allowed_codex_path(path)
check_paths([path])


def test_codex_rules_files_are_stageable():
assert is_allowed_codex_path(".codex/rules/default.rules")
check_paths([".codex/rules/default.rules"])
Expand Down