Back to skill

Security audit

DL Transformer Finetune

Security checks for vulnerabilities and agentic risk

Overview

This fine-tuning planning skill is coherent and shows no hidden network, credential, or persistence behavior, but its helper script has under-disclosed local file-write behavior and a CSV handling risk users should review before installing.

Review this skill before installing if agents may run it automatically. Use explicit, non-sensitive output paths, do not rely on --dry-run to avoid filesystem changes until fixed, and prefer JSON or Markdown over CSV when inputs may come from untrusted sources.

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/build_finetune_plan.py:101
Finding
Dry-Run Mode Still Performs Filesystem Writes## Vulnerability Details **File Location**: `scripts/build_finetune_plan.py:16, 101-105` **Vulnerability Type**: Ineffective safety control leading to unintended file creation or overwrite **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument("--dry-run", action="store_true", help="Run without side effects.") ``` ```python "dry_run": args.dry_run, }, } render(result, Path(args.output), args.format) ``` The `render()` function unconditionally creates the destination directory and writes the requested output: ```python def render(result: dict, output_path: Path, fmt: str) -> None: output_path.parent.mkdir(parents=True, exist_ok=True) if fmt == "json": output_path.write_text(json.dumps(result, indent=2), encoding="utf-8") return ``` ### Technical Analysis The command-line description promises that `--dry-run` will run without side effects. However, the flag is only copied into the generated result and is never evaluated before `render()` is called. Consequently, dry-run execution creates parent directories and creates or overwrites the output file. The output path is supplied by the caller and is not restricted to a dedicated artifact directory. The overwrite therefore applies to any path writable by the account running the script. This does not bypass operating-system permissions or provide privilege escalation, but it defeats a documented safety control that operators or automation may rely upon before approving a real write. ### Attack Path 1. An attacker or untrusted automation influences the `--output` argument. 2. An operator invokes the script with `--dry-run`, expecting validation without filesystem changes. 3. The script builds the result without checking `args.dry_run`. 4. `render()` creates the destination's parent directories and writes to the selected file. 5. If the destination already exists and is writable, i ...[truncated 629 chars]
Remediation
## Remediation Suggestions Enforce dry-run behavior before any call that mutates the filesystem: ```python if args.dry_run: print(json.dumps(result, indent=2)) return 0 render(result, Path(args.output), args.format) ``` Additional hardening should include: - Ensure dry-run mode does not call `mkdir()`, `open()`, `write_text()`, or any other state-changing operation. - Refuse to overwrite an existing output file unless the caller supplies an explicit `--force` option. - If the application has a defined workspace, resolve the destination path and verify that it remains inside the authorized output directory. - Add regression tests that snapshot the filesystem before and after dry-run execution and assert that no files or directories were created or modified. - Update the CLI help only if dry-run is intentionally meant to write output; otherwise, preserve the documented no-side-effect contract.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/build_finetune_plan.py:59
Finding
Spreadsheet Formula Injection in CSV Output## Vulnerability Details **File Location**: `scripts/build_finetune_plan.py:59-65` **Vulnerability Type**: CSV formula injection **Risk Level**: Medium ### Vulnerable Code The script accepts attacker-controlled strings from the input JSON: ```python model_name = str(payload.get("model_name", "distilbert-base-uncased")) task = str(payload.get("task", "sequence-classification")) dataset = str(payload.get("dataset", "dataset-name")) training_config = { "num_epochs": int(payload.get("num_epochs", 3)), "learning_rate": float(payload.get("learning_rate", 2e-5)), "batch_size": int(payload.get("batch_size", 16)), "seed": int(payload.get("seed", 42)), "evaluation_strategy": str(payload.get("evaluation_strategy", "epoch")), "output_dir": str(payload.get("output_dir", "artifacts/finetune-run")), ``` These values are written directly into CSV cells: ```python with output_path.open("w", newline="", encoding="utf-8") as handle: writer = csv.writer(handle) writer.writerow(["field", "value"]) details = result["details"] writer.writerow(["model_name", details["model_name"]]) writer.writerow(["task", details["task"]]) writer.writerow(["dataset", details["dataset"]]) for key, value in details["training_config"].items(): writer.writerow([f"train:{key}", value]) ``` ### Technical Analysis Spreadsheet applications may interpret CSV cells beginning with formula indicators such as `=`, `+`, `-`, or `@` as executable formulas. Python's `csv.writer` correctly handles CSV quoting and delimiters, but quoting does not neutralize spreadsheet formula evaluation. Fields including `model_name`, `task`, `dataset`, `evaluation_strategy`, and `output_dir` can therefore carry formula payloads from the input JSON into the generated CSV. The vulnerability is triggered later when a user opens the ...[truncated 1500 chars]
Remediation
## Remediation Suggestions Sanitize every untrusted value before writing it to CSV. A defensive implementation should detect formula prefixes after leading whitespace and prepend a literal apostrophe: ```python def safe_csv_cell(value: object) -> object: if not isinstance(value, str): return value normalized = value.lstrip() if normalized.startswith(("=", "+", "-", "@", "\t", "\r")): return "'" + value return value ``` Apply the function to all values derived from input: ```python writer.writerow(["model_name", safe_csv_cell(details["model_name"])]) writer.writerow(["task", safe_csv_cell(details["task"])]) writer.writerow(["dataset", safe_csv_cell(details["dataset"])]) for key, value in details["training_config"].items(): writer.writerow([ safe_csv_cell(f"train:{key}"), safe_csv_cell(value), ]) ``` Additional hardening should include: - Treat every string written to CSV as untrusted, including future fields. - Account for leading whitespace, tabs, and carriage returns that can obscure a dangerous prefix. - Document whether sanitization changes the displayed value and, if exact round-tripping is required, prefer JSON for machine-readable output. - Add tests covering values beginning with `=`, `+`, `-`, `@`, tabs, carriage returns, and leading spaces. - Test generated artifacts with the spreadsheet applications expected in the deployment environment.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill directs use of a bundled script and reference file, which implies file read/write capability, but it does not declare any tool scope such as permissions or allowed-tools. This creates an authorization and review gap: an agent may access the filesystem more broadly than intended because the skill contract does not explicitly constrain or document those capabilities.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The --dry-run flag is documented as running 'without side effects', but main() always calls render(), which creates parent directories and writes the output file regardless of args.dry_run. This is a real behavior mismatch that can surprise callers, break automation assumptions, or cause unintended filesystem changes, though it does not by itself enable code execution or privilege escalation.

Static analysis

No suspicious patterns detected.