input-validation

v2026.09.25

Validate and sanitize all user input to prevent XSS, path traversal, command injection, and malformed data. Covers Zod, Joi, class-validator, Laravel validation, and Python Pydantic. Use when building forms, API endpoints, file uploads, or fixing injection vulnerabilities beyond SQL.

GitHub
安装命令
npx skhub add afu-it/input-validation
Markdown
SKILL.md

Input Validation

Validate and sanitize ALL user input at every entry point. This covers Finding #2 (path traversal) and extends beyond SQL injection to XSS, command injection, and data integrity.

The Rule

NEVER trust user input. ALWAYS validate type, length, format, and range on the server side.

Attack Vectors Prevented

AttackHow it worksValidation fix
XSSInject <script> into rendered HTMLHTML-encode output, validate no HTML in text fields
Path Traversal../../etc/passwd in file pathsWhitelist allowed paths, reject ..
Command Injection; rm -rf / in shell commandsNever pass user input to shell, use spawn with args array
SSRFUser-controlled URLs fetch internal servicesWhitelist allowed domains, block private IPs
Prototype Pollution__proto__ in JSON keysStrip dangerous keys, use Object.create(null)
File Upload Abuse.php file uploaded as "image"Validate MIME type + magic bytes, not just extension

Framework Patterns

Node.js with Zod (Recommended)

import { z } from 'zod';

// Define strict schemas
const CreateUserSchema = z.object({
  email: z.string().email().max(255),
  name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/),
  age: z.number().int().min(13).max(150),
  role: z.enum(['user', 'admin']),
});

// Validate at API boundary
app.post('/users', async (req, res) => {
  const result = CreateUserSchema.safeParse(req.body);
  if (!result.success) {
    return res.status(400).json({
      error: 'Validation failed',
      issues: result.error.issues.map(i => ({
        field: i.path.join('.'),
        message: i.message,
      })),
    });
  }
  const validated = result.data; // Type-safe, validated
  await createUser(validated);
});

Path Traversal Prevention

BAD:

app.get('/files/:filename', (req, res) => {
  const file = path.join('/uploads', req.params.filename);
  res.sendFile(file); // ../../etc/passwd works!
});

GOOD:

import path from 'path';

const UPLOAD_DIR = path.resolve('/uploads');

app.get('/files/:filename', (req, res) => {
  const filename = path.basename(req.params.filename); // Strip directory traversal
  const filePath = path.resolve(UPLOAD_DIR, filename);

  // Verify resolved path is still within allowed directory
  if (!filePath.startsWith(UPLOAD_DIR)) {
    return res.status(403).json({ error: 'Access denied' });
  }

  res.sendFile(filePath);
});

XSS Prevention

BAD:

// Server-side rendering with raw user input
app.get('/profile', (req, res) => {
  res.send(`<h1>Welcome, ${req.query.name}</h1>`);
});

GOOD:

import { encode } from 'html-entities';

app.get('/profile', (req, res) => {
  const safeName = encode(req.query.name || '');
  res.send(`<h1>Welcome, ${safeName}</h1>`);
});

// Or better: use a template engine with auto-escaping (EJS, Handlebars, React)

File Upload Validation

import { fileTypeFromBuffer } from 'file-type';

const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB

async function validateUpload(file: Buffer, filename: string) {
  // Check size
  if (file.length > MAX_SIZE) {
    throw new Error('File too large (max 5MB)');
  }

  // Check actual file type via magic bytes (not extension!)
  const type = await fileTypeFromBuffer(file);
  if (!type || !ALLOWED_TYPES.includes(type.mime)) {
    throw new Error(`Invalid file type. Allowed: ${ALLOWED_TYPES.join(', ')}`);
  }

  // Generate safe filename (never use user-provided filename directly)
  const safeFilename = `${crypto.randomUUID()}.${type.ext}`;
  return safeFilename;
}

Command Injection Prevention

BAD:

import { exec } from 'child_process';
exec(`convert ${userFilename} output.png`); // Shell injection!

GOOD:

import { execFile } from 'child_process';
// execFile does NOT use shell, so injection is impossible
execFile('convert', [userFilename, 'output.png']);

Laravel / PHP

// Form request validation
class StoreUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'email' => ['required', 'email:rfc,dns', 'max:255'],
            'name' => ['required', 'string', 'max:100', 'regex:/^[a-zA-Z\s\'-]+$/'],
            'age' => ['required', 'integer', 'min:13', 'max:150'],
            'role' => ['required', Rule::in(['user', 'admin'])],
            'avatar' => ['nullable', 'image', 'mimes:jpg,png,webp', 'max:5120'],
        ];
    }
}

Python with Pydantic

from pydantic import BaseModel, EmailStr, Field, validator
import re

class CreateUser(BaseModel):
    email: EmailStr
    name: str = Field(min_length=1, max_length=100)
    age: int = Field(ge=13, le=150)
    role: Literal['user', 'admin']

    @validator('name')
    def name_must_be_safe(cls, v):
        if not re.match(r"^[a-zA-Z\s'-]+$", v):
            raise ValueError('Name contains invalid characters')
        return v

# Usage in FastAPI
@app.post("/users")
async def create_user(user: CreateUser):
    # user is already validated and type-safe
    pass

Validation Checklist

For every API endpoint, verify:

  • All input fields have type validation (string, number, boolean)
  • String fields have max length limits
  • Numeric fields have min/max range
  • Enum fields use allowlists (not blocklists)
  • Email fields use proper email validation
  • URL fields validate protocol (https only) and domain
  • File uploads check magic bytes, not just extension
  • Path parameters cannot traverse directories
  • No user input is passed to shell commands
  • No user input is used in dynamic import() or eval()

PDPA 2024 Consequence

Seksyen 5(1) dan 5(2) PDPA 2010 (Pindaan 2024):

  • Denda sehingga RM1,000,000
  • Penjara sehingga 3 tahun

Path traversal (Finding #2) allowed attackers to download the entire user database. The developer was charged because input validation is a basic security measure that any competent developer should implement.

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.25

发布时间

Sep 25, 2026

分类

未分类

许可证

未指定

源路径

skills/input-validation

默认分支

main

最新提交

6aeeb8f

Tree SHA

14cb46d