Skip to content

Commit 55d61fb

Browse files
committed
fix(codex): harden sharing edge cases
1 parent 3c98b85 commit 55d61fb

6 files changed

Lines changed: 62 additions & 12 deletions

File tree

platform-integrations/codex/plugins/evolve-lite/skills/publish/scripts/publish.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import re
88
import sys
9-
from pathlib import Path
9+
from pathlib import Path, PurePath
1010

1111
# Walk up from the script location to find the installed plugin lib directory.
1212
_script = Path(__file__).resolve()
@@ -53,6 +53,10 @@ def main():
5353
args = parser.parse_args()
5454

5555
evolve_dir = Path(os.environ.get("EVOLVE_DIR", ".evolve"))
56+
if PurePath(args.entity).name != args.entity or args.entity in {".", ".."}:
57+
print(f"Error: invalid entity name: {args.entity!r}", file=sys.stderr)
58+
sys.exit(1)
59+
5660
src_base = (evolve_dir / "entities" / "guideline").resolve()
5761
src_path = (evolve_dir / "entities" / "guideline" / args.entity).resolve()
5862

platform-integrations/codex/plugins/evolve-lite/skills/recall/scripts/retrieve_entities.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def load_entities_with_source(entities_dirs):
6565
for md in sorted(entities_dir.glob("**/*.md")):
6666
try:
6767
entity = markdown_to_entity(md)
68-
except OSError:
68+
except (OSError, UnicodeError):
6969
continue
7070
if not entity.get("content"):
7171
continue

platform-integrations/codex/plugins/evolve-lite/skills/sync/scripts/sync.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ def main():
8888
args = parser.parse_args()
8989

9090
evolve_dir = Path(os.environ.get("EVOLVE_DIR", ".evolve"))
91-
project_root = str(evolve_dir.resolve()) if evolve_dir.name != ".evolve" else str(evolve_dir.resolve().parent)
91+
resolved_evolve_dir = evolve_dir.resolve()
92+
project_root = str(resolved_evolve_dir.parent)
93+
audit_root = resolved_evolve_dir if resolved_evolve_dir.name == ".evolve" else resolved_evolve_dir / ".evolve"
9294

9395
if args.config:
9496
cfg_path = Path(args.config)
@@ -149,7 +151,7 @@ def main():
149151

150152
summaries.append(f"{name} (+{delta['added']} added, {delta['updated']} updated, {delta['removed']} removed)")
151153

152-
audit_append(project_root=project_root, action="sync", actor=actor, delta=total_delta)
154+
audit_append(project_root=str(audit_root.parent), action="sync", actor=actor, delta=total_delta)
153155

154156
if args.quiet and not any_changes:
155157
sys.exit(0)

tests/platform_integrations/test_codex_sharing.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pytest
1010

11-
pytestmark = pytest.mark.platform_integrations
11+
pytestmark = [pytest.mark.platform_integrations, pytest.mark.e2e]
1212

1313
_PLUGIN_ROOT = Path(__file__).parent.parent.parent / "platform-integrations/codex/plugins/evolve-lite"
1414
SAVE_SCRIPT = _PLUGIN_ROOT / "skills/learn/scripts/save_entities.py"
@@ -196,14 +196,15 @@ def test_publish_fails_when_public_entity_already_exists(self, temp_project_dir)
196196
assert public_path.read_text() == existing_content
197197
assert source.exists()
198198

199-
def test_publish_rejects_path_traversal_in_entity_name(self, temp_project_dir):
199+
@pytest.mark.parametrize("entity_name", ["../../etc/passwd", "subdir/tip.md", ".", ".."])
200+
def test_publish_rejects_invalid_entity_name(self, temp_project_dir, entity_name):
200201
guideline_dir = temp_project_dir / ".evolve" / "entities" / "guideline"
201202
guideline_dir.mkdir(parents=True)
202203
(guideline_dir / "tip.md").write_text("---\ntype: guideline\n---\n\nA tip.\n")
203204
result = run_script(
204205
PUBLISH_SCRIPT,
205206
project_dir=temp_project_dir,
206-
args=["--entity", "../../etc/passwd"],
207+
args=["--entity", entity_name],
207208
evolve_dir=temp_project_dir / ".evolve",
208209
expect_success=False,
209210
)
@@ -390,6 +391,35 @@ def test_sync_skips_invalid_subscription_name(self, temp_project_dir):
390391
assert ". (invalid subscription name)" in result.stdout
391392
assert not (evolve_dir / "entities" / "subscribed").exists()
392393

394+
def test_sync_uses_workspace_config_with_custom_evolve_dir(self, temp_project_dir, local_repo):
395+
evolve_dir = temp_project_dir / "custom-evolve"
396+
subscribed_dir = evolve_dir / "subscribed"
397+
subscribed_dir.mkdir(parents=True)
398+
subprocess.run(
399+
["git", "clone", "--branch", "main", "--depth", "1", str(local_repo["bare"]), str(subscribed_dir / "alice")],
400+
check=True,
401+
env=local_repo["env"],
402+
)
403+
(temp_project_dir / "evolve.config.yaml").write_text(
404+
f'identity:\n user: "alice"\nsubscriptions:\n - name: "alice"\n remote: "{local_repo["bare"]}"\n branch: "main"\n'
405+
)
406+
407+
result = run_script(
408+
SYNC_SCRIPT,
409+
project_dir=temp_project_dir,
410+
evolve_dir=evolve_dir,
411+
expect_success=False,
412+
)
413+
414+
assert result.returncode == 0
415+
mirrored = evolve_dir / "entities" / "subscribed" / "alice" / "guideline" / "tip-one.md"
416+
assert mirrored.exists()
417+
audit_log = evolve_dir / ".evolve" / "audit.log"
418+
assert audit_log.exists()
419+
entry = json.loads(audit_log.read_text().strip())
420+
assert entry["action"] == "sync"
421+
assert entry["actor"] == "alice"
422+
393423
def test_manual_sync_runs_even_when_on_session_start_is_false(self, temp_project_dir, local_repo):
394424
evolve_dir = temp_project_dir / ".evolve"
395425
run_script(

tests/platform_integrations/test_entity_io.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ def test_find_entities_dir_env_missing_subdir_returns_none(monkeypatch, tmp_path
6363
assert entity_io.find_entities_dir() is None
6464

6565

66-
def test_find_recall_entity_dirs_includes_entities_and_public(monkeypatch, tmp_path):
67-
custom = tmp_path / "custom-evolve"
66+
@pytest.mark.unit
67+
def test_find_recall_entity_dirs_includes_entities_and_public(monkeypatch, temp_project_dir):
68+
custom = temp_project_dir / "custom-evolve"
6869
entities = custom / "entities"
6970
public = custom / "public"
7071
entities.mkdir(parents=True)
@@ -73,8 +74,9 @@ def test_find_recall_entity_dirs_includes_entities_and_public(monkeypatch, tmp_p
7374
assert entity_io.find_recall_entity_dirs() == [entities, public]
7475

7576

76-
def test_find_recall_entity_dirs_skips_missing_locations(monkeypatch, tmp_path):
77-
custom = tmp_path / "custom-evolve"
77+
@pytest.mark.unit
78+
def test_find_recall_entity_dirs_skips_missing_locations(monkeypatch, temp_project_dir):
79+
custom = temp_project_dir / "custom-evolve"
7880
public = custom / "public"
7981
public.mkdir(parents=True)
8082
monkeypatch.setenv("EVOLVE_DIR", str(custom))

tests/platform_integrations/test_sync.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pytest
1010

11-
pytestmark = pytest.mark.platform_integrations
11+
pytestmark = [pytest.mark.platform_integrations, pytest.mark.e2e]
1212

1313
_PLUGIN_ROOT = Path(__file__).parent.parent.parent / "platform-integrations/claude/plugins/evolve-lite"
1414
SUBSCRIBE_SCRIPT = _PLUGIN_ROOT / "skills/subscribe/scripts/subscribe.py"
@@ -118,6 +118,18 @@ def test_skips_symlinked_entities(self, subscribed_project):
118118
real_file.write_text("---\ntype: guideline\n---\n\nReal content.\n")
119119
symlink_file = lr["work"] / "guideline" / "link.md"
120120
symlink_file.symlink_to(real_file)
121+
git_env = lr["env"]
122+
subprocess.run(["git", "-C", str(lr["work"]), "add", "."], check=True, env=git_env)
123+
subprocess.run(
124+
["git", "-C", str(lr["work"]), "commit", "-m", "add symlinked entity"],
125+
check=True,
126+
env=git_env,
127+
)
128+
subprocess.run(
129+
["git", "-C", str(lr["work"]), "push", "origin", "main"],
130+
check=True,
131+
env=git_env,
132+
)
121133
run_script(SYNC_SCRIPT, p["project_dir"], evolve_dir=p["evolve_dir"])
122134
mirrored = p["evolve_dir"] / "entities" / "subscribed" / "alice" / "guideline"
123135
assert not (mirrored / "link.md").exists()

0 commit comments

Comments
 (0)