Back to skill

Security audit

Remix V2 Forms

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent Remix form guidance, but one file-upload example teaches unsafe server-side filename handling that could lead to vulnerable generated code.

Review the file-upload section before installing or using this skill. If used, ensure generated upload handlers create server-side random filenames, validate file contents after upload, enforce size and authorization limits, and clean up temporary files. I found no evidence of malicious intent, exfiltration, persistence, or hidden execution.

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

T09 · Insecure Skill Coding Practices

Warning
Location
references/uploads.md:78
Finding

Attacker-Controlled Filename Used for Disk-Backed Upload Storage

Content
View full analysis

Vulnerability Details

File Location: references/uploads.md, lines 78-83
Vulnerability Type: Unsafe temporary-file path and filename handling
Risk Level: Medium

Vulnerable Code

ts
const uploadHandler = unstable_createFileUploadHandler({
  directory: "/tmp/uploads",
  maxPartSize: 10_000_000,    // 10 MB
  file: ({ filename }) => filename,
  filter: ({ contentType }) => contentType.startsWith("image/"), // callbacks also receive `name`
});

Technical Analysis

The disk-backed upload example passes the client-supplied multipart filename directly to the upload handler:

ts
file: ({ filename }) => filename

Multipart filenames are attacker-controlled and must not be used as trusted filesystem identifiers. Depending on the upload library's path normalization and collision behavior, crafted filenames could cause path manipulation, overwrite existing files, create ambiguous file ownership, or interfere with concurrent uploads.

The contentType.startsWith("image/") filter does not provide a security boundary because the multipart content type is also supplied by the client. Although the explicit 10 MB limit reduces resource-exhaustion risk, it does not mitigate unsafe filename selection or malicious file content.

Attack Path

  1. An attacker submits a multipart request to an action implemented from this example.
  2. The attacker supplies an image/* content type to pass the filter.
  3. The attacker chooses a filename designed to collide with another upload or, where runtime path handling permits, containing path-separator or traversal components.
  4. The server passes that filename directly to the disk upload handler.
  5. The handler writes the uploaded bytes using the attacker-influenced destination name.
  6. The attacker may overwrite or interfere with files writable by the server process within the effective upload path. Broader path access depends on the library's path co ...[truncated 709 chars]
Remediation
View remediation

Remediation Suggestions

  • Never use the multipart filename as the stored filename. Generate a cryptographically random server-side identifier, such as a UUID.
  • Store the original filename only as sanitized metadata when it is needed for display.
  • Resolve the generated destination against a fixed upload directory and verify that the normalized result remains inside that directory.
  • Reject filenames containing path separators, traversal sequences, control characters, null bytes, reserved names, or unsupported extensions.
  • Use exclusive file creation or another collision-resistant mechanism rather than allowing replacement of an existing path.
  • Do not trust the declared multipart content type. Validate file signatures or magic bytes and process images through a trusted decoder.
  • Apply authorization checks before accepting an upload and associate each temporary file with the authenticated user or request.
  • Move accepted files into controlled permanent storage and reliably delete temporary files on success, validation failure, and exceptions.
  • Retain explicit upload-size limits and consider aggregate request, rate, and storage quotas.

A safer filename callback would resemble:

ts
import { randomUUID } from "node:crypto";

const uploadHandler = unstable_createFileUploadHandler({
  directory: "/tmp/uploads",
  maxPartSize: 10_000_000,
  file: () => randomUUID(),
  filter: ({ contentType, name }) =>
    name === "attachment" && contentType.startsWith("image/"),
});

File content must still be validated after upload because both the filename and declared content type are attacker-controlled.

Vulnerability Patterns
  • 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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep

Static analysis

No suspicious patterns detected.