Back to skill

Security audit

Skill Creator (Opencode)

Security checks for vulnerabilities and agentic risk

Overview

The skill’s purpose is coherent, but its helper scripts have review-worthy risks around external agent execution, local file writes, HTML injection, packaging symlinks, and local process termination.

Install only if you are comfortable reviewing or patching the bundled scripts first. Avoid running the evaluator or packager on untrusted skills, prefer static review output over the local server, do not use occupied ports, and assume skill contents, eval prompts, outputs, and feedback may be processed by Opencode or exposed in local HTML viewers.

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

T09 · Insecure Skill Coding Practices

Error
Location
eval-viewer/generate_review.py:267
Finding

Stored JavaScript Injection in the Generated Evaluation Viewer

Content
View full analysis
// ---- Embedded data (injected by generate_review.py) ---- /*__EMBEDDED_DATA__*/ ``` ### Technical Analysis The generator recursively reads evaluation output files and includes text-file contents, prompts, grading data, and previous feedback in `embedded`. It serializes this object using `json.dumps()` and inserts the resulting JSON directly into an HTML `` can therefore terminate the enclosing script element, after which attacker-controlled HTML or JavaScript can be interpreted by the browser. The vulnerable data can originate from a Skill being evaluated. Consequently, an untrusted Skill can place the payload in an output file without requiring direct modification of the viewer. ### Attack Path 1. An attacker supplies a Skill or evaluation task that generates a text output containing a script-closing sequence and attacker-controlled markup. 2. The output is saved under an evaluation run's `outputs/` directory. 3. `find_runs()` and `embed_file()` read the output and add its contents to the `runs` structure. 4. `generate_html()` serializ ...[truncated 987 chars]
Remediation
View remediation
SAFE_JSON ``` 3. Before placing JSON into HTML, escape characters significant to the HTML parser: ```python data_json = json.dumps(embedded) data_json = ( data_json .replace("<", "\\u003c") .replace(">", "\\u003e") .replace("&", "\\u0026") .replace("\u2028", "\\u2028") .replace("\u2029", "\\u2029") ) ``` 4. Prefer a templating library with explicit HTML-context escaping rather than raw string replacement. 5. Add a restrictive Content Security Policy that disallows arbitrary inline scripts and limits network destinations. 6. Add regression tests containing ``, HTML tags, event handlers, and Unicode line separators in prompts and output files. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
assets/eval_review.html:40
Finding

HTML and JavaScript Injection in the Trigger-Evaluation Review Template

Content
View full analysis

Eval Set Review: __SKILL_NAME_PLACEHOLDER__

Current description: __SKILL_DESCRIPTION_PLACEHOLDER__

+ Add Query Export Eval Set
QueryShould TriggerActions

`. A crafted eval query can therefore terminate the script element and inject J ...[truncated 1476 chars]
Remediation
View remediation
`, `&`, U+2028, and U+2029 when serializing JSON for an HTML document. 4. Replace the manual placeholder workflow in `SKILL.md` with a dedicated generator script that performs context-aware encoding. 5. Apply a Content Security Policy that blocks arbitrary inline JavaScript and limits outbound connections. 6. Add tests using malicious names, descriptions, and queries containing HTML tags, event handlers, and script-closing sequences. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/run_eval.py:93
Finding

Command-File Path Traversal Through an Unvalidated Skill Name

Content
View full analysis
", "|", ">-", "|-"): continuation_lines: list[str] = [] i += 1 while i < len(frontmatter_lines) and ( frontmatter_lines[i].startswith(" ") or frontmatter_lines[i].startswith("\t") ): continuation_lines.append(frontmatter_lines[i].strip()) i += 1 description = " ".join(continuation_lines) continue else: description = value.strip('"').strip("'") i += 1 return name, description, content ``` The unvalidated value is then incorporated into a filesystem path: ```python unique_id = uuid.uuid4().hex[:8] clean_name = f"{skill_name}-skill-{unique_id}" project_commands_dir = Path(project_root) / ".opencode" / "commands" command_file = project_commands_dir / f"{clean_name}.md" try: project_commands_dir.mkdir(parents=True, exist_ok=True) indented_desc = "\n ".join(skill_description.split("\n")) command_content = ( f"---\n" f"description: |\n" f" {indented_desc}\n" f"---\n\n" f"# {skill_name}\n\n" f"This skill handles: {skill_description}\n" ) command_file.write_text(command_content) ``` Cleanup uses the same attacker-influenced path: ```python finally: if command_ ...[truncated 2123 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/package_skill.py:91
Finding

Skill Packaging Follows Symlinks to Files Outside the Skill Directory

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

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

Evaluation Viewer Unconditionally Terminates Processes Using 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 ) ``` It is invoked unconditionally before binding the viewer: ```python # Kill any existing process on the target port port = args.port _kill_port(port) handler = partial( ReviewHandler, workspace, skill_name, feedback_path, previous, benchmark_path ) try: server = HTTPServer(("127.0.0.1", port), handler) except OSError: # Port still in use after kill attempt — find a free one server = HTTPServer(("127.0.0.1", 0), handler) port = server.server_address[1] ``` ### Technical Analysis The viewer only needs an available local port. Instead of attempting to bind and choosing a different port when the requested one is occupied, `_kill_port()` identifies all listeners on that port and sends them `SIGTERM`. The function does not verify that a discovered process belongs to an earlier instance of this viewer. It also does not request confirmation or restrict termination to a PID previously recorded by the application. An exp ...[truncated 1141 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 (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description focuses on skill creation, editing, optimization, evaluation, benchmarking, and trigger-description tuning. The actual code chunk does not implement those functions. Instead, its primary purpose is packaging an existing skill directory into a .skill file after validating that the folder exists and contains SKILL.md. This is a materially different capability from the declared purpose. There are no dangerous extra permissions shown, but the behavior is clearly mismatched because packaging/distribution is not represented in the description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The code chunk’s actual function is narrowly focused on validating SKILL.md metadata structure and content rules. That is materially different from the declared purpose, which describes a broad skill authoring, editing, optimization, and evaluation system using Opencode. There is no evidence of creating or editing skills, invoking Opencode, running tests/evals, benchmarking, or trigger optimization. This is not just an implementation detail of the declared tool; it is a different primary capability altogether. Therefore the description does not accurately represent the supplied code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Yes, this is a mismatch based on the supplied code chunk. The declared purpose presents a full-featured skill-creator/editor/evaluation tool, but the actual code shown is only a narrow helper utility for parsing SKILL.md frontmatter. While such parsing could support a larger skill-creation system, this chunk by itself does not implement the primary declared capabilities such as creating skills, modifying them, running evaluations, benchmarking performance, or optimizing descriptions. This is not merely a supporting-detail difference in the other direction; the described functionality is much broader than the observed behavior in the provided code.

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 379)May include surrounding context.

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

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill instructs the agent to read and write files and execute shell commands, but it does not declare any tool scope such as permissions or allowed-tools. This increases blast radius because a caller cannot easily constrain what the skill may access or execute, and the skill includes many filesystem and subprocess operations.

Content

No source excerpt is available for this finding.

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 · SKILL.md (reported line 3)May include surrounding context.

md
---
name: skill-creator
description: Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (Modified to use Opencode instead of Claude Code CLI - auto-detects opencode location)
---

## Requirements

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly advises making descriptions 'pushy' and triggering on broad phrases even when the user does not explicitly ask for the skill. Over-broad trigger guidance can cause inappropriate activation of a high-capability skill that performs shell commands, file writes, benchmarking, packaging, and background processes, increasing the chance of unintended side effects.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

The skill instructs launching a long-running background process with nohup and storing the PID for later termination. Background persistence can outlive the interactive task, consume resources, expose local review data over a server, and is risky in shared or headless environments if lifecycle management fails.

Content

Scanner excerpt · SKILL.md (reported line 253)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 \
    

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Before starting its local viewer, the script unconditionally enumerates and sends SIGTERM to any process listening on the requested port, without verifying ownership or relation to this tool. That can terminate unrelated local services or developer processes, causing denial of service or disrupting other workflows; in a skill-creation/eval environment, users may run many local tools, so this behavior is more dangerous than in an isolated single-purpose utility.

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

The viewer imports Google Fonts and a third-party SheetJS bundle from external origins despite being described as a local eval review tool. This creates unnecessary outbound requests that leak usage metadata and introduces a supply-chain/trust dependency; if the CDN content is unavailable or compromised, the page behavior and potentially the confidentiality of reviewed data could be affected.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The page makes network requests to third-party domains during load without any visible notice or consent, which conflicts with the apparent expectation of an offline/local review interface. Even if only fonts/scripts are fetched, this silently exposes reviewer IP, timing, and environment metadata and may violate privacy or air-gapped review assumptions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Reviewer feedback is auto-saved via POST requests to /api/feedback after typing, but the UI does not clearly disclose that content is transmitted to a server during the session. Users may assume feedback remains local until explicit submission, so this can cause unintended disclosure of potentially sensitive review notes and prompts.

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 44)May include surrounding context.

python
# Try to find in PATH
    try:
        result = subprocess.run(
            ["which", "opencode"],
            capture_output=True,
            text=True,

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 49)May include surrounding context.

python
# Try to find in PATH
    try:
        result = subprocess.run(
            ["which", "opencode"],
            capture_output=True,
            text=True,

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 73)May include surrounding context.

python
cmd.extend(["--model", model])
    cmd.append(prompt)

    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The function builds a prompt containing full skill content, evaluation results, user queries, prior attempts, and notes, then transmits all of it to an external CLI/model. If those inputs contain proprietary prompts, internal logic, secrets, or sensitive evaluation data, this code can exfiltrate them to a third-party model backend without explicit consent, minimization, or redaction. In the context of a skill-creation tool, this is more dangerous because it may process unpublished skill contents and benchmark data that are often sensitive by nature.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
91% confidence
Finding

This code executes an external opencode binary discovered from common paths or PATH, then passes attacker-influenced inputs such as query text and optional model selection into that process. While shell=False avoids classic shell injection, the larger risk is execution of an untrusted or trojaned opencode binary, plus unintended side effects from invoking a powerful external tool on untrusted content during evaluation.

Content

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

python
if model:
            cmd.extend(["--model", model])

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

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

文件标题明确标注为“中文版”,全文内容也仅提供中文说明,但未说明这是可选语言版本、未链接其他语言版本,也未声明该技能仅适用于中文用户。按照语言/locale 政策,强制单一语言而没有用户选择或明确、合理的区域限定,属于自然语言层面的政策风险。

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

This markdown skill instructs the agent to save structured analysis to a user-provided path, which is a file write affecting user data. The document does not include any warning or disclosure that the skill will create or overwrite files at the specified output path.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The docstring for improve_description says 'Call Claude to improve the description', but the implementation routes requests through _call_claude, which actually runs opencode run as a subprocess. This is reinforced by prompt text and comments elsewhere that describe prior Claude-specific behavior, creating active documentation drift about what external system is being invoked.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The safety-net comment describes the 'old SDK path' and specifically references 'claude -p is one-shot', but the current code performs a second _call_claude invocation that runs opencode run. This comment is not merely incomplete; it describes a different tool path than the one actually used in the file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

This Python file launches an external process via subprocess.Popen to run opencode, which is a safety-relevant operation under the code-file warning criteria. Although the file has internal docstrings, it does not provide any user-facing confirmation, prompt, or visible disclosure at the point of execution, and the command can process raw queries against the local project context.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest describes capabilities around skill creation, editing, evaluation, benchmarking, and optimization. In this file, the code imports browser-control functionality and later uses it to automatically open a live report, which is a user-environment side effect unrelated to the core skill-authoring and evaluation purpose.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script automatically opens a locally generated HTML report in the user's default browser without requiring explicit consent at runtime. While the report content appears locally generated, launching external applications is an environment-interacting side effect that can surprise users, leak activity context to the desktop/session, or become riskier if report content is ever influenced by untrusted data.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.