From 6778e00054f22e0f81623421739674c10dae9977 Mon Sep 17 00:00:00 2001 From: Mengye Ren Date: Sun, 11 Oct 2026 10:58:09 -0400 Subject: [PATCH 1/2] Skip the filter-override listing in the .git guard's own object read --- CHANGELOG.md | 4 ++++ src/outerloop/github.py | 28 +++++++++++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6ac092a..f0a88647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ Versions follow [SemVer](https://semver.org). - The review and verify posting CLIs now share one gate for the untrusted emitted envelope, so a security fix to the decode/identity/kind checks lands in both (#337). +- The `.git` guard's object read no longer derives filter-driver overrides + (`cat-file -e` never converts working-tree files), dropping one + `git config --get-regexp` subprocess from every guarded kernel git call + with no change to what is refused or neutralized (#512). - A review round refused because the pinned harness is too old for the model, or because the runner's harness install is missing or stale, now says which and how to fix it instead of calling it an API outage (#475). diff --git a/src/outerloop/github.py b/src/outerloop/github.py index f1570b22..b859a6ae 100644 --- a/src/outerloop/github.py +++ b/src/outerloop/github.py @@ -1248,9 +1248,16 @@ def _unreadable(err: OSError) -> None: sha = _head_commit_sha(git_dir) or _any_ref_sha(git_dir) if sha is not None: try: + # The read carries no filter overrides (pairs=[]): `cat-file -e` + # only resolves an object in the store, never converts + # working-tree files, so no filter driver can run in it — and + # every other child-spawning setting is already neutralized on + # the argv above (SAFE_GIT_FLAGS) or stripped from the config by + # _ensure_regular_config. Skipping the override listing keeps the + # guard to ONE git subprocess per call. _run_git( ["git", "-C", str(root), *SAFE_GIT_FLAGS, "cat-file", "-e", f"{sha}^{{commit}}"], - _git_env(None, Path(root)), + _git_env(None, pairs=[]), timeout=GUARD_GIT_TIMEOUT_S, # a stalled read is refused, not waited on ) except GitError as exc: @@ -1487,12 +1494,17 @@ def _run_git_with_credential( raise GitError(message) from None -def _git_env(token: str | None, root: Path | None = None) -> dict[str, str]: +def _git_env( + token: str | None, root: Path | None = None, pairs: list[tuple[str, str]] | None = None +) -> dict[str, str]: """Environment for a git invocation: host global/system config never loads (a host-configured filter driver must not be selectable by a session-written .gitattributes), repo-defined filter drivers are overridden to passthrough, and the token — present only for network - subcommands — rides an env-injected header.""" + subcommands — rides an env-injected header. `pairs` supplies the filter + overrides already derived for this call, skipping the config-listing + subprocess (the guard's object read passes [] — `cat-file -e` never + converts working-tree files, so no filter driver can run in it).""" env = dict(os.environ) env["GIT_TERMINAL_PROMPT"] = "0" env["GIT_CONFIG_GLOBAL"] = "/dev/null" @@ -1503,11 +1515,13 @@ def _git_env(token: str | None, root: Path | None = None) -> dict[str, str]: # for a real one during post-session verification. The kernel never # uses replace refs; disable them on every invocation. env["GIT_NO_REPLACE_OBJECTS"] = "1" - pairs = _filter_override_pairs(root) + if pairs is None: + pairs = _filter_override_pairs(root) if token is not None: - pairs.append( - ("http.https://github.com/.extraheader", f"Authorization: Basic {_basic(token)}") - ) + pairs = [ + *pairs, + ("http.https://github.com/.extraheader", f"Authorization: Basic {_basic(token)}"), + ] env["GIT_CONFIG_COUNT"] = str(len(pairs)) for i, (k, v) in enumerate(pairs): env[f"GIT_CONFIG_KEY_{i}"] = k From 22f1981c178ee0b599b1c7547f64013ad377de9f Mon Sep 17 00:00:00 2001 From: Mengye Ren Date: Sun, 11 Oct 2026 11:11:44 -0400 Subject: [PATCH 2/2] Call the guard's object read without a root instead of a new pairs parameter --- src/outerloop/github.py | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/outerloop/github.py b/src/outerloop/github.py index b859a6ae..7d1bc329 100644 --- a/src/outerloop/github.py +++ b/src/outerloop/github.py @@ -1248,16 +1248,11 @@ def _unreadable(err: OSError) -> None: sha = _head_commit_sha(git_dir) or _any_ref_sha(git_dir) if sha is not None: try: - # The read carries no filter overrides (pairs=[]): `cat-file -e` - # only resolves an object in the store, never converts - # working-tree files, so no filter driver can run in it — and - # every other child-spawning setting is already neutralized on - # the argv above (SAFE_GIT_FLAGS) or stripped from the config by - # _ensure_regular_config. Skipping the override listing keeps the - # guard to ONE git subprocess per call. + # No root, so no filter-override listing: `cat-file -e` never + # converts working-tree files, so no filter driver can run in it. _run_git( ["git", "-C", str(root), *SAFE_GIT_FLAGS, "cat-file", "-e", f"{sha}^{{commit}}"], - _git_env(None, pairs=[]), + _git_env(None), timeout=GUARD_GIT_TIMEOUT_S, # a stalled read is refused, not waited on ) except GitError as exc: @@ -1494,17 +1489,12 @@ def _run_git_with_credential( raise GitError(message) from None -def _git_env( - token: str | None, root: Path | None = None, pairs: list[tuple[str, str]] | None = None -) -> dict[str, str]: +def _git_env(token: str | None, root: Path | None = None) -> dict[str, str]: """Environment for a git invocation: host global/system config never loads (a host-configured filter driver must not be selectable by a session-written .gitattributes), repo-defined filter drivers are overridden to passthrough, and the token — present only for network - subcommands — rides an env-injected header. `pairs` supplies the filter - overrides already derived for this call, skipping the config-listing - subprocess (the guard's object read passes [] — `cat-file -e` never - converts working-tree files, so no filter driver can run in it).""" + subcommands — rides an env-injected header.""" env = dict(os.environ) env["GIT_TERMINAL_PROMPT"] = "0" env["GIT_CONFIG_GLOBAL"] = "/dev/null" @@ -1515,13 +1505,11 @@ def _git_env( # for a real one during post-session verification. The kernel never # uses replace refs; disable them on every invocation. env["GIT_NO_REPLACE_OBJECTS"] = "1" - if pairs is None: - pairs = _filter_override_pairs(root) + pairs = _filter_override_pairs(root) if token is not None: - pairs = [ - *pairs, - ("http.https://github.com/.extraheader", f"Authorization: Basic {_basic(token)}"), - ] + pairs.append( + ("http.https://github.com/.extraheader", f"Authorization: Basic {_basic(token)}") + ) env["GIT_CONFIG_COUNT"] = str(len(pairs)) for i, (k, v) in enumerate(pairs): env[f"GIT_CONFIG_KEY_{i}"] = k