T09 · Insecure Skill Coding Practices
- Location
scripts/nightly-run.py:32- Finding
Remote Command Injection Through Attacker-Controlled GitHub Issue Content
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is openly about autonomous GitHub contributions, but its runnable scripts handle tokens and public PR actions in ways that need careful review before use.
Review this skill before installing or running it. Use only a fine-grained GitHub token with the smallest possible repository permissions, avoid storing the token in the skill config, do not run the nightly runner until shell command construction and token handling are fixed, and require manual review before any fork, push, or PR is made under your account.
scripts/nightly-run.py:32Remote Command Injection Through Attacker-Controlled GitHub Issue Content
scripts/nightly-run.py:39GitHub Token Exposed in Process Arguments and Git Repository Configuration
scripts/setup.py:54Setup Stores a Visibly Entered GitHub Token in a Plaintext Configuration File
scripts/nightly-run.py:343Working Nightly Runner Bypasses Configured Approval, Scheduling, Testing, and Review Controls
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- Python 3.10+
- Git
- GitHub Personal Access Token with `public_repo` scope
- OpenClaw with sessions_spawn capability
## GitHub Token Setup
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
pkill -f contrib-pipeline
# Revoke GitHub token
# Go to: GitHub Settings → Developer settings → Personal access tokens → Delete
# Review recent commits
git log --author="your-email" --since="1 week ago"
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
pkill -f contrib-pipeline
# Revoke GitHub token
# Go to: GitHub Settings → Developer settings → Personal access tokens → Delete
# Review recent commits
git log --author="your-email" --since="1 week ago"
The description presents a functioning autonomous GitHub contribution agent that scouts repositories/issues, implements fixes, and submits pull requests using a multi-agent pattern. The supplied code does not do those core actions. Instead, it creates directories, loads config, checks approval statistics, writes a scout task file, returns prompt strings/dicts for several conceptual subagents, and logs startup. The script explicitly states that actual subagent spawning requires future OpenClaw integration and only provides task definitions. This is a material description-behavior mismatch because the primary advertised capability—autonomous contribution workflow with git/network I/O and PR submission—is not implemented in the provided code chunk.
The code broadly aligns with the high-level theme of autonomous open-source GitHub contribution: it searches issues, makes small fixes, and submits PRs. However, the declared description contains important architectural and behavioral claims that this code does not match. The description says Buck handles all git/network I/O and subagents only do cognitive work, but this script itself directly calls GitHub APIs, forks repos, clones via git, pushes branches, and opens PRs. It is also a specific nightly batch automation with daily quotas and reporting, which is a materially different operating mode from the declared generic skill description. Finally, its fixing capability is much narrower than implied: it mainly handles documentation, examples, contributing guides, code of conduct files, and simple typo fixes via heuristics, with no evidence of the stated three difficulty levels or an Architect-Builder subagent pattern. Therefore the description does not accurately represent the actual code chunk.
The declared description presents a full autonomous GitHub contribution system that interacts with repositories, issues, and PR workflows. The supplied code chunk does not do any of that. It only reads a local log file under /home/wahaj/.openclaw/workspace/contrib-scout/logs/contributions.jsonl, parses stored contribution records, and prints status metrics and rule reminders such as a daily quota and pending PR count. This is materially different from the declared primary purpose and omits the core advertised behaviors (scouting, fixing, PR submission, Architect-Builder orchestration, and git/network operations). While status tracking could be a supporting tool in such a system, this chunk’s actual behavior is specifically local rule enforcement/reporting rather than the declared autonomous contribution agent behavior.
Using subprocess.run(..., shell=True) in a workflow that builds commands from untrusted external inputs enables tool-parameter abuse and shell metacharacter injection. This is especially dangerous here because the skill clones attacker-chosen repositories and processes issue metadata from GitHub, creating a realistic path from remote-controlled text to arbitrary command execution, filesystem deletion, and credential compromise.
def run_cmd(cmd, cwd=None, timeout=60):
try:
r = subprocess.run(cmd, shell=True, cwd=cwd, capture_output=True, text=True, timeout=timeout)
return r.returncode, r.stdout, r.stderr
except Exception as e:
return -1, "", str(e)
The code skips non-English content by rejecting titles containing CJK characters, which imposes a language policy choice automatically. This is a natural-language policy violation because the user is not offered a language preference or informed consent for excluding non-English projects.
The script facilitates credential access by instructing the user to create and enter a GitHub personal access token with repository scope. In isolation that may be expected for GitHub automation, but combined with local plaintext persistence it becomes a meaningful security issue because it captures a reusable secret for later use.
"""Get GitHub token from user"""
print("\n📋 GitHub Configuration")
print("-" * 40)
print("You need a GitHub Personal Access Token with 'public_repo' scope.")
print("Create one at: https://github.com/settings/tokens")
print()
The script writes the full configuration, including any provided github_token, to ~/.openclaw/workspace/contrib-scout/config.json in plaintext. Plaintext secret storage on disk materially increases exposure through local compromise, backups, logs, shell history from review commands, or overly permissive file permissions.
The README immediately advertises an agent that autonomously scouts repositories, writes fixes, and submits pull requests, but it does not place a prominent up-front warning that the tool performs external actions under the user's GitHub identity. For a skill that can make networked changes to third-party systems, delayed disclosure increases the risk of a user installing or running it without fully appreciating that it can automatically create commits and PRs.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
**Mitigation:** Auto-pause on rejection rate >30%, start with simple fixes.
### 3. Token Security
- Token with `public_repo` can write to all your public repos
- If compromised, attacker can push malicious code under your name
**Mitigation:**
The skill describes operations that require sensitive capabilities such as reading a GitHub token from disk, cloning repositories, writing files, and invoking shell/git, but it does not declare an explicit tool scope or allowed-tools boundary. In an agent framework, missing capability scoping increases the chance the skill is invoked with broader-than-necessary privileges, enabling unintended file access, shell execution, or credential exposure if the workflow is abused or prompt-injected.
The trigger set includes very broad terms like 'github', 'open source', 'PR', and 'issue', which can cause the skill to activate in many unrelated conversations. Overbroad activation is dangerous because it can route ordinary developer discussions into a high-privilege workflow involving repository operations, token use, shell commands, and file writes without sufficiently specific user intent.
The skill intentionally creates persistent local state through cloned repositories, branches, logs, configs, and nightly reports. Persistent workspace state can leak repository contents, retain credentials or sensitive metadata, and be reused across sessions in ways that confuse provenance or enable unintended follow-on actions, especially in an autonomous contribution workflow.
The main agent does ALL the I/O work directly (no subagent):
1. Fork the repo via GitHub API
2. Shallow clone: `git clone --depth 1`
3. Create branch: `fix/<issue-number>-<short-description>`
4. Read `CONTRIBUTING.md` and relevant source files
5. Identify the files that need to change
6. Read the full content of those files
The pipeline explicitly permits fully autonomous contributions at complexity level 1 via the 'no approval needed' policy. In the context of an agent that scouts repositories, writes changes, and prepares PR workflows, this removes a human checkpoint and can lead to unintended code changes, policy violations, spammy PRs, or harmful edits if issue classification is wrong. The surrounding safeguards are heuristic and text-based, which makes autonomous action in a network-facing GitHub contribution agent more dangerous than in a purely local workflow.
def get_max_repos_for_level(level, stats):
"""Determine max repos based on complexity level and approval rate"""
limits = {
1: 3, # Typo fixes - no approval needed
2: 3 if stats['rate'] > 0.5 else 0,
3: 2 if stats['rate'] > 0.7 else 0,
4: 1 if stats['rate'] > 0.9 else 0
The module docstring says this is a 'Subagent workflow for autonomous GitHub contributions,' and multiple helpers are named/documented as 'Spawn ... agent'. However, the corresponding functions merely return prompt strings or write a task file, while the main routine explicitly notes that actual spawning is still a TODO.
This code file accesses a sensitive credential via os.environ.get('GITHUB_TOKEN', ''). While the config docstring says it loads user configuration, there is no explicit user-facing warning, print/log statement, or comment disclosing that the skill reads credentials from environment variables.
The pipeline encodes autonomous go/no-go logic for making open-source contributions based on prior approval-rate heuristics, including a path marked 'no approval needed' for level-1 work. In the skill context, which is explicitly an autonomous GitHub contribution agent, this increases the risk of unsupervised external actions such as modifying third-party repositories or generating unwanted PRs once the missing execution pieces are added.
def get_max_repos_for_level(level, stats):
"""Determine max repos based on complexity level and approval rate"""
limits = {
1: 3, # Typo fixes - no approval needed
2: 3 if stats['rate'] > 0.5 else 0,
3: 2 if stats['rate'] > 0.7 else 0,
4: 1 if stats['rate'] > 0.9 else 0
The manifest describes an autonomous GitHub contribution agent whose main agent handles git/network I/O and submits pull requests. In this file, the main flow stops after generating task definitions and explicitly states that actual subagent spawning is not implemented, so the behavior is materially narrower than the claimed end-to-end contribution capability.
The helper executes arbitrary shell strings with shell=True, and multiple call sites interpolate untrusted data from GitHub issue content, repository names, branch names, URLs, and filesystem paths into those commands. In this skill's context, the agent autonomously performs network and git operations against attacker-controlled repositories/issues, so command injection could lead to arbitrary code execution and follow-on token theft or destructive local actions.
def run_cmd(cmd, cwd=None, timeout=60):
try:
r = subprocess.run(cmd, shell=True, cwd=cwd, capture_output=True, text=True, timeout=timeout)
return r.returncode, r.stdout, r.stderr
except Exception as e:
return -1, "", str(e)
The script uses a GitHub token to make API calls that search issues, fork repositories, and create pull requests, but there is no explicit warning in the code comments or user-facing messaging that it will perform account-affecting remote actions. The high-level module docstring states the purpose, but it does not meaningfully warn the user that the script will autonomously modify their GitHub account state and publish contributions.
The script embeds the GitHub token directly into a shell command line for curl, which can expose the secret via process listings, shell debugging, crash logs, or other local monitoring on the host. Because this agent performs unattended authenticated actions, compromise of that token would let an attacker create forks/PRs and potentially access other GitHub resources available to the token.
"Accept": "application/vnd.github.v3+json",
"User-Agent": "OpenClaw-Contributor"
}
cmd = f'curl -s -H "Authorization: token {token}" -H "Accept: application/vnd.github.v3+json" '
if data:
cmd += f'-H "Content-Type: application/json" -X {method} -d \'{json.dumps(data)}\' "{url}"'
else:
The script deletes local directories with rm -rf and invokes git clone through subprocess.run(..., shell=True) without any confirmation prompt or user-facing warning at the point of execution. Although the file has brief docstrings, they do not disclose the destructive cleanup behavior or shell execution risk to the user.
The setup flow explicitly prompts for a GitHub personal access token and later places it into the runtime configuration, creating a credential collection path inside the skill. In the context of an autonomous contribution agent, this expands the trust boundary and makes compromise of the local workspace or accidental disclosure of the config sufficient to expose GitHub account access.
No suspicious patterns detected.