Improper Input Validation
The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly.
Input validation is a frequently-used technique for checking potentially dangerous inputs in order to ensure that the inputs are safe for processing within the code, or when communicating with other components. When software does not validate input properly, an attacker is able to craft the input in a form that is not expected by the rest of the application.
How to fix this vulnerability
Prevention strategies for Improper Input Validation based on 13 Shoulder detection rules.
Use Pydantic models with Field validators instead of raw Request objects
- from fastapi import FastAPI, Request - - app = FastAPI() - - @app.post("/users") - async def create_user(request: Request): - data = await request.json() - return {"user": data["username"]} + from fastapi import FastAPI + from pydantic import BaseModel, EmailStr, Field + + app = FastAPI() + + class UserCreate(BaseModel): + username: str = Field(min_length=3, max_length=50) + email: EmailStr + + @app.post("/users") + async def create_user(user: UserCreate): + return {"user": user.username}
Validate business-critical inputs with range constraints using Pydantic or manual checks
- from flask import request - - @app.route('/apply-discount', methods=['POST']) - def apply_discount_route(): - discount = float(request.form['discount']) - apply_discount(discount) + from pydantic import BaseModel, Field + from fastapi import FastAPI + + class DiscountRequest(BaseModel): + discount: float = Field(..., ge=0, le=100) + quantity: int = Field(..., gt=0) + + @app.post('/apply-discount') + async def apply_discount_route(req: DiscountRequest): + apply_discount(req.discount) return {'status': 'applied'}
Parse string inputs to typed values and validate against business rules before use
package main - import "net/http" - - func applyDiscount(w http.ResponseWriter, r *http.Request) { - discount := r.FormValue("discount") - // discount is a raw string, used without parsing or validation + import ( + "net/http" + "strconv" + ) + + func applyDiscount(w http.ResponseWriter, r *http.Request) { + discountStr := r.FormValue("discount") + discount, err := strconv.ParseFloat(discountStr, 64) + if err != nil { + http.Error(w, "Invalid discount format", http.StatusBadRequest) + return + } + if discount < 0 || discount > 100 { + http.Error(w, "Discount must be between 0 and 100", http.StatusBadRequest) + return + } total := price * (1 - discount/100) processOrder(total) }
Use Echo struct binding with validation tags instead of untyped maps
package main import "github.com/labstack/echo/v4" - func handler(c echo.Context) error { - var input map[string]interface{} - c.Bind(&input) - name := input["name"].(string) - return c.String(200, name) + type UserInput struct { + Name string `json:"name" validate:"required,min=2"` + Email string `json:"email" validate:"required,email"` + } + + func handler(c echo.Context) error { + var input UserInput + if err := c.Bind(&input); err != nil { + return c.JSON(400, map[string]string{"error": err.Error()}) + } + if err := c.Validate(&input); err != nil { + return c.JSON(400, map[string]string{"error": err.Error()}) + } + return c.String(200, input.Name) }
Use Fiber BodyParser with typed structs and validation tags
package main - import "github.com/gofiber/fiber/v2" - - func handler(c *fiber.Ctx) error { - var input map[string]interface{} - c.BodyParser(&input) - name := input["name"].(string) - return c.SendString(name) + import ( + "github.com/gofiber/fiber/v2" + "github.com/go-playground/validator/v10" + ) + + var validate = validator.New() + + type UserInput struct { + Name string `json:"name" validate:"required,min=2"` + Email string `json:"email" validate:"required,email"` + } + + func handler(c *fiber.Ctx) error { + var input UserInput + if err := c.BodyParser(&input); err != nil { + return c.Status(400).JSON(fiber.Map{"error": err.Error()}) + } + if err := validate.Struct(&input); err != nil { + return c.Status(400).JSON(fiber.Map{"error": err.Error()}) + } + return c.SendString(input.Name) }
Validate business-critical inputs with range checks before processing
app.post('/apply-discount', (req, res) => { - const discount = req.body.discount; - applyDiscount(discount); + const value = parseFloat(req.body.discount); + if (isNaN(value) || value < 0 || value > 100) { + return res.status(400).json({ error: 'Discount must be 0-100' }); + } + applyDiscount(value); });
Use server-side prices from the database and validate ranges for business-critical values
app.post('/api/orders', (req, res) => { - const { productId, quantity, price } = req.body; - const total = price * quantity; + const { productId, quantity } = req.body; + const product = products.get(productId); + if (!product) return res.status(404).json({ error: 'Product not found' }); + const total = product.price * quantity; orders.push({ total }); });
Add class-validator decorators to all DTO properties and enable the global ValidationPipe
- export class CreateUserDTO { - username: string; - email: string; - age: number; - } - - @Controller('users') - export class UserController { - @Post() - create(@Body() dto: CreateUserDTO) { - return this.userService.create(dto); - } - } + import { IsString, IsEmail, IsInt, Min, Max, MinLength } from 'class-validator'; + + export class CreateUserDTO { + @IsString() + @MinLength(3) + username: string; + + @IsEmail() + email: string; + + @IsInt() + @Min(0) + @Max(150) + age: number; + } + + // In main.ts: app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
Find vulnerabilities in your code
Use Shoulder to scan your codebase for Improper Input Validation patterns. 13 rules.
# Scan with Shoulder CLI npx @shoulderdev/cli trust --cwe=20 # Or scan entire project npx @shoulderdev/cli trust .
Detection Rules (13)
What to watch for in code reviews
These patterns indicate potential Improper Input Validation vulnerabilities. Look for these during code reviews and security audits.
Scan your codebase for Improper Input Validation
Shoulder CLI finds vulnerable patterns across your entire codebase.