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

Content
View full analysis

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

Content
View full analysis

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

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.