Back to skill

Security audit

Finishing Branch

Security checks for vulnerabilities and agentic risk

Overview

The skill fits a branch-finishing workflow, but it can run project commands and delete local or remote Git branches with weak safeguards, so it needs review before installation.

Install only if you are comfortable giving the skill access to run project test commands and perform Git/GitHub mutations. Prefer reviewing commands before execution, avoid using the --cleanup or --delete script flags on shared repositories, and install from a pinned commit or verified source rather than the mutable npx command.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T08 · Insecure Dependencies

Warning
Location
README.md:24
Finding
Unpinned npx Installer Creates a Mutable Supply-Chain Execution Path## Vulnerability Details **File Location**: `README.md:24` **Vulnerability Type**: Unpinned third-party installer and mutable source reference **Risk Level**: Medium **Vulnerable Code Snippet**: ```bash npx add https://github.com/wpank/ai/tree/main/skills/tools/finishing-branch ``` ### Technical Analysis The installation command invokes `npx add` without pinning the `add` npm package to a reviewed version. Depending on the local npm environment, `npx` can download and execute the package resolved under that name. The command also references the mutable `main` branch of a remote GitHub repository rather than a specific reviewed commit. Consequently, the effective installation code and installed content can change after this project has been audited. No checksum, signature, package lock, or commit identifier is used to verify the retrieved components. This is an insecure dependency and installation pattern. The audited repository does not itself contain evidence that the current remote package or repository is malicious, but the documented command establishes a supply-chain execution path that could become malicious following compromise or unauthorized modification of either source. ### Attack Path 1. An attacker compromises, takes control of, or maliciously updates the npm package resolved as `add`, or modifies the referenced GitHub repository's `main` branch. 2. The attacker adds malicious installation or lifecycle behavior to the affected component. 3. A user follows the documented installation command. 4. `npx` resolves and potentially executes the unpinned npm package, while the installer retrieves content from the mutable remote branch. 5. The malicious code executes with the operating-system privileges and environment access of the user running the installation. ### Impact Assessment Successful exploitation could execute arbitrary commands with the installing user's privileges. Depending on that user's environ ...[truncated 374 chars]
Remediation
## Remediation Suggestions - Replace the generic `npx add` invocation with a trusted installer pinned to an exact, reviewed version. - Pin the remote project to an immutable commit hash rather than the `main` branch. - Publish and verify a cryptographic checksum or signature for the installed content. - Disable or avoid package lifecycle scripts where they are unnecessary. - Prefer a transparent installation procedure that downloads or copies reviewed files without executing an unrelated npm package. - Document the exact expected commit and verification command so users can validate the source before installation.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/finish_branch.py:218
Finding
Remote Branch Is Deleted Even When Safe Local Deletion Fails## Vulnerability Details **File Location**: `scripts/finish_branch.py:218-228` **Vulnerability Type**: Missing fail-closed control for destructive branch cleanup **Risk Level**: Medium **Vulnerable Code Snippet**: ```python rc, _, err = run_git_rc("branch", "-d", branch) if rc == 0: print(f" Deleted local branch '{branch}'.") else: print(f" Could not delete local branch: {err}") print(f" If unmerged, use: git branch -D {branch}") # Delete remote branch rc, _, err = run_git_rc("push", "origin", "--delete", branch) if rc == 0: print(f" Deleted remote branch 'origin/{branch}'.") else: print(f" Could not delete remote branch (may not exist): {err}") ``` ### Technical Analysis The local deletion uses `git branch -d`, which provides a safety check and normally refuses to delete a branch that Git considers unmerged. However, failure of that safety check does not stop the cleanup procedure. Execution falls through unconditionally to: ```python run_git_rc("push", "origin", "--delete", branch) ``` Thus, an unmerged local branch can be retained because `git branch -d` rejects deletion while its remote counterpart is still deleted. The script also does not independently verify that the branch is an ancestor of the intended target branch before deleting the remote reference, and `--cleanup` does not require an explicit destructive confirmation. Test success, a clean worktree, and generation of a merge summary do not prove that the feature branch has already been merged. Therefore, these earlier checks do not mitigate the destructive control-flow flaw. ### Attack Path 1. A user runs `python scripts/finish_branch.py --cleanup` while on a feature branch that has not been merged into `main` or `master`. 2. The script runs tests and produces a summary but does not establish that the feature branch has been merged. 3. `cleanup_branch` checks out the main branch. 4. `git branch -d <fea ...[truncated 1036 chars]
Remediation
## Remediation Suggestions - Abort immediately if safe local deletion fails: ```python rc, _, err = run_git_rc("branch", "-d", branch) if rc != 0: print(f"Cleanup aborted: {err}") return False ``` - Before any deletion, explicitly verify merge status against the intended target: ```python rc, _, _ = run_git_rc( "merge-base", "--is-ancestor", branch, main_branch ) if rc != 0: print(f"Cleanup aborted: '{branch}' is not merged into '{main_branch}'.") return False ``` - Require typed confirmation before deleting a remote branch, clearly listing the local branch, remote branch, and commits that would become unreachable remotely. - Separate local and remote cleanup into distinct command-line options so remote deletion is explicitly requested. - Perform all safety validation before executing either destructive operation. - Return a nonzero process status when cleanup fails, enabling automation to detect and stop on the unsafe state.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description suggests a workflow assistant used at the end of development to help choose among merge, PR creation, or cleanup actions. The supplied code does not provide any structured decision-making, PR support, merge assistance, or finish-branch workflow behavior. Instead, it is a standalone Git maintenance script focused on enumerating merged branches, detecting stale branches, and deleting merged branches locally and remotely when requested. Cleanup is only one part of the declared description, and the code’s actual primary purpose is repository branch cleanup, making the description materially inaccurate.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This skill is described as a finish-branch decision-support tool, but the script can perform destructive state changes by deleting local branches and attempting remote branch deletion on origin. That exceeds a read-only advisory purpose and creates a meaningful risk of accidental or unauthorized data/workflow disruption, especially if invoked by an automated agent with repository credentials.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The git push origin --delete branch operation mutates the remote repository, which is especially dangerous in the context of a finish-branch skill whose stated purpose is to help decide how to integrate work. In an agent setting, remote deletion can remove collaborators' branches, disrupt review/CI workflows, and cause irreversible loss if branches are not otherwise recoverable.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if dry_run:
        print("  [dry-run] Would run tests.")
        return True
    rc = subprocess.run(runner, shell=True).returncode
    if rc != 0:
        print(f"  Tests failed (exit {rc}). Fix before finishing the branch.")
        return False
Confidence
98% confidence
Finding
Using subprocess.run(runner, shell=True) on a command selected from repository markers creates a tool-parameter abuse path: the repository influences what executable logic is launched, and shell invocation adds further parsing risk. In this context, an agent user may expect a safe branch-finishing assistant, but the skill can instead trigger arbitrary project-defined commands with local side effects or data exfiltration.

Session Persistence

Medium
Category
Rogue Agent
Content
- Test verification before proceeding
- Base branch detection
- Four integration options: Merge locally, Push and create PR, Keep as-is, Discard
- Worktree cleanup procedures
- Quick reference table for each option's behavior
- Safety guards (typed confirmation for destructive actions)
Confidence
60% confidence
Finding
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.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases include broad natural-language terms like "done with feature" and "implementation complete," which may be said in ordinary conversation and cause unintended skill activation. Because this skill can lead to merge, PR, cleanup, or discard workflows, accidental invocation could push the agent into sensitive repository operations at the wrong time.

Rp1

Medium
Category
MCP Rug Pull
Confidence
89% confidence
Finding
The README instructs users to install the skill via `npx add` from a remote GitHub URL without pinning to a specific immutable version, tag, or commit. This creates a supply-chain risk: if the upstream repository changes or is compromised, future installs may fetch unexpected or malicious content.

Skill Enumeration

Medium
Category
Agent Snooping
Content
From your project root:

```bash
mkdir -p .claude/skills
cp -r ~/.ai-skills/skills/tools/finishing-branch .claude/skills/finishing-branch
```
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
#### Claude Code (global)

```bash
mkdir -p ~/.claude/skills
cp -r ~/.ai-skills/skills/tools/finishing-branch ~/.claude/skills/finishing-branch
```
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill includes multiple shell command sequences that can modify Git state, create PRs, and delete branches/worktrees, but it declares no explicit tool scope or permission boundary. In an agent environment, missing tool restrictions increases the chance of unintended or over-privileged execution if the skill is auto-invoked or reused in a broader context.

External Model or Provider Selection

Medium
Category
Excessive Agency
Content
---
name: finishing-branch
model: fast
description: Complete development work by presenting structured options for merge, PR, or cleanup. Use when implementation is complete, all tests pass, and you need to decide how to integrate work. Triggers on finish branch, complete branch, merge branch, create PR, done with feature, implementation complete.
---
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The metadata description embeds many broad trigger phrases such as 'merge branch', 'create PR', and 'implementation complete', which could cause the skill to activate in contexts where the user did not intend this workflow. Because the skill can lead to destructive Git operations, unintended activation materially raises the risk of accidental merges, pushes, or deletions.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The in-body keyword list repeats ambiguous activators like 'merge', 'PR', and 'done with feature' without strong scope constraints. In a skill-routing system, vague activation terms increase the likelihood this skill is selected during ordinary discussion of Git workflows, exposing users to state-changing operations they did not request.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_git(*args, check=True):
    """Run a git command and return stripped stdout."""
    cmd = ["git"] + list(args)
    result = subprocess.run(cmd, capture_output=True, text=True, check=check)
    return result.stdout.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_git_rc(*args):
    """Run a git command and return (return_code, stdout, stderr)."""
    cmd = ["git"] + list(args)
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode, result.stdout.strip(), result.stderr.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_git_rc(*args):
    """Run a git command and return (return_code, stdout, stderr)."""
    cmd = ["git"] + list(args)
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode, result.stdout.strip(), result.stderr.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_git(*args, check=True, capture=True):
    """Run a git command and return stripped stdout."""
    cmd = ["git"] + list(args)
    result = subprocess.run(
        cmd,
        capture_output=capture,
        text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
Automatically detecting and executing tests is broader than necessary for a branch-finishing advisory tool and causes code execution based on repository contents. Because the repository can define Makefile, tox, npm, or other commands with arbitrary side effects, this turns a summarization/integration helper into an execution surface for untrusted project logic.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if dry_run:
        print("  [dry-run] Would run tests.")
        return True
    rc = subprocess.run(runner, shell=True).returncode
    if rc != 0:
        print(f"  Tests failed (exit {rc}). Fix before finishing the branch.")
        return False
Confidence
96% confidence
Finding
This code executes an auto-detected test command with shell=True, which allows shell parsing of the command string and broadens the attack surface. In the skill context, merely 'finishing a branch' should not require executing repository-selected commands, and a malicious repository can influence detection so the tool runs attacker-controlled local build/test hooks or binaries.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The skill description says it should present structured options for merge, PR, or cleanup, but this implementation performs state-changing actions including branch checkout, local branch deletion, and remote deletion. In an agent setting, that mismatch is risky because invoking a decision-aid skill can unexpectedly modify repositories or delete remote branches.

Static analysis

No suspicious patterns detected.