Skip to content

Commit 8a0ba2a

Browse files
committed
fix(codex): tighten sharing script safeguards
1 parent e4e78e7 commit 8a0ba2a

5 files changed

Lines changed: 52 additions & 26 deletions

File tree

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,18 @@ def main():
9191
if dest_path.exists():
9292
print(f"Error: already published: {dest_path}\nUnpublish it first or delete it manually.", file=sys.stderr)
9393
sys.exit(1)
94-
dest_path.write_text(entity_to_markdown(entity), encoding="utf-8")
95-
src_path.unlink()
96-
97-
audit_append(
98-
project_root=str(evolve_dir.resolve()) if evolve_dir.name != ".evolve" else str(evolve_dir.resolve().parent),
99-
action="publish",
100-
actor=effective_user or "unknown",
101-
entity=args.entity,
102-
)
94+
src_path.write_text(entity_to_markdown(entity), encoding="utf-8")
95+
src_path.replace(dest_path)
96+
97+
try:
98+
audit_append(
99+
project_root=str(evolve_dir.resolve()) if evolve_dir.name != ".evolve" else str(evolve_dir.resolve().parent),
100+
action="publish",
101+
actor=effective_user or "unknown",
102+
entity=args.entity,
103+
)
104+
except Exception as exc:
105+
print(f"Warning: failed to append audit entry for publish: {exc}", file=sys.stderr)
103106

104107
print(f"Published: {args.entity} -> {dest_path}")
105108

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,10 @@ def load_entities_with_source(entities_dirs):
7272
if not entity.get("content"):
7373
continue
7474

75+
entity.pop("_source", None)
7576
parts = md.relative_to(entities_dir).parts
76-
for index, part in enumerate(parts):
77-
if part == "subscribed" and index + 1 < len(parts):
78-
entity["_source"] = parts[index + 1]
79-
break
77+
if parts and parts[0] == "subscribed" and len(parts) > 1:
78+
entity["_source"] = parts[1]
8079

8180
entities.append(entity)
8281

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

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import argparse
55
import os
6+
import re
67
import subprocess
78
import sys
89
from pathlib import Path
@@ -27,22 +28,35 @@
2728
from config import _parse_yaml, load_config # noqa: E402
2829

2930

31+
_GIT_TIMEOUT = 30 # seconds
32+
33+
3034
def git_pull(repo_path, branch):
31-
"""Pull latest from origin."""
32-
return subprocess.run(
33-
["git", "-C", str(repo_path), "pull", "origin", branch, "--ff-only"],
34-
capture_output=True,
35-
text=True,
36-
)
35+
"""Pull latest from origin. Returns CompletedProcess, or None on timeout."""
36+
try:
37+
return subprocess.run(
38+
["git", "-C", str(repo_path), "pull", "origin", branch, "--ff-only"],
39+
capture_output=True,
40+
text=True,
41+
timeout=_GIT_TIMEOUT,
42+
)
43+
except subprocess.TimeoutExpired:
44+
print(f"Warning: git pull timed out for {repo_path} (branch: {branch})", file=sys.stderr)
45+
return None
3746

3847

3948
def count_delta(repo_path):
40-
"""Count added, modified, and deleted markdown files since last pull."""
49+
"""Count added/modified/deleted .md files since last pull."""
4150
result = subprocess.run(
4251
["git", "-C", str(repo_path), "diff", "--name-status", "HEAD@{1}", "HEAD"],
4352
capture_output=True,
4453
text=True,
54+
timeout=_GIT_TIMEOUT,
4555
)
56+
if result.returncode != 0:
57+
# HEAD@{1} doesn't exist (initial sync) — count all .md files as added.
58+
added = len(list(repo_path.glob("**/*.md")))
59+
return {"added": added, "updated": 0, "removed": 0}
4660
added = updated = removed = 0
4761
for line in result.stdout.splitlines():
4862
if not line.strip():
@@ -103,19 +117,25 @@ def main():
103117
summaries = []
104118
total_delta = {}
105119
any_changes = False
120+
safe_name = re.compile(r"^[A-Za-z0-9._-]+$")
106121

107122
for sub in subscriptions:
108123
if not isinstance(sub, dict):
109124
continue
110125
name = sub.get("name", "unknown")
111126
branch = sub.get("branch", "main")
127+
128+
if not safe_name.match(name):
129+
summaries.append(f"{name!r} (skipped - invalid subscription name)")
130+
continue
131+
112132
subscribed_base = (evolve_dir / "entities" / "subscribed").resolve()
113133
repo_path = (evolve_dir / "entities" / "subscribed" / name).resolve()
114134
legacy_base = (evolve_dir / "subscribed").resolve()
115135
legacy_repo_path = (evolve_dir / "subscribed" / name).resolve()
116136

117137
if repo_path == subscribed_base or not repo_path.is_relative_to(subscribed_base):
118-
summaries.append(f"{name} (invalid subscription name)")
138+
summaries.append(f"{name!r} (skipped - invalid subscription name)")
119139
continue
120140

121141
if legacy_repo_path != legacy_base and legacy_repo_path.is_relative_to(legacy_base):
@@ -131,13 +151,17 @@ def main():
131151
continue
132152

133153
pull_result = git_pull(repo_path, branch)
134-
if pull_result.returncode != 0:
154+
if pull_result is None or pull_result.returncode != 0:
135155
error_lines = (pull_result.stderr or pull_result.stdout or "").strip().splitlines()
136156
short_error = error_lines[-1] if error_lines else "unknown error"
137157
summaries.append(f"{name} (git pull failed: {short_error})")
158+
total_delta[name] = {"added": 0, "updated": 0, "removed": 0}
138159
continue
139160

140-
delta = count_delta(repo_path)
161+
if "Already up to date" in (pull_result.stdout or ""):
162+
delta = {"added": 0, "updated": 0, "removed": 0}
163+
else:
164+
delta = count_delta(repo_path)
141165
total_delta[name] = delta
142166
if any(value > 0 for value in delta.values()):
143167
any_changes = True

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ def main():
5050
name = args.name
5151
subscribed_base = (evolve_dir / "entities" / "subscribed").resolve()
5252
dest = (evolve_dir / "entities" / "subscribed" / name).resolve()
53-
if not dest.is_relative_to(subscribed_base):
53+
if name in {"", "."} or dest == subscribed_base or not dest.is_relative_to(subscribed_base):
5454
print(f"Error: invalid subscription name: {name!r}", file=sys.stderr)
5555
sys.exit(1)
5656

5757
legacy_base = (evolve_dir / "subscribed").resolve()
5858
legacy_dest = (evolve_dir / "subscribed" / name).resolve()
59-
if not legacy_dest.is_relative_to(legacy_base):
59+
if legacy_dest == legacy_base or not legacy_dest.is_relative_to(legacy_base):
6060
print(f"Error: invalid subscription name: {name!r}", file=sys.stderr)
6161
sys.exit(1)
6262

tests/platform_integrations/test_codex_sharing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ def test_sync_skips_invalid_subscription_name(self, temp_project_dir):
389389
)
390390

391391
assert result.returncode == 0
392-
assert ". (invalid subscription name)" in result.stdout
392+
assert "'.' (skipped - invalid subscription name)" in result.stdout
393393
assert not (evolve_dir / "entities" / "subscribed").exists()
394394

395395
def test_sync_uses_workspace_config_with_custom_evolve_dir(self, temp_project_dir, local_repo):

0 commit comments

Comments
 (0)