Skip to content

Code Analysis & Secure Review

Static analysis catches vulnerabilities before code reaches production. Combined with security-focused code review, it prevents the most common attack vectors: injection, XSS, and insecure data handling.


SAST (Static Application Security Testing)

SAST tools analyze source code for security flaws without executing it.

Tool Languages Free Best For
Semgrep 30+ languages OSS rules Custom rules, fast scans
SonarQube 30+ languages Community Quality + security combined
Snyk Code 10+ languages Free tier IDE integration
Bandit Python only Yes (OSS) Python-specific checks
ESLint Security JavaScript/TS Yes (OSS) JS/TS-specific checks
gosec Go only Yes (OSS) Go-specific checks

CI Integration

# GitHub Actions — Semgrep
name: SAST
on: [pull_request]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep
    steps:
      - uses: actions/checkout@v4
      - run: semgrep scan --config auto --error --json --output results.json
# Python — Bandit
- name: Bandit Security Scan
  run: |
    uv add --dev bandit
    uv run bandit -r src/ -f json -o bandit-results.json

Tune false positives below 20%

Review SAST findings monthly. Disable or adjust rules that consistently flag false positives. A noisy scanner gets ignored by developers.


Injection Prevention

SQL Injection

# BAD — string interpolation in SQL
query = f"SELECT * FROM users WHERE id = {user_id}"

# GOOD — parameterized query
query = "SELECT * FROM users WHERE id = %s"
cursor.execute(query, (user_id,))

# GOOD — ORM (SQLAlchemy)
user = session.query(User).filter(User.id == user_id).first()

OS Command Injection

import subprocess

# BAD — shell=True with user input
subprocess.run(f"convert {filename} output.png", shell=True)

# GOOD — list args, no shell
subprocess.run(["convert", filename, "output.png"], check=True)

NoSQL Injection (MongoDB)

# BAD — unsanitized query
db.users.find({"username": request.json["username"]})

# GOOD — validate input type first
from pydantic import BaseModel

class LoginRequest(BaseModel):
    username: str  # Pydantic rejects non-string values

LDAP / Expression Language Injection

Always use framework-provided escaping functions. Never build LDAP filters or EL expressions via string concatenation.


Input Validation

Validate all external input at trust boundaries: user forms, API payloads, file uploads, URL parameters, headers.

from pydantic import BaseModel, Field, field_validator
import re

class CreateUserRequest(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    email: str = Field(max_length=254)
    age: int = Field(ge=0, le=150)

    @field_validator("email")
    @classmethod
    def validate_email(cls, v: str) -> str:
        pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
        if not re.match(pattern, v):
            raise ValueError("Invalid email format")
        return v.lower()

    @field_validator("name")
    @classmethod
    def validate_name(cls, v: str) -> str:
        if re.search(r"[<>&\"']", v):
            raise ValueError("Name contains forbidden characters")
        return v.strip()

Client-side validation is for UX only

Server-side validation is mandatory. Client-side checks prevent nothing — attackers bypass them trivially.


XSS (Cross-Site Scripting) Prevention

React / Frontend

// BAD — XSS vector
<div dangerouslySetInnerHTML={{ __html: userContent }} />

// GOOD — use DOMPurify if HTML rendering is required
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />

// BEST — render as text (React escapes by default)
<div>{userContent}</div>

URL Validation

function isValidUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    return ["https:", "http:"].includes(parsed.protocol);
  } catch {
    return false;
  }
}

Output Encoding

Use your framework's built-in encoding. Never concatenate user input into HTML, SQL, or shell commands.


Secure Code Review Checklist

High-risk PRs (auth, data handling, crypto) require a security-focused reviewer.

Check What to Look For
Secrets No hardcoded keys, tokens, passwords
Input validation All user input validated server-side
SQL/NoSQL queries Parameterized, no string interpolation
Shell commands No shell=True with user input
File operations Path traversal protection (resolve() + prefix check)
HTML rendering No dangerouslySetInnerHTML with user data
Error handling No stack traces or internal details in responses
Auth checks Every endpoint verifies authentication + authorization
Data exposure API returns only needed fields, no over-fetching
Logging Sensitive data (passwords, tokens) never logged
Crypto Modern algorithms (AES-256, Argon2id), no MD5/SHA-1
Dependencies New deps are reviewed for known vulnerabilities