█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Cybersecurity/Application Security/Security Code Review
55 minIntermediate

Security Code Review

After this lesson, you will be able to: Read another developer's code (JavaScript, Python, or SQL) and find vulnerabilities by pattern recognition before running the code.

Code review is the highest-leverage AppSec skill: one good reviewer prevents bugs from ever reaching production. This lesson teaches the canonical insecure patterns to grep for and the discipline that turns reviewers into engineers.

Prerequisites:OWASP Top 10 Lab

The reviewer's mindset

When you read code, ask: where does untrusted data enter? where does it leave? what transformations happen along the way? Most vulnerabilities live at the boundary, request input concatenated into a query, response output not encoded, file paths assembled from user-controlled strings. Train yourself to slow down at the boundaries.

Insecure JavaScript / Node.js patterns

Each of these is a real bug from a real codebase.

tsx
// 1. SQL Injection via string concat
const q = `SELECT * FROM users WHERE id = ${req.query.id}`; // BAD
// Fix: parameterised query
const q = `SELECT * FROM users WHERE id = $1`; pool.query(q, [req.query.id]);
// 2. Command Injection via shell
require('child_process').exec(`ping ${req.body.host}`); // BAD
// Fix: execFile + array args, no shell
require('child_process').execFile('ping', ['-c', '1', req.body.host]);
// 3. SSRF
fetch(req.body.url); // BAD
// Fix: allowlist hostnames; reject 169.254.169.254, 127.0.0.1, *.internal
// 4. Path traversal
fs.readFileSync(`./uploads/${req.params.name}`); // BAD (../../etc/passwd)
// Fix: path.resolve and check it stays inside ./uploads/

Insecure Python patterns

Read these and predict the bug before reading the fix.

python
# 1. Pickle deserialization of untrusted data
import pickle
user_obj = pickle.loads(request.body) # BAD, arbitrary code execution
# Fix: never pickle untrusted input. Use JSON or a typed schema.
# 2. eval / exec on user input
result = eval(request.args['expr']) # BAD
# Fix: parse with ast.literal_eval for limited literals, or build a real parser
# 3. SSTI (Server-Side Template Injection) in Jinja2
template = f"Hello, {request.args['name']}" # BAD when rendered
# Fix: Jinja2 autoescape + never interpolate user input into template source
# 4. YAML.load (full loader) on untrusted input
import yaml; yaml.load(open('config.yml')) # BAD
# Fix: yaml.safe_load(...)

The 30-minute review checklist

1. Auth and authz on every endpoint? (Is the middleware actually applied?) 2. Input validation at the boundary? (Schema validation library, e.g., zod, pydantic.) 3. Output encoding context-aware? (HTML vs JSON vs SQL.) 4. Secrets in code or env? (Grep for AWS_, _TOKEN, _KEY, etc.) 5. Crypto primitives correct? (No MD5/SHA-1 for passwords, no ECB mode, no hardcoded IVs.) 6. Error messages safe? (No stack traces / DB structure leaking to users.) 7. Dependencies pinned and scanned? (npm audit, pip-audit, dependabot.) 8. Logging and observability? (Audit trail for sensitive actions.)

💡 Use SAST as your second pair of eyes

Semgrep (free, open source) catches most of these patterns automatically. Add it to CI and review the findings as code review prep, not a replacement. GitHub Advanced Security (CodeQL) is the enterprise standard if you're at a paid GitHub plan. Never trust SAST alone; logic bugs and business rules are still humans-only.

Practice on a real PR

Find an open PR on a real GitHub project that touches request handling. Read it like a reviewer.

  1. 1

    Find an open PR in an active repo (e.g., browse popular Python or Node.js projects on GitHub)

  2. 2

    Pick a PR that touches user input, file handling, or external API calls

  3. 3

    Apply the 30-minute checklist above

  4. 4

    Write a review comment for each concern you find. Be specific and helpful, not nitpicky

  5. 5

    Compare your findings against the actual PR review by maintainers

Common mistakes only experienced reviewers catch

Reviewing the diff in isolation, missing how the new code interacts with existing trust boundaries. Always read the calling code too. Approving 'it compiles' without testing the security claim (e.g., the prepared statement is parameterised correctly but the schema migration changed the column type from int to text, which re-opens injection through casting). Blocking on style while letting real vulnerabilities through. Prioritise. Trusting comments that say 'sanitised'; check the sanitisation function actually exists and does what the comment claims.

Quick Check

Where does most vulnerable code live?

Pick the cleanest one-line answer.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Security Best Practices for Developers
Back to Application Security
Bug Bounty Programs→