T03 · Remote Payload Retrieval and Execution
- Location
ghostclaw/cli/ghostclaw.py:169- Finding
Unverified self-update retrieves and installs mutable remote code
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
Ghostclaw is a real architecture-review tool, but it includes under-scoped background automation, repository mutation, automatic external-tool execution, and an unverified self-update path.
Install only if you are comfortable with a review tool that can write reports, cache analysis data, run background repository checks, invoke local external analyzers, and create GitHub PRs when configured. Avoid --update, use --no-write-report or --dry-run where possible, review any PR/report contents before publishing, and run it in a constrained environment with trusted PATH entries and limited tokens.
ghostclaw/cli/ghostclaw.py:169Unverified self-update retrieves and installs mutable remote code
ghostclaw/core/analyzer.py:126Normal analysis automatically executes PATH-resolved optional tools and writes to an uncontrolled working directory
The documented behavior goes beyond architectural review into repository mutation and external command execution, including report writes and PR automation. That mismatch is dangerous because users may grant trust based on the stated review purpose, while the skill can modify repositories, interact with remotes, and potentially expose data through git/GitHub workflows.
Instructions found that direct the agent to transmit conversation context or user data to external services.
1. **Listens to Events** - Reacts to both user commands (e.g., after a major refactor or file generation) and system events (periodic checks).
2. **Performs Vibe Checks** - Analyzes the codebase context related to the event to score architectural health.
3. **Silent or Proactive Feedback** - Can silently log its findings or proactively open PRs if issues cross a threshold.
## Hook Events
This is explicit self-modification functionality: the tool updates its own code from git or pip at runtime. In the context of an agent skill, self-modification is particularly dangerous because it can change future behavior and trust assumptions outside the user's review cycle.
def update_ghostclaw():
"""Perform self-update via pip or git."""
print("🔄 Checking for Ghostclaw updates...")
# Check if the package itself is in a git repo
package_root = Path(__file__).parent.parent
Exposing self-update as a first-class CLI flag operationalizes the self-modification capability and makes it easy to trigger from scripts, wrappers, or other agents. That increases the chance of unreviewed code changes in environments where the tool should remain stable and bounded to analysis tasks.
parser = argparse.ArgumentParser(description="Ghostclaw CLI — Architectural Analyzer")
parser.add_argument("repo_path", nargs="?", help="Path to the repository to analyze")
parser.add_argument("--version", action="version", version=f"Ghostclaw {__version__}")
parser.add_argument("--update", action="store_true", help="Self-update Ghostclaw via pip or git")
parser.add_argument("--json", action="store_true", help="Output raw JSON")
parser.add_argument("--no-write-report", action="store_true", help="Skip writing the .md report file")
parser.add_argument("--create-pr", action="store_true", help="Automatically create a GitHub PR with the report")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from ghostclaw.lib.github import GitHubClient
from ghostclaw.lib.notify import Notifier
# Load .env file if present (secrets management)
load_dotenv()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Directories that should always be excluded from analysis
EXCLUDE_DIRS: Set[str] = {
'.venv', 'venv', '.env', '__pycache__', '.pytest_cache', '.coverage',
'.git', '.hg', '.svn', 'node_modules', 'dist', 'build', 'target',
'vendor', '.deps', 'tests', 'test', 'spec', 'specs', 'docs', 'doc',
'example', 'examples', 'scripts'
The skill advertises operational behaviors that require shell, filesystem, and environment access, but it does not declare any tool scope or permissions boundaries. This creates a capability-transparency gap: a user or orchestrator may invoke it expecting passive analysis, while the skill can write files, inspect environment variables, and execute external commands such as git or gh.
The top-level description uses broad language like code review, monitoring, and opening PRs in ways that can match ordinary user requests without making the side effects prominent. In an agent ecosystem, overly broad triggers increase the chance of accidental invocation of a skill that can write to repositories or perform automated remote actions.
The core trigger list includes subjective phrases like a module feels off or ongoing vibe health monitoring, which are broad enough to capture routine review tasks. Because this skill includes write/report/PR capabilities, ambiguous activation conditions raise the risk of unintended execution and repository changes in contexts where the user only wanted advisory analysis.
Watcher mode states that it clones or pulls repositories and opens PRs if credentials are available, but the markdown does not present this as a prominent warning or consent boundary. Background automation combined with repo mutation and token use is especially risky because users may schedule it as a passive monitor without appreciating that it can continuously access repositories and create remote changes.
The hook is declared to trigger on both "command" and "system" events with a broad description of automatic review after significant commands, but it does not narrowly define which commands, repositories, or trust boundaries apply. In an agent-integrated environment, overly broad hook activation increases the chance of unintended execution on sensitive contexts, creating excessive access to repository contents and potentially surprising autonomous behavior.
The documentation states that the hook can silently log findings or proactively open PRs, but it does not present a strong warning about modifying repositories or taking outward actions on the user's behalf. In this skill context, autonomous PR creation is especially sensitive because it can change code, trigger CI/CD, and affect collaboration workflows if users do not clearly understand or consent to that behavior.
The system-event behavior is described as reacting to periodic heartbeats or cron schedules for deep repository-wide scanning, which is an expansive background execution model. Without clear limits on cadence, scope, and authorization, this can lead to continuous access to codebases and metadata beyond user expectations, increasing the risk of privacy and operational issues.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def detect_github_remote(repo_path: str) -> Optional[str]:
"""Detect if the repository has a GitHub remote (origin)."""
try:
result = subprocess.run(
["git", "remote", "get-url", "origin"],
cwd=repo_path,
capture_output=True,
Automatic PR creation creates a branch, commits a report, pushes to GitHub, and opens a PR without an explicit privacy or outbound-transmission warning. Because the report may contain sensitive architectural findings from a private repository, the lack of a clear disclosure materially increases the risk of unintended data exposure.
Creating and switching to a new branch persists changes in the user's repository state beyond the current session. For a supposedly analytical assistant, that persistence is risky because it can disrupt workflows, trigger automation, and combine with later commit/push steps to publish data unexpectedly.
branch_name = f"ghostclaw/arch-report-{timestamp}"
try:
# Create branch
subprocess.run(["git", "checkout", "-b", branch_name], cwd=repo_path, check=True, capture_output=True, text=True)
# Add report
subprocess.run(["git", "add", report_file.name], cwd=repo_path, check=True, capture_output=True, text=True)
This command creates and checks out a new git branch in a user-supplied repository, which is a persistent modification to the target workspace. In the context of an analysis tool, unexpectedly altering repository state increases risk, especially when the skill can be run as an automated sub-agent or watcher.
try:
# Create branch
subprocess.run(["git", "checkout", "-b", branch_name], cwd=repo_path, check=True, capture_output=True, text=True)
# Add report
subprocess.run(["git", "add", report_file.name], cwd=repo_path, check=True, capture_output=True, text=True)
# Commit
This stages a generated file into the repository index, which modifies the user's repo state and prepares data for later publication. In a tool advertised for architectural review, automatic staging is more dangerous because users may not expect an analyzer to alter version-control state.
# Create branch
subprocess.run(["git", "checkout", "-b", branch_name], cwd=repo_path, check=True, capture_output=True, text=True)
# Add report
subprocess.run(["git", "add", report_file.name], cwd=repo_path, check=True, capture_output=True, text=True)
# Commit
subprocess.run(["git", "commit", "-m", f"Add architecture report: {report_file.name}"], cwd=repo_path, check=True, capture_output=True, text=True)
# Push
This creates a commit automatically, permanently recording generated content into repository history. That can capture sensitive architectural details or internal findings and is especially risky for an analysis assistant that may process private codebases.
# Add report
subprocess.run(["git", "add", report_file.name], cwd=repo_path, check=True, capture_output=True, text=True)
# Commit
subprocess.run(["git", "commit", "-m", f"Add architecture report: {report_file.name}"], cwd=repo_path, check=True, capture_output=True, text=True)
# Push
subprocess.run(["git", "push", "origin", branch_name], cwd=repo_path, check=True, capture_output=True, text=True)
# Create PR
This pushes a branch to the remote origin, transmitting repository changes and report contents to an external service. For a code-analysis skill, that is a meaningful security and privacy boundary crossing because internal architecture data may leave the local environment.
# Commit
subprocess.run(["git", "commit", "-m", f"Add architecture report: {report_file.name}"], cwd=repo_path, check=True, capture_output=True, text=True)
# Push
subprocess.run(["git", "push", "origin", branch_name], cwd=repo_path, check=True, capture_output=True, text=True)
# Create PR
pr_cmd = ["gh", "pr", "create", "--title", title, "--body", body]
result = subprocess.run(pr_cmd, cwd=repo_path, capture_output=True, text=True, check=True)
Creating a GitHub PR automatically publishes analysis results and metadata to GitHub, potentially exposing sensitive internal architecture or security observations. Although the command is not injection-prone, it performs outbound publication that is risky in the context of an analyzer skill.
subprocess.run(["git", "push", "origin", branch_name], cwd=repo_path, check=True, capture_output=True, text=True)
# Create PR
pr_cmd = ["gh", "pr", "create", "--title", title, "--body", body]
result = subprocess.run(pr_cmd, cwd=repo_path, capture_output=True, text=True, check=True)
print(f"🔗 PR created: {result.stdout.strip()}")
except subprocess.CalledProcessError as e:
print(f"❌ Failed to create PR: {e.stderr or e}", file=sys.stderr)
The CLI includes a self-update mechanism that pulls code from git or upgrades via pip, which is unrelated to the core function of repository architecture analysis. In a security-sensitive agent ecosystem, this broadens the attack surface and enables code changes from external sources during normal tool operation.
The self-update flow modifies the local installation through git pull and pip install without an explicit warning that the tool will change its own code and dependencies. Lack of informed consent is dangerous here because users may trigger substantial code changes from network sources under the guise of a simple maintenance action.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# Check if the package itself is in a git repo
package_root = Path(__file__).parent.parent
try:
is_git = subprocess.run(
["git", "rev-parse", "--is-inside-work-tree"],
cwd=package_root,
capture_output=True,
Running git pull against the package's own repository modifies the installed tool's source code in place, which is a form of self-modification from a network source. In an agent skill, this is risky because it can silently alter the executable logic and trust boundary after deployment.
if is_git:
print(f"Detected git repository at {package_root}. Pulling latest changes...")
subprocess.run(["git", "pull"], cwd=package_root, check=True)
print("✅ Updated via git.")
# Also run pip install . to ensure dependencies are updated if pyproject.toml changed
subprocess.run([sys.executable, "-m", "pip", "install", "-e", "."], cwd=package_root, check=True)
No suspicious patterns detected.