{"id":"699fa215-0328-42ed-8203-cb9e08c24dd9","shortId":"ek64zM","kind":"skill","title":"file-uploads","tagline":"Expert at handling file uploads and cloud storage. Covers S3,","description":"# File Uploads & Storage\n\nExpert at handling file uploads and cloud storage. Covers S3,\nCloudflare R2, presigned URLs, multipart uploads, and image\noptimization. Knows how to handle large files without blocking.\n\n**Role**: File Upload Specialist\n\nCareful about security and performance. Never trusts file\nextensions. Knows that large uploads need special handling.\nPrefers presigned URLs over server proxying.\n\n### Principles\n\n- Never trust client file type claims\n- Use presigned URLs for direct uploads\n- Stream large files, never buffer\n- Validate on upload, optimize after\n\n## Sharp Edges\n\n### Trusting client-provided file type\n\nSeverity: CRITICAL\n\nSituation: User uploads malware.exe renamed to image.jpg. You check\nextension, looks fine. Store it. Serve it. Another user\ndownloads and executes it.\n\nSymptoms:\n- Malware uploaded as images\n- Wrong content-type served\n\nWhy this breaks:\nFile extensions and Content-Type headers can be faked.\nAttackers rename executables to bypass filters.\n\nRecommended fix:\n\n# CHECK MAGIC BYTES\n\nimport { fileTypeFromBuffer } from \"file-type\";\n\nasync function validateImage(buffer: Buffer) {\n  const type = await fileTypeFromBuffer(buffer);\n  \n  const allowedTypes = [\"image/jpeg\", \"image/png\", \"image/webp\"];\n  \n  if (!type || !allowedTypes.includes(type.mime)) {\n    throw new Error(\"Invalid file type\");\n  }\n  \n  return type;\n}\n\n// For streams\nimport { fileTypeFromStream } from \"file-type\";\nconst type = await fileTypeFromStream(readableStream);\n\n### No upload size restrictions\n\nSeverity: HIGH\n\nSituation: No file size limit. Attacker uploads 10GB file. Server runs\nout of memory or disk. Denial of service. Or massive\nstorage bill.\n\nSymptoms:\n- Server crashes on large uploads\n- Massive storage bills\n- Memory exhaustion\n\nWhy this breaks:\nWithout limits, attackers can exhaust resources. Even\nlegitimate users might accidentally upload huge files.\n\nRecommended fix:\n\n# SET SIZE LIMITS\n\n// Formidable\nconst form = formidable({\n  maxFileSize: 10 * 1024 * 1024, // 10MB\n});\n\n// Multer\nconst upload = multer({\n  limits: { fileSize: 10 * 1024 * 1024 },\n});\n\n// Client-side early check\nif (file.size > 10 * 1024 * 1024) {\n  alert(\"File too large (max 10MB)\");\n  return;\n}\n\n// Presigned URL with size limit\nconst command = new PutObjectCommand({\n  Bucket: BUCKET,\n  Key: key,\n  ContentLength: expectedSize, // Enforce size\n});\n\n### User-controlled filename allows path traversal\n\nSeverity: CRITICAL\n\nSituation: User uploads file named \"../../../etc/passwd\". You use\nfilename directly. File saved outside upload directory.\nSystem files overwritten.\n\nSymptoms:\n- Files outside upload directory\n- System file access\n\nWhy this breaks:\nUser input should never be used directly in file paths.\nPath traversal sequences can escape intended directories.\n\nRecommended fix:\n\n# SANITIZE FILENAMES\n\nimport path from \"path\";\nimport crypto from \"crypto\";\n\nfunction safeFilename(userFilename: string): string {\n  // Extract just the base name\n  const base = path.basename(userFilename);\n  \n  // Remove any remaining path chars\n  const sanitized = base.replace(/[^a-zA-Z0-9.-]/g, \"_\");\n  \n  // Or better: generate new name entirely\n  const ext = path.extname(userFilename).toLowerCase();\n  const allowed = [\".jpg\", \".png\", \".pdf\"];\n  \n  if (!allowed.includes(ext)) {\n    throw new Error(\"Invalid extension\");\n  }\n  \n  return crypto.randomUUID() + ext;\n}\n\n// Never do this\nconst path = \"uploads/\" + req.body.filename; // DANGER!\n\n// Do this\nconst path = \"uploads/\" + safeFilename(req.body.filename);\n\n### Presigned URL shared or cached incorrectly\n\nSeverity: MEDIUM\n\nSituation: Presigned URL for private file returned in API response.\nResponse cached by CDN. Anyone with cached URL can access\nprivate file for hours.\n\nSymptoms:\n- Private files accessible via cached URLs\n- Access after expiry\n\nWhy this breaks:\nPresigned URLs grant temporary access. If cached or shared,\naccess extends beyond intended scope.\n\nRecommended fix:\n\n# CONTROL PRESIGNED URL DISTRIBUTION\n\n// Short expiry for sensitive files\nconst url = await getSignedUrl(s3, command, {\n  expiresIn: 300, // 5 minutes\n});\n\n// No-cache headers for presigned URL responses\nreturn Response.json({ url }, {\n  headers: {\n    \"Cache-Control\": \"no-store, max-age=0\",\n  },\n});\n\n// Or use CloudFront signed URLs for more control\n\n## Validation Checks\n\n### Only checking file extension\n\nSeverity: CRITICAL\n\nMessage: Check magic bytes, not just extension\n\nFix action: Use file-type library to verify actual type\n\n### User filename used directly in path\n\nSeverity: CRITICAL\n\nMessage: Sanitize filenames to prevent path traversal\n\nFix action: Use path.basename() and generate safe name\n\n## Collaboration\n\n### Delegation Triggers\n\n- image optimization CDN -> performance-optimization (Image delivery)\n- storing file metadata -> postgres-wizard (Database schema)\n\n## When to Use\n- User mentions or implies: file upload\n- User mentions or implies: S3\n- User mentions or implies: R2\n- User mentions or implies: presigned URL\n- User mentions or implies: multipart\n- User mentions or implies: image upload\n- User mentions or implies: cloud storage\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["file","uploads","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows"],"capabilities":["skill","source-sickn33","skill-file-uploads","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/file-uploads","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34793 github stars · SKILL.md body (5,427 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T00:50:56.427Z","embedding":null,"createdAt":"2026-04-18T21:37:11.748Z","updatedAt":"2026-04-24T00:50:56.427Z","lastSeenAt":"2026-04-24T00:50:56.427Z","tsv":"'/../../etc/passwd':333 '/g':413 '0':557 '10':272,282,292 '1024':273,274,283,284,293,294 '10gb':218 '10mb':275,300 '300':533 '5':534 '9':412 'a-za-z0':408 'access':353,483,491,495,505,510 'accident':258 'action':582,608 'actual':590 'age':556 'alert':295 'allow':323,426 'allowed.includes':431 'allowedtyp':176 'allowedtypes.includes':182 'anoth':119 'anyon':478 'api':472 'ask':709 'async':165 'attack':148,216,250 'await':172,202,528 'base':394,397 'base.replace':407 'better':415 'beyond':512 'bill':233,242 'block':43 'boundari':717 'break':137,247,356,500 'bucket':311,312 'buffer':87,168,169,174 'bypass':152 'byte':158,577 'cach':460,475,480,493,507,538,549 'cache-control':548 'care':48 'cdn':477,620 'char':404 'check':111,156,289,567,569,575 'claim':76 'clarif':711 'clear':684 'client':73,97,286 'client-provid':96 'client-sid':285 'cloud':10,23,674 'cloudflar':27 'cloudfront':560 'collabor':615 'command':308,531 'const':170,175,200,268,277,307,396,405,420,425,444,451,526 'content':132,142 'content-typ':131,141 'contentlength':315 'control':321,517,550,565 'cover':12,25 'crash':236 'criteria':720 'critic':102,327,573,599 'crypto':383,385 'crypto.randomuuid':439 'danger':448 'databas':632 'deleg':616 'deliveri':625 'denial':227 'describ':688 'direct':81,337,363,595 'directori':342,350,373 'disk':226 'distribut':520 'download':121 'earli':288 'edg':94 'enforc':317 'entir':419 'environ':700 'environment-specif':699 'error':186,435 'escap':371 'even':254 'execut':123,150 'exhaust':244,252 'expecteds':316 'expert':4,17,705 'expiresin':532 'expiri':497,522 'ext':421,432,440 'extend':511 'extens':56,112,139,437,571,580 'extract':391 'fake':147 'file':2,7,14,20,41,45,55,74,85,99,138,163,188,198,213,219,261,296,331,338,344,347,352,365,469,485,490,525,570,585,627,641 'file-typ':162,197,584 'file-upload':1 'file.size':291 'filenam':322,336,377,593,602 'files':281 'filetypefrombuff':160,173 'filetypefromstream':195,203 'filter':153 'fine':114 'fix':155,263,375,516,581,607 'form':269 'formid':267,270 'function':166,386 'generat':416,612 'getsignedurl':529 'grant':503 'handl':6,19,39,63 'header':144,539,547 'high':210 'hour':487 'huge':260 'imag':34,129,618,624,668 'image.jpg':109 'image/jpeg':177 'image/png':178 'image/webp':179 'impli':640,646,651,656,662,667,673 'import':159,194,378,382 'incorrect':461 'input':358,714 'intend':372,513 'invalid':187,436 'jpg':427 'key':313,314 'know':36,57 'larg':40,59,84,238,298 'legitim':255 'librari':587 'limit':215,249,266,280,306,676 'look':113 'magic':157,576 'malwar':126 'malware.exe':106 'massiv':231,240 'match':685 'max':299,555 'max-ag':554 'maxfiles':271 'medium':463 'memori':224,243 'mention':638,644,649,654,660,665,671 'messag':574,600 'metadata':628 'might':257 'minut':535 'miss':722 'multer':276,279 'multipart':31,663 'name':332,395,418,614 'need':61 'never':53,71,86,360,441 'new':185,309,417,434 'no-cach':536 'no-stor':551 'optim':35,91,619,623 'output':694 'outsid':340,348 'overwritten':345 'path':324,366,367,379,381,403,445,452,597,605 'path.basename':398,610 'path.extname':422 'pdf':429 'perform':52,622 'performance-optim':621 'permiss':715 'png':428 'postgr':630 'postgres-wizard':629 'prefer':64 'presign':29,65,78,302,456,465,501,518,541,657 'prevent':604 'principl':70 'privat':468,484,489 'provid':98 'proxi':69 'putobjectcommand':310 'r2':28,652 'readablestream':204 'recommend':154,262,374,515 'remain':402 'remov':400 'renam':107,149 'req.body.filename':447,455 'requir':713 'resourc':253 'respons':473,474,543 'response.json':545 'restrict':208 'return':190,301,438,470,544 'review':706 'role':44 'run':221 's3':13,26,530,647 'safe':613 'safefilenam':387,454 'safeti':716 'sanit':376,406,601 'save':339 'schema':633 'scope':514,687 'secur':50 'sensit':524 'sequenc':369 'serv':117,134 'server':68,220,235 'servic':229 'set':264 'sever':101,209,326,462,572,598 'share':458,509 'sharp':93 'short':521 'side':287 'sign':561 'situat':103,211,328,464 'size':207,214,265,305,318 'skill':679 'skill-file-uploads' 'source-sickn33' 'special':62 'specialist':47 'specif':701 'stop':707 'storag':11,16,24,232,241,675 'store':115,553,626 'stream':83,193 'string':389,390 'substitut':697 'success':719 'symptom':125,234,346,488 'system':343,351 'task':683 'temporari':504 'test':703 'throw':184,433 'tolowercas':424 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'travers':325,368,606 'treat':692 'trigger':617 'trust':54,72,95 'type':75,100,133,143,164,171,181,189,191,199,201,586,591 'type.mime':183 'upload':3,8,15,21,32,46,60,82,90,105,127,206,217,239,259,278,330,341,349,446,453,642,669 'url':30,66,79,303,457,466,481,494,502,519,527,542,546,562,658 'use':77,335,362,559,583,594,609,636,677 'user':104,120,256,320,329,357,592,637,643,648,653,659,664,670 'user-control':319 'userfilenam':388,399,423 'valid':88,566,702 'validateimag':167 'verifi':589 'via':492 'without':42,248 'wizard':631 'wrong':130 'z0':411 'za':410","prices":[{"id":"1d42f0bd-dc38-46a7-a162-f0ebd2b5cbfe","listingId":"699fa215-0328-42ed-8203-cb9e08c24dd9","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:37:11.748Z"}],"sources":[{"listingId":"699fa215-0328-42ed-8203-cb9e08c24dd9","source":"github","sourceId":"sickn33/antigravity-awesome-skills/file-uploads","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/file-uploads","isPrimary":false,"firstSeenAt":"2026-04-18T21:37:11.748Z","lastSeenAt":"2026-04-24T00:50:56.427Z"}],"details":{"listingId":"699fa215-0328-42ed-8203-cb9e08c24dd9","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"file-uploads","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34793,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-24T00:28:59Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"d456f36f4e4f64bc4fdda3cb155a196d46817c38","skill_md_path":"skills/file-uploads/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/file-uploads"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"file-uploads","description":"Expert at handling file uploads and cloud storage. Covers S3,"},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/file-uploads"},"updatedAt":"2026-04-24T00:50:56.427Z"}}