跳到主要内容
知仓学习社ZHICANG

file-uploads

Expert at handling file uploads and cloud storage. Covers S3,

不碰外部(只输出文字)无严重或高危命中sickn33/agentic-awesome-skills

它会碰到什么

扫了多少1 个文本文件,6 KB
它会碰到什么不碰外部(只输出文字)
命中总数0 处
命中统计严重 0 · 高 0 · 中 0 · 低 0

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

File Uploads & Storage

Expert at handling file uploads and cloud storage. Covers S3,

Cloudflare R2, presigned URLs, multipart uploads, and image

optimization. Knows how to handle large files without blocking.

Role: File Upload Specialist

Careful about security and performance. Never trusts file

extensions. Knows that large uploads need special handling.

Prefers presigned URLs over server proxying.

Principles

  • Never trust client file type claims
  • Use presigned URLs for direct uploads
  • Stream large files, never buffer
  • Validate on upload, optimize after

Sharp Edges

Trusting client-provided file type

Severity: CRITICAL

Situation: User uploads malware.exe renamed to image.jpg. You check

extension, looks fine. Store it. Serve it. Another user

downloads and executes it.

Symptoms:

  • Malware uploaded as images
  • Wrong content-type served

Why this breaks:

File extensions and Content-Type headers can be faked.

Attackers rename executables to bypass filters.

Recommended fix:

CHECK MAGIC BYTES

import { fileTypeFromBuffer } from "file-type";

async function validateImage(buffer: Buffer) {

const type = await fileTypeFromBuffer(buffer);

const allowedTypes = ["image/jpeg", "image/png", "image/webp"];

if (!type || !allowedTypes.includes(type.mime)) {

throw new Error("Invalid file type");

}

return type;

}

// For streams

import { fileTypeFromStream } from "file-type";

const type = await fileTypeFromStream(readableStream);

No upload size restrictions

Severity: HIGH

Situation: No file size limit. Attacker uploads 10GB file. Server runs

out of memory or disk. Denial of service. Or massive

storage bill.

Symptoms:

  • Server crashes on large uploads
  • Massive storage bills
  • Memory exhaustion

Why this breaks:

Without limits, attackers can exhaust resources. Even

legitimate users might accidentally upload huge files.

Recommended fix:

SET SIZE LIMITS

// Formidable

const form = formidable({

maxFileSize: 10 1024 1024, // 10MB

});

// Multer

const upload = multer({

limits: { fileSize: 10 1024 1024 },

});

// Client-side early check

if (file.size > 10 1024 1024) {

alert("File too large (max 10MB)");

return;

}

// Presigned URL with size limit

const command = new PutObjectCommand({

Bucket: BUCKET,

Key: key,

ContentLength: expectedSize, // Enforce size

});

User-controlled filename allows path traversal

Severity: CRITICAL

Situation: User uploads file named "../../../etc/passwd". You use

filename directly. File saved outside upload directory.

System files overwritten.

Symptoms:

  • Files outside upload directory
  • System file access

Why this breaks:

User input should never be used directly in file paths.

Path traversal sequences can escape intended directories.

Recommended fix:

SANITIZE FILENAMES

import path from "path";

import crypto from "crypto";

function safeFilename(userFilename: string): string {

// Extract just the base name

const base = path.basename(userFilename);

// Remove any remaining path chars

const sanitized = base.replace(/[^a-zA-Z0-9.-]/g, "_");

// Or better: generate new name entirely

const ext = path.extname(userFilename).toLowerCase();

const allowed = [".jpg", ".png", ".pdf"];

if (!allowed.includes(ext)) {

throw new Error("Invalid extension");

}

return crypto.randomUUID() + ext;

}

// Never do this

const path = "uploads/" + req.body.filename; // DANGER!

// Do this

const path = "uploads/" + safeFilename(req.body.filename);

Presigned URL shared or cached incorrectly

Severity: MEDIUM

Situation: Presigned URL for private file returned in API response.

Response cached by CDN. Anyone with cached URL can access

private file for hours.

Symptoms:

  • Private files accessible via cached URLs
  • Access after expiry

Why this breaks:

Presigned URLs grant temporary access. If cached or shared,

access extends beyond intended scope.

Recommended fix:

CONTROL PRESIGNED URL DISTRIBUTION

// Short expiry for sensitive files

const url = await getSignedUrl(s3, command, {

expiresIn: 300, // 5 minutes

});

// No-cache headers for presigned URL responses

return Response.json({ url }, {

headers: {

"Cache-Control": "no-store, max-age=0",

},

});

// Or use CloudFront signed URLs for more control

Validation Checks

Only checking file extension

Severity: CRITICAL

Message: Check magic bytes, not just extension

Fix action: Use file-type library to verify actual type

User filename used directly in path

Severity: CRITICAL

Message: Sanitize filenames to prevent path traversal

Fix action: Use path.basename() and generate safe name

Collaboration

Delegation Triggers

  • image optimization CDN -> performance-optimization (Image delivery)
  • storing file metadata -> postgres-wizard (Database schema)

When to Use

  • User mentions or implies: file upload
  • User mentions or implies: S3
  • User mentions or implies: R2
  • User mentions or implies: presigned URL
  • User mentions or implies: multipart
  • User mentions or implies: image upload
  • User mentions or implies: cloud storage

Example

User request:

> Use @file-uploads for this task: Expert at handling file uploads and cloud storage.

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。

同名技能的其他版本

有 3 个不同仓库或目录里都有叫 file-uploads 的技能。它们内容并不相同,别混用: