fix(security): close GitSpawn RCE class — malicious repo .git/config no longer executes on context gathering (GHSA-7x36-8jrh-v4pw)
Hermes gathers workspace context by running git against the session directory automatically — the coding-workspace snapshot, gateway project-tree build, /diff, @diff|@staged context refs, goal-gate fingerprint, and -w startup worktree add — before any prompt, tool call, approval, or trust gate. Those probes ran the system git without stripping the repository's own config, so a repo delivered as files with its .git directory intact (a shared zip, sync folder, or USB stick; git clone never transfers .git/config) could set an execution-sink git setting and get arbitrary host code execution as the user with nothing on screen. - core.fsmonitor / core.hooksPath / pager / editor / credential helper: neutralized by routing every automatic probe through noninteractive_git_env(), which pins those keys to inert values via GIT_CONFIG_* and ignores global/system config. bounded_git_probe (the reported sink, coding_context._git + tui_gateway.git_probe) now defaults to that env; worktree-add, working_diff, web_git, context_references, goals, and subagent_worktree route through it too. - Attribute-scoped [diff "x"] command=/textconv= drivers: the attacker names the driver in .gitattributes, so GIT_CONFIG_KEY overrides can't enumerate them. Added harden_git_argv(), which inserts --no-ext-diff --no-textconv on diff-rendering subcommands (diff/show/ log/blame) only — status et al reject the flags. Both flags required (verified empirically; each alone leaves the other live). Builds on the noninteractive_git_env config-scrubbing from the gemini-cli #28792 port. Real-git E2E regression suite arms a malicious repo and asserts every automatic path neutralizes fsmonitor, hooks, external-diff, and textconv; a baseline test proves the repo is armed.
This commit is contained in:
@@ -12,7 +12,12 @@ from pathlib import Path
|
||||
from typing import Awaitable, Callable
|
||||
|
||||
from agent.model_metadata import estimate_tokens_rough
|
||||
from hermes_cli._subprocess_compat import IS_WINDOWS, windows_hide_flags
|
||||
from hermes_cli._subprocess_compat import (
|
||||
IS_WINDOWS,
|
||||
harden_git_argv,
|
||||
noninteractive_git_env,
|
||||
windows_hide_flags,
|
||||
)
|
||||
from hermes_cli.sizefmt import format_bytes
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -425,12 +430,13 @@ def _expand_git_reference(
|
||||
_popen_kwargs = {"creationflags": windows_hide_flags()} if IS_WINDOWS else {}
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", *args],
|
||||
["git", *harden_git_argv(args)],
|
||||
cwd=cwd,
|
||||
capture_output=True,
|
||||
text=True, encoding='utf-8', errors='replace',
|
||||
timeout=30,
|
||||
stdin=subprocess.DEVNULL,
|
||||
env=noninteractive_git_env(),
|
||||
**_popen_kwargs,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
|
||||
Reference in New Issue
Block a user