Skip to content

Commit 20702f9

Browse files
committed
Sync main back to beta: bring Copilot fixes from #426 onto beta
Closes the final 55-line content drift between beta and main. PR #426 was squash-merged into main, so the two Copilot review fixes I pushed during its review cycle landed on main as part of the squash commit but never made it back to beta: - Path-traversal guard in tests/integration/_self_update_fixture.py (mirrors update_reload_runner.gd's runtime check) - Negative-path tests for v2.4.0+ addon-shape preflight in tests/unit/test_self_update_smoke_harness.py (+4 tests) After this merges, beta and main are content-identical and the merge graph reflects an ongoing-convergence relationship. https://claude.ai/code/session_01VgXf3Lqv2ypt36g6EqpRYg # Conflicts: # tests/integration/_self_update_fixture.py
2 parents 2092374 + bb30343 commit 20702f9

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

tests/integration/_self_update_fixture.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,11 @@ def extract_addon_from_zip(zip_path: Path, target_addon: Path) -> None:
279279
if not info.filename.startswith("addons/godot_ai/") or info.is_dir():
280280
continue
281281
rel = Path(info.filename).relative_to("addons/godot_ai")
282+
if rel.is_absolute() or any(part in ("", ".", "..") for part in rel.parts):
283+
raise ValueError(
284+
f"Refusing unsafe zip entry {info.filename!r}: relative path "
285+
f"{rel!s} contains absolute or traversal segments"
286+
)
282287
out = target_addon / rel
283288
out.parent.mkdir(parents=True, exist_ok=True)
284289
out.write_bytes(zf.read(info.filename))

tests/unit/test_self_update_smoke_harness.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from pathlib import Path
1010
from types import ModuleType
1111

12+
import pytest
13+
1214
ROOT = Path(__file__).resolve().parents[2]
1315
SCRIPT = ROOT / "script" / "local-self-update-smoke"
1416

@@ -208,6 +210,54 @@ def test_self_update_smoke_harness_refuses_unmarked_existing_dir(tmp_path: Path)
208210
assert "not marked as a smoke fixture" in result.stderr
209211

210212

213+
def _make_addon_with(tmp_path: Path, *, present: tuple[str, ...]) -> Path:
214+
addon = tmp_path / "addon"
215+
(addon / "utils").mkdir(parents=True)
216+
for rel in present:
217+
(addon / rel).write_text("# fixture stub\n", encoding="utf-8")
218+
return addon
219+
220+
221+
def test_v240_preflight_passes_when_both_files_present(tmp_path: Path) -> None:
222+
smoke = load_smoke_script()
223+
addon = _make_addon_with(
224+
tmp_path,
225+
present=("utils/server_lifecycle.gd", "utils/update_manager.gd"),
226+
)
227+
smoke._require_v240_plus_addon_shape(addon, "2.4.0")
228+
229+
230+
@pytest.mark.parametrize(
231+
("present", "expected_missing"),
232+
[
233+
(("utils/update_manager.gd",), "utils/server_lifecycle.gd"),
234+
(("utils/server_lifecycle.gd",), "utils/update_manager.gd"),
235+
],
236+
)
237+
def test_v240_preflight_raises_clear_harness_error_for_single_missing_file(
238+
tmp_path: Path, present: tuple[str, ...], expected_missing: str
239+
) -> None:
240+
smoke = load_smoke_script()
241+
addon = _make_addon_with(tmp_path, present=present)
242+
with pytest.raises(smoke.HarnessError) as exc_info:
243+
smoke._require_v240_plus_addon_shape(addon, "2.3.2")
244+
message = str(exc_info.value)
245+
assert expected_missing in message, message
246+
assert "2.3.2" in message, message
247+
assert "v2.4.0" in message, message
248+
assert "--base-from-release-tag" in message, message
249+
250+
251+
def test_v240_preflight_lists_all_missing_files_when_both_absent(tmp_path: Path) -> None:
252+
smoke = load_smoke_script()
253+
addon = _make_addon_with(tmp_path, present=())
254+
with pytest.raises(smoke.HarnessError) as exc_info:
255+
smoke._require_v240_plus_addon_shape(addon, "2.3.2")
256+
message = str(exc_info.value)
257+
assert "utils/server_lifecycle.gd" in message, message
258+
assert "utils/update_manager.gd" in message, message
259+
260+
211261
def test_self_update_smoke_harness_refuses_suspicious_marker(tmp_path: Path) -> None:
212262
project = tmp_path / "existing-project"
213263
(project / ".godot-ai-self-update-smoke").mkdir(parents=True)

0 commit comments

Comments
 (0)