You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@magpie.apache.org by po...@apache.org on 2026/10/25 00:24:38 UTC

(magpie) branch main updated: fix(container-gateway-hook): derive the checkout root without repository config (#1804)

This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/magpie.git


The following commit(s) were added to refs/heads/main by this push:
     new d600695fa fix(container-gateway-hook): derive the checkout root without repository config (#1804)
d600695fa is described below

commit d600695fafb15016d4fa5cf67f6d2970caf6a524
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Oct 25 02:24:31 2026 +0200

    fix(container-gateway-hook): derive the checkout root without repository config (#1804)
    
    A .git directory planted in a sandbox-writable checkout could set
    core.worktree to a parent directory: git then reports a toplevel that
    still contains the session cwd (so the existing containment check passes)
    but keys the worktree records differently, so the main checkout's record
    is never consulted and its grants are skipped. The hook now also finds the
    root the way the filesystem shows it, the nearest directory holding a .git,
    refuses when git's toplevel differs, and refuses whenever core.worktree is
    set. The new test fails against the previous hook.
    
    Generated-by: Claude (Opus 5.5)
---
 .../setup/setup_preflight/isolated_fingerprint.py  |  2 +-
 tools/agent-isolation/container-gateway-hook.sh    | 34 +++++++++++++++++++
 .../tests/test_container_gateway_hook.py           | 39 ++++++++++++++++++++++
 3 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/plugins/magpie-setup/skills/setup/setup_preflight/isolated_fingerprint.py b/plugins/magpie-setup/skills/setup/setup_preflight/isolated_fingerprint.py
index 865128204..f00c82004 100644
--- a/plugins/magpie-setup/skills/setup/setup_preflight/isolated_fingerprint.py
+++ b/plugins/magpie-setup/skills/setup/setup_preflight/isolated_fingerprint.py
@@ -21,4 +21,4 @@ The isolated-setup fingerprint of the framework this package shipped with,
 for installs that do not carry the framework source (see `isolated.py`).
 """
 
-FRAMEWORK_FINGERPRINT = "sha256:198dc3e80e1af151"
+FRAMEWORK_FINGERPRINT = "sha256:802fecf15e0df3ce"
diff --git a/tools/agent-isolation/container-gateway-hook.sh b/tools/agent-isolation/container-gateway-hook.sh
index 32a289553..829698da8 100755
--- a/tools/agent-isolation/container-gateway-hook.sh
+++ b/tools/agent-isolation/container-gateway-hook.sh
@@ -120,6 +120,10 @@ done
 
 log() { printf '%s: %s\n' "${0##*/}" "$*" >&2; }
 
+# Every git call: never discover a bare repository, which sandboxed code could
+# plant as HEAD / config / objects in a project that has no .git of its own.
+git() { command git -c safe.bareRepository=explicit "$@"; }
+
 payload="$(cat 2>/dev/null || true)"
 cwd="$(printf '%s' "$payload" | jq -r '.cwd // empty' 2>/dev/null || true)"
 [[ -n $cwd ]] || cwd="$PWD"
@@ -132,6 +136,36 @@ if [[ $cwd != "$root" && $cwd != "$root"/* ]]; then
     log "git reports $root as the toplevel of $cwd, which it does not contain; not starting the container gateway"
     exit 0
 fi
+# The root as the filesystem shows it, read without any repository config: the
+# nearest directory above the cwd holding a .git. git's toplevel must be that
+# directory, and core.worktree must be unset: a .git directory planted in a
+# sandbox-writable checkout could otherwise name a parent as the toplevel, which
+# still contains the cwd but keys the worktree records differently, so an
+# existing record would never be consulted.
+fs_root=""
+probe_dir="$cwd"
+while :; do
+    if [[ -e $probe_dir/.git || -L $probe_dir/.git ]]; then
+        fs_root="$probe_dir"
+        break
+    fi
+    [[ $probe_dir == / ]] && break
+    probe_dir="$(dirname -- "$probe_dir")"
+done
+git_toplevel="$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null || true)"
+if [[ -z $fs_root && -n $git_toplevel ]]; then
+    log "git finds a repository for $cwd but there is no .git on the way up; not starting the container gateway"
+    exit 0
+fi
+if [[ -n $fs_root && $fs_root != "$root" ]]; then
+    log "git reports $root as the toplevel of $cwd, but the checkout is $fs_root; not starting the container gateway"
+    exit 0
+fi
+# Checked whether or not a .git was found: core.worktree is what moves a toplevel.
+if [[ -n "$(git -C "$cwd" config --get core.worktree 2>/dev/null)" ]]; then
+    log "core.worktree is set for $cwd; not starting the container gateway"
+    exit 0
+fi
 
 # The home directory from the account database (`~user` reads it, not $HOME),
 # so a $HOME redirected by the environment cannot hide the real ~/.cache,
diff --git a/tools/agent-isolation/tests/test_container_gateway_hook.py b/tools/agent-isolation/tests/test_container_gateway_hook.py
index a219048e8..99fd232c0 100644
--- a/tools/agent-isolation/tests/test_container_gateway_hook.py
+++ b/tools/agent-isolation/tests/test_container_gateway_hook.py
@@ -782,3 +782,42 @@ def test_a_writable_record_directory_refuses_even_a_plain_checkout(tmp_path: Pat
     lay.settings({"sandbox": {"filesystem": {"allowWrite": [str(lay.home / ".claude" / "magpie-gateway")]}}})
     done = run("start", lay.project, home=lay.home)
     assert done.stdout == "" and "is sandbox-writable" in done.stderr
+
+
+
+def test_a_planted_core_worktree_cannot_move_the_record_key(tmp_path: Path) -> None:
+    """A fake .git naming a parent as the toplevel still contains the cwd, but would
+    key the worktree records differently and skip the existing one."""
+    lay = Layout(tmp_path)
+    worktree = _linked_worktree(lay)
+    run("start", worktree, home=lay.home)
+    # A plain .git directory (so the checkout no longer looks linked) whose config
+    # names the parent as the work tree: git's toplevel then contains the cwd.
+    (worktree / ".git").unlink()
+    subprocess.run(["git", "init", "-q", str(worktree)], check=True)
+    subprocess.run(["git", "-C", str(worktree), "config", "core.worktree", str(lay.base)], check=True)
+    done = run("stop", worktree, home=lay.home)
+    assert done.stdout == ""
+    assert "but the checkout is" in done.stderr
+
+
+def test_core_worktree_on_a_plain_checkout_refuses(tmp_path: Path) -> None:
+    lay = Layout(tmp_path)
+    subprocess.run(["git", "-C", str(lay.project), "config", "core.worktree", str(lay.project)], check=True)
+    done = run("start", lay.project, home=lay.home)
+    assert done.stdout == "" and "core.worktree is set" in done.stderr
+
+
+def test_a_planted_bare_repository_is_not_discovered(tmp_path: Path) -> None:
+    """In a project with no .git, HEAD / config / objects planted in the cwd would make
+    git treat it as a bare repository whose core.worktree names a parent."""
+    lay = Layout(tmp_path)
+    plain = lay.base / "plain"
+    plain.mkdir()
+    subprocess.run(["git", "init", "-q", "--bare", str(plain)], check=True)
+    subprocess.run(["git", "--git-dir", str(plain), "config", "core.worktree", str(lay.base)], check=True)
+    subprocess.run(["git", "--git-dir", str(plain), "config", "core.bare", "false"], check=True)
+    done = run("start", plain, home=lay.home)
+    # Either refused, or served for the directory itself: never for the parent.
+    assert done.stdout == "" or f"--project={plain.resolve()} " in done.stdout
+    assert f"--project={lay.base} " not in done.stdout