Back to skill

Security audit

Skill Eval Preflight

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly a coherent eval-preflight helper, but its bundled scripts can write comparison or run artifacts outside the intended skill folder if given a crafted run-group path.

Install only if you trust the skill author and use simple run-group names such as letters, digits, underscores, dots, or hyphens. Avoid passing user-supplied or path-like run-group values until the scripts validate and contain output paths under evals/runs.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run_eval.py:34
Finding
Path Traversal Enables Artifact Creation Outside the Intended Run Directory## Vulnerability Details **File Location**: `scripts/run_eval.py:34-38` **Vulnerability Type**: Unsanitized path construction and path traversal **Risk Level**: Medium ### Vulnerable Code ```python def resolve_run_dirs(skill_dir: Path, run_group: str | None, mode: str) -> tuple[str, Path]: resolved_group = run_group or run_stamp() run_root = skill_dir / "evals" / "runs" / resolved_group run_dir = run_root / mode run_dir.mkdir(parents=True, exist_ok=False) return resolved_group, run_dir ``` The resulting uncontained directory is subsequently used for writes at `scripts/run_eval.py:342-344`: ```python (run_dir / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n") (run_dir / "summary.md").write_text(make_summary_md(skill_dir.name, summary)) (run_dir / "run_metadata.json").write_text(json.dumps(run_metadata, ensure_ascii=False, indent=2) + "\n") ``` ### Technical Analysis The user-controlled `--run-group` argument is appended directly to the nominal `evals/runs` root. The implementation does not reject absolute paths, parent-directory components such as `..`, or path separators. It also does not resolve the resulting path and verify that it remains beneath the authorized run root. In Python's `pathlib`, parent-directory components can escape the intended root, while joining an absolute path can discard the preceding path components entirely. Consequently, `run_dir.mkdir()` and the subsequent `write_text()` operations can act outside the target Skill directory. The `mode` component is constrained by `argparse` choices, but this does not mitigate traversal through `resolved_group`. ### Attack Path 1. The attacker can invoke `run_eval.py` and control `--run-group`. 2. The attacker supplies an absolute path or a traversal sequence, for example: ```bash python3 scripts/run_eval.py /path/to/skill \ --run-group ../../../../attacker-selected-dire ...[truncated 1312 chars]
Remediation
## Remediation Suggestions 1. Restrict run-group names to simple identifiers: ```python import re RUN_GROUP_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$") def validate_run_group(value: str) -> str: if value in {".", ".."} or not RUN_GROUP_PATTERN.fullmatch(value): raise SystemExit( "Error: run-group may contain only letters, digits, dots, " "underscores, and hyphens." ) return value ``` 2. Resolve and enforce containment before creating anything: ```python runs_root = (skill_dir / "evals" / "runs").resolve() resolved_group = validate_run_group(run_group or run_stamp()) run_root = (runs_root / resolved_group).resolve() if not run_root.is_relative_to(runs_root): raise SystemExit("Error: run-group escapes the evals/runs directory.") ``` 3. Perform the same containment check on `run_dir` before calling `mkdir()` or writing files. 4. If target directories may be writable by untrusted users, reject symlinked path components or open files through directory-relative, no-follow mechanisms to reduce symlink race risks. 5. Add regression tests covering absolute paths, `../` traversal, nested separators, `.`, `..`, and symlink-based escape attempts.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/compare_runs.py:158
Finding
Path Traversal Enables External Summary Reads and Comparison File Overwrites## Vulnerability Details **File Location**: `scripts/compare_runs.py:158-188` **Vulnerability Type**: Unsanitized path construction allowing traversal-based reads and writes **Risk Level**: Medium ### Vulnerable Code ```python skill_dir = Path(args.skill_dir).expanduser().resolve() ensure_skill_dir(skill_dir) run_group_dir = skill_dir / "evals" / "runs" / args.run_group if not run_group_dir.is_dir(): raise SystemExit( f"Error: run group not found: {run_group_dir}\n" "- next step: run run_eval.py first, then retry compare_runs.py" ) left_summary_path = run_group_dir / args.left_mode / "summary.json" right_summary_path = run_group_dir / args.right_mode / "summary.json" if not left_summary_path.is_file() or not right_summary_path.is_file(): raise SystemExit( missing_summary_message( run_group_dir, args.left_mode, args.right_mode, left_summary_path, right_summary_path, ) ) left_summary = load_json(left_summary_path) right_summary = load_json(right_summary_path) comparison = compare_summaries(left_summary, right_summary, args.left_mode, args.right_mode) comparison_json_path = run_group_dir / "comparison.json" comparison_md_path = run_group_dir / "comparison.md" comparison_json_path.write_text(json.dumps(comparison, ensure_ascii=False, indent=2) + "\n") comparison_md_path.write_text(make_md(skill_dir.name, args.run_group, comparison)) ``` ### Technical Analysis The required `--run-group` value is incorporated directly into `run_group_dir` without validation or containment enforcement. An absolute path can replace the intended base, and `..` components can traverse out of `evals/runs`. If the attacker-selected directory contains the expected mode subdirectories and valid `summary.json` files, the script reads those external files. It then writes `comparison.json` and `comparison.md` ...[truncated 1945 chars]
Remediation
## Remediation Suggestions 1. Apply the same strict run-group identifier validation used by `run_eval.py`. 2. Resolve the trusted root and candidate directory, then enforce containment before any existence check, directory iteration, read, or write: ```python runs_root = (skill_dir / "evals" / "runs").resolve() run_group = validate_run_group(args.run_group) run_group_dir = (runs_root / run_group).resolve() if not run_group_dir.is_relative_to(runs_root): raise SystemExit("Error: run-group escapes the evals/runs directory.") ``` 3. Resolve each summary path and verify it remains inside `run_group_dir` before reading it. 4. Reject unexpected symlinks for the run-group directory, mode directories, summaries, and output files when the workspace is not fully trusted. 5. Use atomic output replacement through temporary files inside the validated directory. If overwriting existing comparisons is not required, use exclusive file creation instead. 6. Add tests proving that absolute paths, parent traversal, embedded separators, and symlink escapes are rejected before any file is read or modified.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Ae1

High
Category
analysis-evasion
Content
1. Confirm the target folder is a skill directory with `SKILL.md`.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
1. Confirm the target folder is a skill directory with `SKILL.md`.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill instructs the agent to run local scripts against arbitrary skill paths and references capabilities consistent with file read, file write, and potentially network-adjacent behavior, but it declares no explicit tool scope such as permissions or allowed-tools. That mismatch is dangerous because consumers and enforcement layers cannot clearly constrain what the skill may access, increasing the risk of overbroad file operations or unintended external access when evaluating untrusted skills.

Static analysis

No suspicious patterns detected.