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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
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.

Content

Scanner excerpt · scripts/finish_branch.py (reported line 97)May include surrounding context.

python
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

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · README.md (reported line 9)May include surrounding context.

md
- 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)

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Skill Enumeration

Medium
Category
Agent Snooping
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.

Content

Scanner excerpt · README.md (reported line 50)May include surrounding context.

From your project root:

bash
mkdir -p .claude/skills
cp -r ~/.ai-skills/skills/tools/finishing-branch .claude/skills/finishing-branch

Skill Enumeration

Medium
Category
Agent Snooping
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.

Content

Scanner excerpt · README.md (reported line 57)May include surrounding context.

Claude Code (global)

bash
mkdir -p ~/.claude/skills
cp -r ~/.ai-skills/skills/tools/finishing-branch ~/.claude/skills/finishing-branch

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

External Model or Provider Selection

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
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.
---

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/cleanup_branches.py (reported line 20)May include surrounding context.

python
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()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/cleanup_branches.py (reported line 27)May include surrounding context.

python
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()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/finish_branch.py (reported line 34)May include surrounding context.

python
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()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/finish_branch.py (reported line 22)May include surrounding context.

python
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,

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · scripts/finish_branch.py (reported line 97)May include surrounding context.

python
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

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.