Back to skill

Security audit

GitHub Issue Resolver

Security checks for vulnerabilities and agentic risk

Overview

This skill is meant to fix GitHub issues, but its safety wrappers are under-enforced and some scripts can run unsafe shell commands while using your GitHub credentials.

Use this only in a disposable checkout with a tightly scoped GitHub account or token. Do not give it access to private repositories or broad local files until command execution is changed to structured argv calls, PR creation is forced through verified approvals, and repo/branch/path scope checks are enforced at execution time.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_pr.py:10
Finding
Shell Command Injection and Guardrail Bypass in Pull Request Creation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create_pr.py:10-12, 58-71` **Vulnerability Type**: Shell command injection and missing authorization enforcement **Risk Level**: Critical ### Vulnerable Code ```python def run_cmd(cmd, check=True): """Run shell command and return output.""" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if check and result.returncode != 0: print(f"Command failed: {cmd}", file=sys.stderr) print(f"Error: {result.stderr}", file=sys.stderr) sys.exit(1) return result.stdout.strip() ``` ```python # Create branch if not exists current_branch = run_cmd("git branch --show-current") if current_branch != branch_name: run_cmd(f"git checkout -b {branch_name}") # Push branch print(f"Pushing branch {branch_name}...", file=sys.stderr) run_cmd(f"git push -u origin {branch_name}") # Create PR print("Creating Pull Request...", file=sys.stderr) cmd = f'gh pr create --title "{title}" --body-file "{body_file}"' try: pr_url = run_cmd(cmd) ``` ### Technical Analysis The script constructs shell command strings using the CLI-controlled `branch_name`, `title`, and `body_file` values and executes them through `subprocess.run(..., shell=True)`. These values are not escaped, validated, or passed as individual argument-array elements. An attacker can place shell syntax in one of these arguments. For example, a malicious title can terminate the quoted `--title` value, insert another shell command, and comment out or otherwise neutralize the remaining command text. Branch and body-file arguments provide additional injection surfaces. The script also directly performs branch creation, push, and pull-request creation without calling `Guardrails`, `Sandbox`, or an approval-verification mechanism. This contradicts the Skill's documented requirements that pushing and pull-request creation require explicit user approval. The command also omits `--draft`, despite the confi ...[truncated 1434 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `shell=True` and pass commands as argument arrays: ```python subprocess.run( ["git", "checkout", "-b", branch_name], shell=False, capture_output=True, text=True, check=True, ) ``` 2. Invoke GitHub CLI in the same manner: ```python subprocess.run( ["gh", "pr", "create", "--draft", "--title", title, "--body-file", body_file], shell=False, capture_output=True, text=True, check=True, ) ``` 3. Validate `branch_name` through `Guardrails.check_branch()` and reject names that do not use the configured working-branch prefix. 4. Resolve and validate `body_file` against the intended repository root. Reject absolute paths, traversal, symlinks escaping the repository, and denied sensitive paths. 5. Require a cryptographically unpredictable, short-lived approval artifact bound to: - The exact repository. - The exact branch and commit. - The exact push operation. - The pull-request title and draft status. 6. Perform push and pull-request creation only through the hardened sandbox implementation. 7. Add tests covering quote characters, semicolons, command substitution, newlines, option injection, path traversal, and malicious branch names. 8. Enforce `--draft` programmatically rather than relying on documentation or configuration alone. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/sandbox.py:130
Finding
Caller-Asserted Approval Allows Execution of Non-Allowlisted Commands<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sandbox.py:130-177, 243-247`; `scripts/guardrails.py:311-317` **Vulnerability Type**: Approval bypass and arbitrary command execution **Risk Level**: Critical ### Vulnerable Code ```python def execute_approved(self, command: str, action_type: str = "run_command", cwd: str = None, approved_by: str = "user") -> dict: """Execute a previously-approved command (bypasses gate, not safety checks).""" # Still check command safety (blocked commands stay blocked even with approval) cmd_check = self.guardrails.check_command(command) if not cmd_check["allowed"] and not cmd_check.get("requires_approval"): self.audit.log_guardrail_block(action_type, cmd_check["reason"], {"command": command, "approved_by": approved_by}) return { "executed": False, "blocked": True, "reason": f"Blocked even with approval: {cmd_check['reason']}" } # Log the approval self.audit.log_guardrail_approval(action_type, approved_by) # Execute work_dir = cwd or self._working_dir try: result = subprocess.run( command, shell=True, capture_output=True, text=True, cwd=work_dir, timeout=self.guardrails.behavior.get("timeoutMinutes", 15) * 60 ) ``` ```python elif cmd == "run_approved" and len(sys.argv) >= 3: command = " ".join(sys.argv[2:]) result = sandbox.execute_approved(command) print(json.dumps(result, indent=2)) ``` The command validator classifies every command not found in the allowlist as approval-requiring rather than permanently denied: ```python if not matched_allow: return { "allowed": False, "reason": f"Command '{cmd_stripped[:50]}...' not in allowlist. Request approval.", "requires_approval": True } ``` ### Technical Analysis The public `run_approved` CLI operation does not ve ...[truncated 2300 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the public `run_approved` operation. Approval should not be asserted by selecting a CLI subcommand. 2. If an approved execution endpoint is necessary, require a short-lived, single-use approval token generated only after explicit user confirmation. 3. Bind each approval token to: - A canonical executable path and complete argument array. - The repository and working directory. - The action type. - The relevant branch, issue, and commit. - An expiration time and unique nonce. 4. Treat the allowlist as a hard execution boundary. Approval may authorize a gated allowlisted action, but it should not automatically authorize arbitrary non-allowlisted executables. 5. Replace string commands and `shell=True` with argument arrays and `shell=False`. 6. Resolve executable paths using a trusted, fixed PATH or explicit absolute paths. Confirm that resolved binaries are owned by a trusted administrator and are not writable by the repository or Agent. 7. Verify authorization before logging approval. Include the approval identifier and command hash in the audit record. 8. Add negative tests proving that direct calls to the execution wrapper cannot fabricate approval and that non-allowlisted commands remain blocked. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/sandbox.py:34
Finding
Protected Branch, Repository, and Path Scope Checks Are Not Enforced During Command Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sandbox.py:34-81, 130-152`; `scripts/guardrails.py:303-306` **Vulnerability Type**: Missing scope enforcement and unsafe allowlist matching **Risk Level**: High ### Vulnerable Code The ordinary execution path checks only the command and action gate: ```python def execute(self, command: str, action_type: str = "run_command", cwd: str = None, dry_run: bool = False) -> dict: """Execute a command through guardrails.""" # Step 1: Check command against guardrails cmd_check = self.guardrails.check_command(command) if not cmd_check["allowed"] and not cmd_check.get("requires_approval"): self.audit.log_guardrail_block(action_type, cmd_check["reason"], {"command": command}) return { "executed": False, "blocked": True, "reason": cmd_check["reason"], "command": command } # Step 2: Check action gate gate = self.guardrails.check_action(action_type) # Step 3: If requires approval, return approval request if gate["needs_approval"] or cmd_check.get("requires_approval"): return { "executed": False, "blocked": False, "needs_approval": True, "action": action_type, "command": command, "gate_message": gate.get("message", "This action requires approval."), "reason": cmd_check.get("reason", "Gate requires approval") } ``` The command is later executed without repository, branch, or path validation: ```python result = subprocess.run( command, shell=True, capture_output=True, text=True, cwd=work_dir, timeout=self.guardrails.behavior.get("timeoutMinutes", 15) * 60 ) ``` The approved path has the same omission: ```python cmd_check = self.guardrails.check_command(command) ... result = subprocess.run( command, shell=True, capture_output=True, ...[truncated 3146 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse each command into a structured argument array before validation. Do not validate or execute free-form shell strings. 2. Match allowlist entries by exact executable and subcommand tokens. For example, distinguish precisely among: - `git checkout` - `git checkout -b` - `git push` - `git push -u origin` 3. Implement operation-specific validators: - Pass checkout and push branch arguments to `check_branch()`. - Validate clone owner and repository through `check_repo()`. - Pass all file operands through `check_path()` and `check_self_modify()`. - Verify that commits and pushes are associated with the currently locked issue. - Enforce diff-size and test requirements before commit or push. 4. Resolve the working directory with `realpath()` and verify that it is inside a designated workspace associated with the approved repository. Reject symlink escapes. 5. Require working branches to use the configured prefix, unless a separately recorded custom-prefix approval exists. 6. Query the active Git branch and configured remote immediately before commit and push. Compare both against the approved context. 7. Apply branch validation to push refspecs, not only to local branch names. 8. Use `shell=False` and a trusted executable path. 9. Add integration tests covering protected-branch checkout, protected refspec pushes, alternate remotes, repository-context mismatch, path traversal, symlink escape, and executable-prefix confusion. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (43)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description promises a broad autonomous GitHub issue resolution agent with end-to-end workflow support from discovery through fix and PR submission, plus guardrails. The supplied code chunk does something much narrower: it reads one specified issue and related metadata from the public GitHub API and emits JSON. It does not discover issues across a repo, modify code, generate fixes, interact with git or GitHub pull requests beyond reading linked references, or implement any guardrails. This is a material description-versus-behavior mismatch in primary purpose and capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description promises a full autonomous GitHub issue resolution workflow, including discovering issues, analyzing them, fixing bugs, and safely submitting PRs with guardrails. The supplied code only performs one limited step: creating/checking out a branch, pushing it to origin, and creating a pull request with gh. It accepts an issue number argument but does not use it. There is no logic for finding issues, inspecting repository state beyond current branch, modifying code, validating fixes, constraining scope, or enforcing safety policies. While PR creation could be a supporting component of such an agent, the code chunk itself materially underimplements the declared purpose and lacks the stated guardrails, so this is a description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description promises a much broader autonomous agent that can discover, analyze, fix issues, and submit PRs with explicit guardrails. The supplied code chunk only reads public GitHub issue data via the GitHub API, checks timelines for linked PRs, scores issue candidates, and prints ranked results. This is related to issue discovery, but it does not implement issue fixing, code changes, PR creation, repository write access, or meaningful guardrail logic. The primary behavior is a narrow issue-fetching and ranking utility, which materially underdelivers relative to the declared end-to-end resolver agent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
87% confidence
Finding
The declared description promises an autonomous GitHub issue resolver covering discovery, analysis, fixing issues, and PR submission with guardrails. The supplied code only performs issue fetching, heuristic analysis, ranking, and presentation of recommendations. While issue discovery/analysis is consistent with part of the description, the primary declared capability—actually resolving issues—is absent. No code changes, repository writes, branch/PR operations, or explicit safety guardrails are present in this chunk. Therefore the description materially overstates the implemented behavior.

Ae1

High
Category
analysis-evasion
Content
1. Load `guardrails.json` config
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
},
    "paths": {
      "denied": [
        ".env",
        ".env.*",
        "*.pem",
        "*.key",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
},
    "paths": {
      "denied": [
        ".env",
        ".env.*",
        "*.pem",
        "*.key",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"git add",
      "git commit",
      "git push",
      "git push -u origin",
      "git diff",
      "git status",
      "git branch",
Confidence
87% confidence
Finding
Allowing broad commands like `git push` and `git push -u origin` creates parameter-abuse risk because the guardrail lists only command prefixes, not tightly validated argument structures. If enforcement is naive, an agent could push to an unintended remote, ref, or branch, undermining the intended approval workflow and repository-scope restrictions.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"git push --mirror",
      "git push --delete",
      "git rebase",
      "git reset --hard",
      "git reset --merge",
      "git clean",
      "git filter-branch",
Confidence
65% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Self-Modification

High
Category
Rogue Agent
Content
| `timeoutMinutes` | 15 | Auto-abort stuck operations |
| `draftPRByDefault` | true | Always open as draft PR |
| `noForceEver` | true | `--force` push permanently blocked |
| `noSelfModify` | true | Cannot edit its own skill/plugin files |
| `autoRollbackOnTestFail` | true | Revert changes if tests fail |

## Scripts
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
| `timeoutMinutes` | 15 | Auto-abort stuck operations |
| `draftPRByDefault` | true | Always open as draft PR |
| `noForceEver` | true | `--force` push permanently blocked |
| `noSelfModify` | true | Cannot edit its own skill/plugin files |
| `autoRollbackOnTestFail` | true | Revert changes if tests fail |

## Scripts
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
| `timeoutMinutes` | 15 | Auto-abort stuck operations |
| `draftPRByDefault` | true | Always open as draft PR |
| `noForceEver` | true | `--force` push permanently blocked |
| `noSelfModify` | true | Cannot edit its own skill/plugin files |
| `autoRollbackOnTestFail` | true | Revert changes if tests fail |

## Scripts
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
| `timeoutMinutes` | 15 | Auto-abort stuck operations |
| `draftPRByDefault` | true | Always open as draft PR |
| `noForceEver` | true | `--force` push permanently blocked |
| `noSelfModify` | true | Cannot edit its own skill/plugin files |
| `autoRollbackOnTestFail` | true | Revert changes if tests fail |

## Scripts
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
python3 guardrails.py branch main

# Check if command is allowed
python3 guardrails.py command "git push --force"

# Check file path
python3 guardrails.py path ".env.production"
Confidence
70% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
python3 guardrails.py command "git push --force"

# Check file path
python3 guardrails.py path ".env.production"

# Full validation
python3 guardrails.py validate write_code owner=facebook repo=react path=src/App.tsx
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
python3 sandbox.py run_approved git push origin fix-issue-42

# Check if file is safe
python3 sandbox.py check_file .env.local

# Get status
python3 sandbox.py status
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def run_cmd(cmd, check=True):
    """Run shell command and return output."""
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    if check and result.returncode != 0:
        print(f"Command failed: {cmd}", file=sys.stderr)
        print(f"Error: {result.stderr}", file=sys.stderr)
Confidence
99% confidence
Finding
This is a true tool-parameter-abuse issue because the script builds shell commands from external inputs and executes them through a shell. In the context of an autonomous GitHub issue resolver with access to local git state and GitHub authentication, command injection is especially dangerous: it can run arbitrary local commands, tamper with repositories, steal tokens, or alter remote state.

Credential Access

High
Category
Privilege Escalation
Content
if "\x00" in normalized:
            return {"allowed": False, "reason": "Path contains null byte (injection attempt)"}

        # ── STRIP WHITESPACE for matching (catch ".env " with trailing space) ──
        stripped = normalized.strip()
        stripped_nfkc = normalized_nfkc.strip()
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Self-Modification

High
Category
Rogue Agent
Content
results["blocked_by"] = "path"
                return results

            # Self-modify check
            self_check = self.check_self_modify(kwargs["path"])
            results["checks"].append({"check": "self_modify", "result": self_check})
            if not self_check["allowed"]:
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Step 5: Execute
        work_dir = cwd or self._working_dir
        try:
            result = subprocess.run(
                command,
                shell=True,
                capture_output=True,
Confidence
97% confidence
Finding
Passing unstructured command text into subprocess.run with shell=True is a classic tool-parameter-abuse primitive. In this skill, the agent is explicitly designed to operate on repositories and issues autonomously, so attacker-influenced content could be transformed into shell commands, leading to arbitrary command execution, data exfiltration, repository sabotage, or lateral movement if the runtime has credentials.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Execute
        work_dir = cwd or self._working_dir
        try:
            result = subprocess.run(
                command, shell=True, capture_output=True, text=True,
                cwd=work_dir,
                timeout=self.guardrails.behavior.get("timeoutMinutes", 15) * 60
Confidence
98% confidence
Finding
The pre-approved execution path still accepts a raw shell command and executes it with shell=True, which preserves the same abuse surface while potentially encouraging over-trust because it is marked approved. If approval decisions can be socially engineered or based on a non-canonical command string, an attacker may smuggle dangerous shell behavior into the approved command.

Self-Modification

High
Category
Rogue Agent
Content
def check_file_safe(self, file_path: str) -> dict:
        """Check if a file is safe to read/modify."""
        path_check = self.guardrails.check_path(file_path)
        self_check = self.guardrails.check_self_modify(file_path)

        if not path_check["allowed"]:
            self.audit.log_guardrail_block("file_access", path_check["reason"],
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
def check_file_safe(self, file_path: str) -> dict:
        """Check if a file is safe to read/modify."""
        path_check = self.guardrails.check_path(file_path)
        self_check = self.guardrails.check_self_modify(file_path)

        if not path_check["allowed"]:
            self.audit.log_guardrail_block("file_access", path_check["reason"],
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
def check_file_safe(self, file_path: str) -> dict:
        """Check if a file is safe to read/modify."""
        path_check = self.guardrails.check_path(file_path)
        self_check = self.guardrails.check_self_modify(file_path)

        if not path_check["allowed"]:
            self.audit.log_guardrail_block("file_access", path_check["reason"],
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
def check_file_safe(self, file_path: str) -> dict:
        """Check if a file is safe to read/modify."""
        path_check = self.guardrails.check_path(file_path)
        self_check = self.guardrails.check_self_modify(file_path)

        if not path_check["allowed"]:
            self.audit.log_guardrail_block("file_access", path_check["reason"],
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Static analysis

No suspicious patterns detected.