Back to skill

Security audit

huditest

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly a legitimate skill-building and evaluation helper, but it should be reviewed because its eval viewer has unsafe local side effects and browser-data exposure risks.

Install only if you are comfortable with a developer-oriented skill that writes evaluation workspaces, invokes the local Claude CLI, opens local HTML reports, and may expose eval outputs in a browser. Before using the eval viewer, avoid running it on a port used by important local services, prefer static/offline review when possible, and treat eval outputs from untrusted skills as unsafe until the HTML injection issue is fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

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

Stored script injection in generated evaluation review pages

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 serialized value is inserted directly into an executable script element: ```html
Remediation
View remediation
``` 3. Serialize the JSON while escaping characters significant to the HTML parser, including `<`, `>`, `&`, U+2028, and U+2029. At minimum, replace `<` with `\u003c` before insertion. 4. Retrieve and parse the value using: ```javascript const EMBEDDED_DATA = JSON.parse( document.getElementById("embedded-data").textContent ); ``` 5. Add a restrictive Content Security Policy that prevents inline and arbitrary third-party scripts. 6. Add regression tests using payloads containing ``, HTML tags, Unicode separators, and malicious values in prompts, filenames, grading evidence, and text outputs. 7. Treat every field originating from an evaluated skill or workspace as untrusted, even when it is serialized as JSON. ]]>

T05 · Unauthorized Access and Privilege Escalation

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

Evaluation viewer terminates unrelated processes occupying its requested 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 invoked unconditionally before binding the server: ```python # Kill any existing process on the target port port = args.port _kill_port(port) ``` ### Technical Analysis The viewer discovers every process listening on the selected port and sends each process `SIGTERM`. It does not verify that a target process is: - A previous instance of this evaluation viewer. - Owned or started by the current workflow. - Safe to terminate. - Associated with a PID previously recorded by the Skill. Port ownership is not an authorization boundary. An unrelated development server, local database, proxy, test harness, or other application may legitimately occupy the selected port. The existing fallback behavior already demonstrates a safer design: if binding fails, the code can bind to port `0` and let the operating system select an available port. Consequently, terminating an existing listener exceeds the minimum privileges necessary to provide the viewer. ### Attack Path ...[truncated 985 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/init_skill.py:206
Finding

Unvalidated skill names permit path traversal during skill initialization

Content
View full analysis
Remediation
View remediation
40: raise ValueError("Skill name must be no more than 40 characters") ``` 2. Explicitly reject absolute paths, path separators, `.` components, and `..` components. 3. Resolve both the base and destination and verify containment: ```python base = Path(path).resolve() destination = (base / skill_name).resolve() if not destination.is_relative_to(base): raise ValueError("Skill path escapes the requested output directory") ``` 4. Perform validation inside `init_skill()` rather than only in CLI-facing code so programmatic callers receive the same protection. 5. Create files with restrictive default permissions where appropriate. 6. Add tests for absolute paths, `../` traversal, embedded separators, empty names, overlong names, and symlink-based containment edge cases. ]]>

T08 · Insecure Dependencies

Warning
Location
eval-viewer/viewer.html:7
Finding

Review viewer loads executable spreadsheet-rendering code from a third-party CDN

Content
View full analysis
``` ### Technical Analysis The evaluation viewer loads and executes SheetJS from an external CDN at runtime. The page also contacts Google Fonts infrastructure. This means opening a local review page is not fully local or dependency-free. The SheetJS URL is version-pinned and includes Subresource Integrity, which materially reduces arbitrary CDN content substitution risk. Nevertheless: - The viewer remains dependent on availability and behavior of an external origin. - Browsing metadata such as the user's IP address, request headers, and access time is disclosed to third parties. - The executable dependency runs in the same page context as embedded evaluation outputs and feedback. - Environments that weaken or bypass integrity enforcement would have a larger compromise scope. - No restrictive Content Security Policy was identified to constrain script and connection behavior. Loading remote executable code is not required for the core review workflow. The pinned JavaScript can be vendored with the project and served from the local viewer. ### Attack Path 1. The user generates and opens an evaluation review page. 2. The browser contacts `cdn.sheetjs.com`, `fonts.googleapis.com`, and `fonts.gstatic.com`. 3. The browser loads SheetJS into the review page and executes it with access to the ...[truncated 1140 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 (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description suggests a skill-development and optimization tool that creates or modifies skills and measures skill performance. This code does not create or edit skills, optimize descriptions, or directly run evals/benchmark analysis. Instead, it is a viewer/server for inspecting existing eval run artifacts and collecting review feedback. While this is adjacent to evaluation workflows, its primary purpose is materially narrower and different: presenting eval outputs for review rather than building or improving skills. It also includes undeclared operational behavior such as starting an HTTP server, opening a browser, writing feedback files, and terminating processes on a port.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code chunk implements only one narrow subset of the declared description: initializing a new skill from a template. That partially supports the 'create a skill from scratch' claim, but the declared purpose prominently includes additional capabilities—editing existing skills, optimization, evals, benchmarking, and trigger-description tuning—that are absent from the code shown. There is no logic for loading or changing existing skills, no evaluation harness, no metrics collection, no variance/statistical analysis, and no description-optimization behavior. Therefore the description materially overstates what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description focuses on authoring, modifying, optimizing, and evaluating skills. However, the actual code does not create or edit skills, run evals, benchmark performance, analyze variance, or optimize descriptions. Its primary purpose is materially different: it validates an existing skill directory and packages it into a distributable .skill file. That packaging capability is not represented in the declared purpose, so this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description promises skill creation, editing, optimization, evaluation, and benchmarking functionality. The supplied code does none of those things; it only performs static validation of SKILL.md frontmatter and naming/content constraints. This is a materially different primary purpose, not just a supporting implementation detail, because the code lacks any creation, modification, eval, benchmarking, or optimization behavior.

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`

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The function unconditionally terminates any process listening on the selected port, regardless of ownership or relationship to the viewer. That creates an unauthorized local denial-of-service condition against unrelated applications and is especially risky in a skill that may be run by developers on active workstations.

Content

No source excerpt is available for this finding.

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,

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · scripts/init_skill.py (reported line 266)May include surrounding context.

python
# Print next steps
    print(f"\n✅ Skill '{skill_name}' initialized successfully at {skill_dir}")
    print("\nNext steps:")
    print("1. Edit SKILL.md to complete the TODO items and update the description")
    print("2. Customize or delete the example files in scripts/, references/, and assets/")
    print("3. Run the validator when ready to check the skill structure")

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
87% confidence
Finding

The skill clearly directs use of shell, file I/O, environment access, and potentially networked tooling, but it declares no explicit tool scope or allowlist. That increases the blast radius if the skill is triggered unexpectedly or modified, because the executing agent may exercise more capabilities than the user would infer from metadata alone.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The instruction to make descriptions 'pushy' and to include broad trigger contexts encourages overbroad activation. In practice, that can cause the skill to trigger on loosely related requests and perform high-capability actions such as file operations, shell execution, eval runs, or packaging when a simpler response would have sufficed.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

Using nohup to launch a background review server creates session persistence beyond the immediate interaction and can leave long-running processes behind. In a shared or remote environment, that may expose workspace contents via a local service longer than intended, consume resources, or complicate cleanup if the PID is lost or the process outlives the session.

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
88% confidence
Finding

The documentation describes a benign review-page generator/server but omits the fact that the script may terminate unrelated local processes. This mismatch increases the likelihood that users will run the tool without understanding its destructive side effects, which materially raises operational risk.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
90% confidence
Finding

The subprocess invocation itself is not shell-injection prone because it uses an argument list and the port is parsed as an integer, but it is part of destructive behavior: probing for PIDs on a port in order to terminate them. In this skill context, a local eval viewer should not kill unrelated user processes, so the call contributes to unsafe system-side effects beyond its stated purpose.

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

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The main flow invokes the port-killing routine automatically before starting the HTTP server and gives the user no warning or confirmation. This creates a surprising destructive action that can interrupt local services, tests, or development workflows without consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The viewer converts untrusted spreadsheet content into HTML with XLSX.utils.sheet_to_html(...) and inserts it via wrapper.innerHTML = htmlStr. If the spreadsheet contains active HTML or dangerous constructs that survive SheetJS conversion, this can lead to script execution or markup injection in the reviewer’s browser. The absence of a warning is not the core issue; the dangerous part is rendering attacker-controlled spreadsheet-derived HTML directly into the DOM.

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
90% confidence
Finding

The function transmits full skill content, current descriptions, eval queries, and prior attempt history to an external claude subprocess, which is an external model/tool boundary. Because this code path has no explicit user-facing consent, warning, or data-minimization step at the call site, sensitive prompts, proprietary skill contents, or test data could be unintentionally exfiltrated outside the local process boundary.

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

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

This markdown skill instructs the agent to save structured analysis to an output path, which is a file-modifying action. The description does not provide any warning or disclosure that running the skill will write to the filesystem or may overwrite an existing file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The benchmark-analysis section directs the agent to save notes as a JSON array to output_path, which modifies user files. The markdown does not warn users that the skill performs file writes or clarify overwrite behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This markdown skill directs the agent to save results to {outputs_dir}/../grading.json, which is a file write that can affect user data or the workspace. The description does not include any warning that the skill will create or overwrite this file, so users are not explicitly informed of this behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

This code creates and writes a markdown command file under .claude/commands containing the supplied skill description, which is a file write affecting the user's workspace. Although the docstring explains the behavior for developers, there is no user-facing confirmation, prompt, or visible disclosure at the point of execution that a file will be created and later removed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

This code creates an HTML file and immediately opens it in the user's default browser. Although the behavior is visible in code, there is no runtime disclosure, confirmation prompt, or warning to the user that local files will be written and a browser will be launched automatically.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.