Back to skill

Security audit

Improvement Evaluator

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a legitimate skill evaluator, but it runs local Claude and pytest-based evaluations on skill/task content without clear sandbox or permission boundaries.

Install only if you will run it in a contained evaluation environment. Use mock mode or a locked-down Claude CLI where possible, remove unrelated secrets from the environment, restrict filesystem and network access, and treat task suites and candidate SKILL.md content as untrusted inputs.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/task_runner.py:106
Finding

Untrusted Skill Instructions Execute in an Unrestricted Agent Session

Content
View full analysis

Vulnerability Details

File Location: scripts/task_runner.py:106-112, 122-130
Vulnerability Type: Untrusted instruction execution without an enforced sandbox
Risk Level: High

Vulnerable Code

python
def _build_prompt(self, skill_content: str, task: dict) -> str:
    """Prepend SKILL.md content to the task prompt."""
    return (
        f"You have been given the following skill documentation to guide your work:\n\n"
        f"---BEGIN SKILL.MD---\n{skill_content}\n---END SKILL.MD---\n\n"
        f"Task: {task['prompt']}"
    )
python
cmd = ["claude", "-p", "--output-format", "json"]
if self.model:
    cmd.extend(["--model", self.model])
result = subprocess.run(
    cmd,
    input=prompt,
    capture_output=True,
    text=True,
    timeout=timeout,
)

Technical Analysis

Candidate Skill content and task-suite prompts are evaluation inputs that may be attacker-controlled. The evaluator inserts both directly into an instruction prompt and launches claude -p without explicitly disabling tools, restricting network access, isolating the filesystem, removing credentials, or enforcing a sandbox.

The BEGIN SKILL.MD and END SKILL.MD markers are only textual delimiters; they do not create a security boundary. A malicious candidate can place instructions in its Skill content that direct the agent to perform actions unrelated to evaluation. The actual actions available depend on the host's Claude CLI configuration and permissions, but this code does not independently enforce least privilege.

The subprocess call uses an argument list rather than a shell command, so this is not conventional shell injection. The vulnerability is instruction-driven use of ambient agent capabilities.

Attack Path

  1. An attacker places malicious instructions in a candidate's skill_content, content, or body field.
  2. Pipeline evaluation selects that candidate and passes its content to TaskRunner.run().
  3. _build_prompt() presents the c ...[truncated 1001 chars]
Remediation
View remediation

Remediation Suggestions

  1. Run each candidate evaluation in an isolated container or sandbox using a dedicated unprivileged account.
  2. Disable agent tools for text-only evaluation tasks. If tools are indispensable, use a minimal allowlist and deny arbitrary shell, filesystem, and network access.
  3. Remove API keys, tokens, SSH agents, cloud credentials, and unrelated environment variables from the evaluation process.
  4. Use a read-only root filesystem, a fresh writable temporary directory, and explicit CPU, memory, process, and execution-time limits.
  5. Disable outbound network access unless a task explicitly requires it; then allowlist only required destinations.
  6. Treat candidate Skill content as hostile data rather than trusted operational instructions. Use a purpose-built evaluation harness with a fixed higher-priority policy that prohibits side effects.
  7. Add adversarial tests containing instructions to read files, invoke tools, access the network, or override the task, and verify that the sandbox blocks every attempted side effect.
  8. Record attempted tool calls and fail the evaluation if a text-only task tries to perform external actions.

T01 · Skill Instruction Hijacking

Warning
Location
interfaces/judges.py:138
Finding

LLM Rubric Judge Is Vulnerable to Evaluation Prompt Injection

Content
View full analysis

Vulnerability Details

File Location: interfaces/judges.py:138-156
Vulnerability Type: Candidate output is interpreted in the judge's instruction context
Risk Level: Medium

Vulnerable Code

python
prompt = (
    f"You are a judge evaluating AI output quality.\n\n"
    f"## Rubric\n{rubric}\n\n"
    f"## AI Output\n{output[:3000]}\n\n"
    f"Score the output from 0.0 to 1.0 based on the rubric.\n"
    f"Respond with ONLY a JSON object: {{\"score\": <float>, \"reasoning\": \"<str>\"}}"
)
try:
    result = subprocess.run(
        ["claude", "-p", "--output-format", "json"],
        input=prompt,
        capture_output=True,
        text=True,
        timeout=120,
    )

Technical Analysis

The output being evaluated is candidate-influenced and therefore untrusted. It is interpolated directly into the same natural-language prompt that instructs the judge. No reliable security boundary separates the rubric and judge instructions from the candidate output.

A malicious candidate can induce the task-running model to return text that addresses the judge directly, such as instructions to disregard the rubric and emit a perfect score. Because the judge is itself an LLM, textual headings and output truncation do not guarantee that it will treat the content solely as data.

The rubric is also loaded from the task suite and inserted as instruction text. If task suites can come from untrusted sources, they provide another instruction-injection channel. Parsing the final response as JSON only validates its syntax; it does not establish that the score was derived honestly from the rubric.

Attack Path

  1. An attacker submits a candidate Skill that directs the task-running model to include judge-targeted instructions in its answer.
  2. TaskRunner stores that answer as raw_output.
  3. LLMRubricJudge.evaluate() interpolates the hostile output into the judge prompt.
  4. The second LLM treats some or all of the hostile output as instructions a ...[truncated 805 chars]
Remediation
View remediation

Remediation Suggestions

  1. Do not use a single unconstrained LLM score as the sole acceptance control for security-sensitive or promotion decisions.
  2. Prefer deterministic validators for properties that can be checked mechanically, including schemas, prohibited terms, required sections, parsing, and output structure.
  3. Use a separate, tightly constrained judging interface with structured fields for the rubric and candidate output rather than concatenating both into one instruction string.
  4. Add a fixed higher-priority judging policy stating that content inside the candidate-output field is untrusted data and that any embedded instructions must be ignored. This reduces risk but should not be treated as a complete security boundary.
  5. Detect common judge-manipulation phrases and either reject the result or route it for independent review.
  6. Use multiple independent judges or models and require agreement for consequential decisions.
  7. Constrain scores to the documented range and validate the complete response against a strict JSON schema.
  8. Add adversarial tests in which candidate output requests a perfect score, attempts to replace the rubric, or impersonates system instructions.
  9. Only accept task suites from trusted, reviewed locations, or authenticate and integrity-check them before execution.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · tests/test_judges.py (reported line 110)May include surrounding context.

python
judge = PytestJudge()
        result = judge.evaluate(
            "some output",
            {"judge": {"test_file": "../../../etc/passwd"}},
        )
        assert result["passed"] is False
        assert "SECURITY" in result["details"]

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill instructs use of a Python evaluation script, references reading task suites and ranking input, writing state/output artifacts, and potentially invoking external tooling such as the Claude CLI, but it does not declare any explicit tool scope or permissions. This creates an overbroad execution surface where an agent may grant file and shell access implicitly, increasing the risk of unintended file modification, command execution, or evaluation of attacker-controlled inputs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest mixes English with Chinese-only description and trigger phrases such as '当需要验证…' and '执行效果评估' without stating that the skill is region-specific or that language can be chosen by the user. This creates a natural-language policy issue because the skill appears to impose a locale/language choice rather than offering opt-in or a documented justification.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
92% confidence
Finding

PytestJudge executes pytest against a task-selected test file, which means arbitrary Python code contained in repository test fixtures will run as a subprocess. Although shell injection is avoided and path traversal is checked, this still creates a code-execution boundary where untrusted or tampered fixture tests can execute with the agent's environment inherited, including sensitive variables from os.environ.

Content

Scanner excerpt · interfaces/judges.py (reported line 85)May include surrounding context.

python
output_file = Path(tmpdir) / "ai_output.txt"
            output_file.write_text(output, encoding="utf-8")

            result = subprocess.run(
                ["python3", "-m", "pytest", str(test_path), "-v",
                 "--tb=short", f"--rootdir={tmpdir}"],
                capture_output=True,

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · interfaces/judges.py (reported line 92)May include surrounding context.

python
text=True,
                timeout=60,
                env={
                    **__import__("os").environ,
                    "AI_OUTPUT_FILE": str(output_file),
                },
            )

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This skill advertises task-suite pass/fail evaluation, but LLMRubricJudge adds a secondary model-execution pathway through the claude CLI. That expands capability beyond simple judging, increases attack surface for prompt-injection and secret disclosure, and may violate operator expectations about what the skill is allowed to execute.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

LLMRubricJudge launches an external CLI model process and sends it task rubric and AI output, creating a data-exfiltration and capability-expansion risk. If evaluated outputs contain secrets or attacker-crafted prompt-injection content, they are forwarded to another model/tool outside the immediate evaluator boundary, which is more dangerous in a skill meant for deterministic execution assessment.

Content

Scanner excerpt · interfaces/judges.py (reported line 151)May include surrounding context.

python
f"Respond with ONLY a JSON object: {{\"score\": <float>, \"reasoning\": \"<str>\"}}"
        )
        try:
            result = subprocess.run(
                ["claude", "-p", "--output-format", "json"],
                input=prompt,
                capture_output=True,

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest describes an evaluation skill that validates improvements using predefined YAML tasks and computes execution_pass_rate. In this file, the core execution path invokes the external claude binary via subprocess.run, which introduces host-level process execution capability beyond ordinary scoring logic and is not declared in the stated purpose.

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

python
cmd = ["claude", "-p", "--output-format", "json"]
            if self.model:
                cmd.extend(["--model", self.model])
            result = subprocess.run(
                cmd,
                input=prompt,
                capture_output=True,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The prompt explicitly requires release notes to be generated "in Chinese" and similar locale-forcing language appears elsewhere in the file. This is a natural-language policy concern because it imposes a specific language/locale without offering the user a choice or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instruction "Generate in Chinese" enforces a language requirement with no opt-in or alternative locale path. Under the policy rule, this is a locale/language constraint that should either be optional or clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This task says "Output in Chinese markdown," again mandating a specific language without user selection. Repeated language forcing across tasks indicates a policy-level issue rather than an incidental translation example.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This YAML manifest describes the skill broadly as evaluating a skill that removes AI patterns from text, but it does not specify any narrow activation phrases, scope limits, or exclusion conditions. For manifest files, broad natural-language descriptions like this can overlap with common editing or rewriting requests and may cause unintended invocation when a user merely asks for help revising text.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file includes a dedicated task that requires Chinese output and evaluates Chinese-specific stylistic patterns, but the manifest provides no indication that this locale-specific behavior is optional or limited to a region-specific use case. That creates a language/locale policy concern because the skill enforces a specific language mode without documented user choice or justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code writes evaluation artifacts to disk in standalone mode via write_json, affecting the user's filesystem. Although the script purpose implies evaluation output, there is no confirmation prompt or nearby user-facing warning/comment at the write site beyond generic CLI argument names, so the file-write side effect is not explicitly disclosed in-code at the operation point.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

When baseline caching is enabled, the script writes cached evaluation data to the user-specified cache directory. The code does not provide a clear user-facing notice at the write point that cache files containing results will be created and retained.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The script persists an evaluation artifact JSON to the resolved output path, which is a filesystem-modifying action. While expected for an evaluator, the operation lacks a dedicated warning, confirmation, or explicit log statement immediately describing that a file will be written to disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file includes user-facing release notes in two languages, with lines L08-L09 switching to Chinese while the rest of the document is in English. This can violate a language/locale policy when the skill presents mixed-language output without user opt-in or an explicit explanation of the intended audience.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

This YAML task suite is a manifest-type file, so vague-trigger checks apply. The prompt asks to "generate release notes" for provided commit data, but the file gives no explicit trigger scope, activation constraints, or negative examples indicating when the changelog skill should or should not be invoked; this is broad enough to overlap with common changelog/help requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The manifest repeatedly uses generic instructions like "Generate iOS platform release notes" and "Generate in Chinese" without any accompanying activation boundary or exclusion criteria. In a manifest file, this can indicate overly broad trigger semantics because the skill's use conditions are not clearly distinguished from ordinary requests to write or summarize release notes.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.