Skip to content

Commit 9fda064

Browse files
Merge pull request #8 from subdepthtech/codex/track-project-codex-config
[codex] Track project Codex config safely
2 parents 8b9b154 + 62386a2 commit 9fda064

4 files changed

Lines changed: 66 additions & 2 deletions

File tree

.codex/config.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Project-local Codex config for this repository.
2+
#
3+
# Keep credentials and runtime state out of this file. Codex/ChatGPT auth for
4+
# the provider container belongs in data/codex-home, mounted at /root/.codex.

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ temp/
2222

2323
auth.json
2424
codex-home/
25-
.codex/
2625
sessions/
2726
history.jsonl
27+
28+
# Track reviewed project-level Codex config without exposing Codex home state.
29+
.codex/*
30+
!.codex/config.toml
31+
!.codex/rules/
32+
.codex/rules/*
33+
!.codex/rules/*.rules

scripts/check_repo_hygiene.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
from pathlib import Path
66

77

8+
ALLOWED_CODEX_PATHS = {
9+
".codex/config.toml",
10+
}
11+
812
FORBIDDEN_STAGEABLE_PATHS = (
913
".env",
1014
"data/",
1115
".venv/",
1216
"venv/",
13-
".codex/",
1417
"codex-home/",
1518
)
1619

@@ -28,6 +31,18 @@
2831
}
2932

3033

34+
def is_allowed_codex_path(path: str) -> bool:
35+
if path in ALLOWED_CODEX_PATHS:
36+
return True
37+
38+
rules_prefix = ".codex/rules/"
39+
if path.startswith(rules_prefix):
40+
rule_name = path.removeprefix(rules_prefix)
41+
return "/" not in rule_name and rule_name.endswith(".rules")
42+
43+
return False
44+
45+
3146
def fail(message: str) -> None:
3247
print(f"FAIL: {message}", file=sys.stderr)
3348
raise SystemExit(1)
@@ -53,6 +68,8 @@ def check_paths(paths: list[str]) -> None:
5368
fail(f"forbidden path is stageable: {path}")
5469
if any(normalized.startswith(prefix) for prefix in FORBIDDEN_STAGEABLE_PATHS if prefix.endswith("/")):
5570
fail(f"forbidden path is stageable: {path}")
71+
if normalized == ".codex" or (normalized.startswith(".codex/") and not is_allowed_codex_path(normalized)):
72+
fail(f"forbidden Codex state/config path is stageable: {path}")
5673
if Path(normalized).name in FORBIDDEN_FILENAMES:
5774
fail(f"forbidden credential/state filename is stageable: {path}")
5875

tests/test_repo_hygiene.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import pytest
2+
3+
from scripts.check_repo_hygiene import check_paths, is_allowed_codex_path
4+
5+
6+
def test_codex_project_config_is_stageable():
7+
assert is_allowed_codex_path(".codex/config.toml")
8+
check_paths([".codex/config.toml"])
9+
10+
11+
def test_codex_rules_files_are_stageable():
12+
assert is_allowed_codex_path(".codex/rules/default.rules")
13+
check_paths([".codex/rules/default.rules"])
14+
15+
16+
@pytest.mark.parametrize(
17+
"path",
18+
[
19+
".codex/auth.json",
20+
".codex/history.jsonl",
21+
".codex/sessions/2026/session.jsonl",
22+
".codex/cache/state.json",
23+
".codex/state.sqlite",
24+
".codex/rules/nested/default.rules",
25+
".codex/local-environment.json",
26+
],
27+
)
28+
def test_codex_state_and_unknown_files_are_not_stageable(path):
29+
assert not is_allowed_codex_path(path)
30+
with pytest.raises(SystemExit):
31+
check_paths([path])
32+
33+
34+
@pytest.mark.parametrize("path", [".env", "data/codex-home/auth.json", "codex-home/auth.json"])
35+
def test_existing_forbidden_paths_stay_forbidden(path):
36+
with pytest.raises(SystemExit):
37+
check_paths([path])

0 commit comments

Comments
 (0)