Back to skill

Security audit

Execution Verifier

Security checks for vulnerabilities and agentic risk

Overview

The skill’s basic progress checker is reasonable, but its closed-loop mode can automatically run powerful commands without enough safeguards.

Review this skill before installing. The passive verifier is relatively narrow, but the closed-loop mode should only be used if you intentionally want a scheduled process that can automatically run work commands. Avoid --force, do not pass untrusted text into --verify-cmd or --execute-cmd, and restrict any executor to a fixed, low-privilege command you are comfortable running repeatedly.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/verify_execute_verify.py:9
Finding

Arbitrary Shell Command Execution with Fail-Open Triggering

Content
View full analysis

Vulnerability Details

File Location: scripts/verify_execute_verify.py:9-10, 24-25, 31-38
Vulnerability Type: OS command injection and unsafe failure handling
Risk Level: High

Vulnerable Code

python
def run(cmd: str):
    p = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return {
        "cmd": cmd,
        "code": p.returncode,
        "stdout": p.stdout.strip(),
        "stderr": p.stderr.strip(),
    }
python
ap.add_argument("--verify-cmd", required=True, help="command that outputs verifier JSON")
ap.add_argument("--execute-cmd", required=True, help="executor command to run when no progress")
python
before_raw = run(args.verify_cmd)
before = parse_json(before_raw["stdout"]) or {"progress_detected": False, "parse_error": True}

triggered = False
exec_raw = None

if not before.get("progress_detected", False):
    triggered = True
    exec_raw = run(args.execute_cmd)
    time.sleep(args.sleep_sec)

Technical Analysis

The script accepts --verify-cmd and --execute-cmd as command strings and passes both directly to subprocess.run() with shell=True. Consequently, shell metacharacters, command separators, redirections, pipelines, and command substitutions embedded in either argument are interpreted by the operating-system shell.

The execution decision also fails open. If the verifier produces malformed JSON, no output, or output that cannot be decoded, parse_json() returns None. The fallback object sets progress_detected to False, which automatically enters the executor branch. The script does not require the verifier process to return a successful exit status before trusting its output, and the final result reports "ok": true regardless of subprocess failures.

This is exploitable when an untrusted user, automation component, configuration source, or agent-generated value can influence either command argument. Direct command-line access already permits intentional command exe ...[truncated 2100 chars]

Remediation
View remediation

Remediation Suggestions

  1. Eliminate shell interpretation. Accept commands as argument arrays and invoke them with shell=False:

    python
    def run(argv: list[str]):
        p = subprocess.run(
            argv,
            shell=False,
            capture_output=True,
            text=True,
            check=False,
        )
    
  2. Prefer dedicated structured options. Instead of accepting an unrestricted verifier command, call the known verifier script directly and expose only validated parameters such as project directory and time window.

  3. Use an allowlist for executor operations. If multiple executors must be supported, map fixed identifiers to predefined argument arrays. Do not concatenate user-controlled values into command strings.

  4. Fail closed on verifier errors. Abort without invoking the executor if:

    • The verifier returns a nonzero exit code.
    • Standard output is empty or malformed.
    • The decoded value is not a JSON object.
    • progress_detected is absent or is not a Boolean.
  5. Return accurate status information. Set the top-level ok field to false when verification, execution, parsing, or re-verification fails.

  6. Apply execution safeguards. Add subprocess timeouts, restrict the working directory and environment, run under a least-privileged service account, and avoid exposing unnecessary secrets to child processes.

  7. If compatibility requires parsing a trusted command string, parse it once into an argument list with shlex.split() and still use shell=False. This is not a substitute for validation when the command source is untrusted.

Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The skill claims to enforce a safe progress-verification workflow, but its documented behavior includes executing arbitrary shell commands supplied as arguments and relying on external command strings rather than independently enforcing the described checks. This mismatch is dangerous because operators may trust the skill as a passive verifier while it can actually trigger unreviewed command execution, increasing the chance of unintended or unsafe actions.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is a classic tool-parameter abuse issue: operational behavior is delegated to externally supplied command strings, which are then executed by a shell. The skill context makes this more dangerous, not less, because the purpose of the script is to trigger execution automatically when 'no progress' is detected, reducing human scrutiny and increasing the chance of unsafe or attacker-influenced command execution in an automated loop.

Content

Scanner excerpt · scripts/verify_execute_verify.py (reported line 10)May include surrounding context.

python
def run(cmd: str):
    p = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return {
        "cmd": cmd,
        "code": p.returncode,

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The strict report format requires responses using Chinese labels and wording such as 已完成, 进行中, and 下一步+ETA. This imposes a specific language on users without opt-in or explanation, which is a natural-language policy violation under the stated rules.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
90% confidence
Finding

The skill explicitly promotes autonomous decision-making through an auto-execute path that decides to run commands based on verifier output. In a security-sensitive context, unattended execution increases risk because it reduces human review and can amplify mistakes, especially when paired with shell commands and forceful task triggering.

Content

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

--window-min 30

text

## Closed-loop mode (verify → auto-execute → re-verify)

Use built-in script:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The closed-loop mode automatically triggers an execution command when progress is not detected, without any prominent warning, confirmation, or constraint on what may be executed. This is dangerous because a user or downstream agent may invoke what appears to be a verification routine and unexpectedly cause real system changes or privileged task execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documented executor example uses a force flag, which implies bypassing normal safeguards, yet the skill provides no warning about the operational or security consequences. In a verification-themed skill, this is especially risky because it normalizes forced execution in response to missing progress, potentially causing unsafe or irreversible actions under misleadingly routine conditions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The tool takes shell commands directly from CLI arguments and executes them without any confirmation, safety prompt, or validation. This creates a command-injection and misuse surface where a caller may accidentally or maliciously supply destructive commands, and the script will execute them as part of its normal workflow.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

The script executes arbitrary strings from the CLI via subprocess.run(..., shell=True). Because both --verify-cmd and --execute-cmd are attacker-controllable inputs at invocation time, shell metacharacters, command chaining, and environment expansion can lead to unintended command execution beyond the apparent command name. In a skill meant to orchestrate other commands, this is especially dangerous because the behavior is normalized and likely to be used with high-trust automation contexts.

Content

Scanner excerpt · scripts/verify_execute_verify.py (reported line 10)May include surrounding context.

python
def run(cmd: str):
    p = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return {
        "cmd": cmd,
        "code": p.returncode,

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/verify_progress.py (reported line 35)May include surrounding context.

python
def last_commit_age_min(repo_dir: Path):
    try:
        ts = subprocess.check_output(
            ["git", "-C", str(repo_dir), "log", "-1", "--format=%ct"],
            text=True,
            stderr=subprocess.DEVNULL,

Static analysis

No suspicious patterns detected.