Back to skill

Security audit

Skill Creator (Ming)

Security checks for vulnerabilities and agentic risk

Overview

This skill supports skill-building workflows, but its local review server has avoidable safety issues that can stop unrelated local processes and expose or corrupt review data.

Install only if you are comfortable with a skill that runs local Python tools, launches Claude CLI subprocesses, creates evaluation files, and opens a local review UI. Review or patch the viewer before using it on sensitive eval outputs: remove automatic port killing, prefer static output or a short-lived foreground server, avoid third-party CDN resources, and do not open generated review pages containing untrusted outputs until the script-embedding issue is fixed.

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
eval-viewer/generate_review.py:259
Finding

Stored Script Injection Through Embedded Evaluation Data

Content
View full analysis
str: """Generate the complete standalone HTML page with embedded data.""" template_path = Path(__file__).parent / "viewer.html" template = template_path.read_text() # Build previous_feedback and previous_outputs maps for the template previous_feedback: dict[str, str] = {} previous_outputs: dict[str, list[dict]] = {} if previous: for run_id, data in previous.items(): if data.get("feedback"): previous_feedback[run_id] = data["feedback"] if data.get("outputs"): previous_outputs[run_id] = data["outputs"] embedded = { "skill_name": skill_name, "runs": runs, "previous_feedback": previous_feedback, "previous_outputs": previous_outputs, } if benchmark: embedded["benchmark"] = benchmark data_json = json.dumps(embedded) return template.replace( "/*__EMBEDDED_DATA__*/", f"const EMBEDDED_DATA = {data_json};" ) ``` The resulting data is inserted into this executable script context: ```html
Remediation
View remediation
__SAFELY_ENCODED_JSON__ ``` 3. Before inserting JSON into HTML, escape characters significant to the HTML parser: ```python data_json = json.dumps(embedded, ensure_ascii=False) data_json = ( data_json .replace("&", "\\u0026") .replace("<", "\\u003c") .replace(">", "\\u003e") .replace("\u2028", "\\u2028") .replace("\u2029", "\\u2029") ) ``` 4. Prefer serving JSON through a dedicated authenticated endpoint rather than embedding all potentially sensitive outputs in the HTML. 5. Add a restrictive Content Security Policy that disallows inline and untrusted scripts. Use nonces or hashes for required local scripts. 6. Add regression tests containing ``, HTML tags, quotes, Unicode separators, and event-handler payloads in every embedded text field. 7. Treat all generated skill outputs as untrusted, even when the viewer is only bound to loopback. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
eval-viewer/generate_review.py:364
Finding

Unauthenticated and CSRF-Capable Local Feedback Overwrite

Content
View full analysis
None: if self.path == "/api/feedback": length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length) try: data = json.loads(body) if not isinstance(data, dict) or "reviews" not in data: raise ValueError("Expected JSON object with 'reviews' key") self.feedback_path.write_text(json.dumps(data, indent=2) + "\n") resp = b'{"ok":true}' self.send_response(200) except (json.JSONDecodeError, OSError, ValueError) as e: resp = json.dumps({"error": str(e)}).encode() self.send_response(500) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(resp))) self.end_headers() self.wfile.write(resp) else: self.send_error(404) ``` ### Technical Analysis The feedback endpoint performs a state-changing filesystem write without authenticating the caller. It does not validate an unpredictable session token, check the `Origin` header, enforce an expected content type, or apply a maximum request-body size. Binding the service to `127.0.0.1` prevents direct remote connections but does not prevent a webpage opened in the user's browser from sending requests to localhost. Because the handler ignores `Content-Type`, an attacker can attempt a CORS-simple request using `text/plain`, avoiding a preflight request while still providing a body that `json.loads()` accepts. The endpoint only verifies that the body is a JSON object containing a `reviews` key. It does not validate review field types, entry counts, status values, or payload size. The `Content-Length` value is trusted when reading th ...[truncated 1762 chars]
Remediation
View remediation
MAX_FEEDBACK_BYTES: self.send_error(413) return ``` 6. Validate the complete schema, including: - `reviews` must be a bounded list. - Every review must have expected string fields. - String lengths must be bounded. - `status` must be an allowed value. - Unknown top-level properties should be rejected or ignored. 7. Write updates atomically through a temporary file followed by `os.replace()`. 8. Consider using a randomly selected port instead of a predictable default, although port randomization must not replace authentication and CSRF controls. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
eval-viewer/generate_review.py:287
Finding

Viewer Startup Terminates Unrelated Processes on the Selected Port

Content
View full analysis
None: """Kill any process listening on the given port.""" try: result = subprocess.run( ["lsof", "-ti", f":{port}"], capture_output=True, text=True, timeout=5, ) for pid_str in result.stdout.strip().split("\n"): if pid_str.strip(): try: os.kill(int(pid_str.strip()), signal.SIGTERM) except (ProcessLookupError, ValueError): pass if result.stdout.strip(): time.sleep(0.5) except subprocess.TimeoutExpired: pass except FileNotFoundError: print("Note: lsof not found, cannot check if port is in use", file=sys.stderr) ``` The function is automatically invoked during normal startup: ```python # Kill any existing process on the target port port = args.port _kill_port(port) ``` ### Technical Analysis The viewer automatically uses `lsof` to identify every process listening on the selected port and sends `SIGTERM` to each returned PID. It does not verify that a process is an earlier instance of this viewer, check its executable path, inspect ownership beyond the operating system's normal signal permissions, or request user confirmation. Terminating unrelated services is not necessary for the viewer's declared functionality. The code immediately below this behavior already supports a safer approach: if binding fails, it creates a server on an automatically selected free port. Although the subprocess uses an argument array and is not vulnerable to shell injection, the process termination behavior violates least privilege by interfering with unrelated user processes ...[truncated 1375 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description centers on skill authoring, editing, optimization, and measuring performance through eval execution/benchmarking. This code does not create or modify skills, optimize descriptions, or run evals. Its primary function is post-hoc visualization and review of already-produced eval artifacts, with a simple server and feedback-saving API. While reviewing eval results is loosely adjacent to measuring skill performance, the implementation is materially narrower and different: it is a viewer for existing results, not a tool for creating/improving skills or conducting evaluations. Additionally, the code includes undeclared operational behavior such as starting a web server, opening a browser, writing feedback files, and killing processes on a port.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description focuses on authoring, editing, optimizing, and evaluating skills, including benchmarking and trigger-description optimization. The actual code does none of those primary functions. Instead, it validates a local skill directory and packages it into a .skill file for distribution. That is a materially different primary purpose and an undeclared capability. While validation could be loosely related to skill development workflows, the core behavior here is packaging/archive creation, not skill creation/modification/evaluation. Therefore this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description promises a broad skill authoring and optimization capability: creating skills, editing them, improving them, running evals, benchmarking performance, and optimizing descriptions. The actual code does none of those. It only performs static validation of SKILL.md metadata/frontmatter and exits with a pass/fail message. While validation could be a supporting utility in a larger skill-development workflow, this code chunk’s primary behavior is materially narrower and different from the declared purpose. There is no evidence of creating or modifying skills, running evals, measuring performance, benchmarking variance, or optimizing trigger descriptions.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly recommends making descriptions 'pushy' and triggering even when the user does not explicitly ask for the skill. Overbroad triggering can cause the agent to invoke a powerful workflow unnecessarily, leading to unexpected file operations, eval execution, subprocess launches, or packaging actions outside the user's actual intent.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
1. Read the template from `assets/eval_review.html`

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/improve_description.py (reported line 33)May include surrounding context.

python
# Remove CLAUDECODE env var to allow nesting claude -p inside a
    # Claude Code session. The guard is for interactive terminal conflicts;
    # programmatic subprocess usage is safe. Same pattern as run_eval.py.
    env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"}

    result = subprocess.run(
        cmd,

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/run_eval.py (reported line 83)May include surrounding context.

python
# Remove CLAUDECODE env var to allow nesting claude -p inside a
    # Claude Code session. The guard is for interactive terminal conflicts;
    # programmatic subprocess usage is safe. Same pattern as run_eval.py.
    env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"}

    result = subprocess.run(
        cmd,

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · scripts/run_eval.py (reported line 23)May include surrounding context.

python
def find_project_root() -> Path:
    """Find the project root by walking up from cwd looking for .claude/.

    Mimics how Claude Code discovers its project root, so the command file
    we create ends up where claude -p will look for it.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill clearly instructs use of shell commands, file reads/writes, environment-dependent tooling, and background processes, but it declares no explicit tool scope or permission boundaries. That creates an unnecessary expansion of authority: if invoked in a permissive environment, the skill may perform filesystem or shell operations the user did not specifically anticipate.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The skill instructs authors to "Prefer using the imperative form in instructions," which is a natural-language style constraint applied by default rather than offered as a user choice. This can amount to forcing a particular linguistic form without opt-in or justification.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The skill instructs launching a review server with nohup in the background, creating session-persistent processes that can outlive the user's immediate task. Persistent local services increase risk of unintended exposure of workspace contents, orphaned processes, and resource misuse, especially when paired with automatic browser opening and local file embedding.

Content

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

  1. Launch the viewer with both qualitative outputs and quantitative data:
    bash
    nohup python <skill-creator-path>/eval-viewer/generate_review.py \
      <workspace>/iteration-N \
      --skill-name "my-skill" \
      --benchmark <workspace>/iteration-N/benchmark.json \
    

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file begins by defining the agent as a 'Post-hoc Analyzer Agent' that explains why one skill beat another and generates improvement suggestions. Later, the document states that when analyzing benchmark results, the analyzer's purpose is instead to surface patterns and anomalies and explicitly not suggest improvements, which is an active contradiction in documented intent for the same skill file.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The mismatch between the docstring and the actual behavior hides a destructive side effect: the tool does more than generate and serve a review page, it also kills processes on the selected port. This is dangerous because operators may run it expecting read-only behavior and inadvertently disrupt unrelated local services.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script forcibly sends SIGTERM to any process listening on the requested port before starting its own server, without validating that the target process belongs to this tool or to the current session. This creates an unnecessary local denial-of-service primitive against unrelated applications such as dev servers, databases, or admin consoles.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

Terminating any process on the selected port without warning or confirmation is a destructive action that can disrupt unrelated workloads and surprises users who only intended to launch a local viewer. In this skill context, where tools may be run repeatedly during evaluations, that behavior is more dangerous because it can repeatedly interfere with other local services used during development or testing.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

The subprocess invocation itself is not shell-injection-prone because it uses a fixed argument list, but it is part of behavior that enumerates and later terminates arbitrary processes bound to a chosen port. In a local developer environment, this can kill unrelated services and cause denial of service or loss of work, especially since the port may be user-supplied and there is no ownership check.

Content

Scanner excerpt · eval-viewer/generate_review.py (reported line 291)May include surrounding context.

python
def _kill_port(port: int) -> None:
    """Kill any process listening on the given port."""
    try:
        result = subprocess.run(
            ["lsof", "-ti", f":{port}"],
            capture_output=True, text=True, timeout=5,
        )

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This viewer is intended for local review of eval artifacts, yet it loads remote Google Fonts and a CDN-hosted SheetJS library. That creates a supply-chain and privacy risk: opening the local review page causes network access to third parties, and a compromised or changed remote dependency could execute arbitrary script in the page context and exfiltrate embedded review data or feedback.

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

python
# programmatic subprocess usage is safe. Same pattern as run_eval.py.
    env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"}

    result = subprocess.run(
        cmd,
        input=prompt,
        capture_output=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script sends the full skill content, eval results, prior attempt history, and possibly logged prompts/responses to an external model subprocess without any explicit consent gate, redaction, or warning at the point of transfer. If those inputs contain secrets, proprietary prompts, user data, or sensitive evaluation corpora, this creates an unintended data exfiltration path to a third-party model service.

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

python
# programmatic subprocess usage is safe.
        env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"}

        process = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script persists evaluation queries, descriptions, and per-iteration history into HTML/JSON report files, and those fields may contain sensitive prompts, proprietary skill content, or other confidential data. Because this is done by default and without a clear warning, redaction, or opt-in, users can unintentionally create local artifacts that expose sensitive information to other local users, backups, sync tools, or later sharing.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown skill instructs the agent to save analysis results to output_path, which is a file write affecting user data or the filesystem. The document provides no warning, caution, or disclosure that the skill will create or overwrite an output file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The benchmark-analysis section instructs the agent to save notes to output_path as JSON, which is another filesystem write. The markdown does not warn users that running this skill will persist generated notes to disk or potentially replace an existing file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This code performs a file write that replaces the workspace's feedback.json contents, but the write path itself has no confirmation prompt or user-facing disclosure in the handler. Although the module docstring mentions auto-saving, the write operation is silent at runtime and request logging is suppressed, so users may not notice when persisted data is modified.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code writes benchmark.json and benchmark.md to the filesystem, which modifies user data in the target directory. Although the script logs the generated paths after writing, there is no prior warning, confirmation, or explicit disclosure near the write operation that existing files may be overwritten.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.