PS Product SecurityKnowledge Base

Interview Labs

AppSec Vulnerable Code Screening Cheat Sheet by Language

Purpose: give candidates and reviewers a fast, repeatable way to inspect vulnerable-code snippets under interview pressure without scanners. This page is intentionally biased toward the first 2 to 5 minutes of analysis: what to look at first, what patterns are high signal, and how to explain findings crisply.

Use this page with: AppSec Engineer Code and Weakness Review Drills and Live Code-Review Drills and Answer Guides.

The universal screening pattern

When a snippet lands on screen, scan in this order:

  1. Where does attacker-controlled input enter? Request params, JSON body, headers, cookies, uploaded files, environment variables, CLI args, queue payloads, DB results, deserialized objects.
  2. Where are the dangerous sinks? SQL, OS commands, filesystem writes, archive extraction, templates, redirects, authz decisions, crypto calls, serialization, reflection, unsafe native APIs.
  3. What protects the path? Validation, output encoding, parameterization, allow-lists, authn, authz, object ownership checks, safe defaults, rate limits, audit logs.
  4. What business action is happening? Money movement, role change, password reset, approval, export, delete, token issuance, tenant switch, webhook trust decision.
  5. What is the likely impact? Code execution, data theft, cross-tenant access, privilege escalation, account takeover, denial of service, tampering, secret disclosure.
  6. What exact fix would you make first? Name the vulnerable line, the control you would add, and the regression test you would write.

A good 30-second opening statement

"I would start by identifying the trust boundary and the dangerous sinks. In this snippet, untrusted input reaches [sink] without [control]. The primary issue is [weakness]; the likely impact is [impact]. I would fix it by [precise fix] and add [test/guardrail] so it does not regress."

What you can usually skip on first pass

  • harmless constant definitions and obvious DTOs;
  • formatting, style, and naming nits;
  • imports that are not security-relevant;
  • comments unless they reveal hidden trust assumptions;
  • framework boilerplate if it does not affect auth, validation, or I/O.

Python

High-signal danger words and APIs

  • os.system, subprocess.run(..., shell=True), subprocess.Popen(..., shell=True)
  • eval, exec, ast.literal_eval on untrusted input used incorrectly
  • raw SQL via string formatting, %, .format(), f-strings
  • pickle.loads, yaml.load with unsafe loader, marshal, shelve
  • hashlib.md5, hashlib.sha1 for passwords or integrity decisions
  • tempfile.mktemp, unsafe file writes to predictable paths
  • Flask/Django routes that mutate state without authz/CSRF/object ownership checks

What dangerous sections look like

  • f-strings or concatenation around SELECT, UPDATE, DELETE, ORDER BY
  • shell helpers that interpolate request params into command strings
  • serializers/deserializers fed by cookies, queues, or uploaded files
  • password reset, invite acceptance, admin endpoints guarded only by a shared token
  • archive processing with no canonical path checks

What to say out loud

  • "I see untrusted input flowing into a shell/SQL/deserialize sink."
  • "This is structural injection, not only string injection, because ORDER BY/table names are also attacker-influenced."
  • "The larger issue is broken authorization or tenant isolation, not just input validation."

Typical safe replacements

  • parameterized queries with DB driver placeholders
  • subprocess.run([...], shell=False, check=True) with allow-listed arguments
  • yaml.safe_load, never pickle for untrusted data
  • Argon2, bcrypt, or scrypt for passwords

Java

High-signal danger words and APIs

  • Runtime.getRuntime().exec, ProcessBuilder with attacker-influenced args
  • string-built JDBC / JPA native queries
  • ObjectInputStream.readObject, XML decoders, risky Jackson polymorphic config
  • Spring controller methods missing @PreAuthorize or object ownership checks
  • MessageDigest.getInstance("MD5") or SHA-1 for passwords/signatures
  • Files.write, Paths.get with user-controlled path elements
  • open redirects through redirect: or attacker-controlled Location

Dangerous section patterns

  • repositories loading by id only when path also contains tenantId
  • approval or delete endpoints that check authentication but not role or workflow state
  • native queries with concatenated ORDER BY, search, tenant, or status filters
  • download/export handlers returning entire entity objects or raw stack traces

What to say out loud

  • "This looks like IDOR or broken object-level authorization because the lookup ignores tenant or ownership context."
  • "I would not stop at the controller annotation; I want service-layer authorization and state-transition checks too."
  • "The right remediation is a repository method constrained by tenant plus a policy service, not another inline if block."

Typical safe replacements

  • findByIdAndTenantId(...) or equivalent object-scoped data access
  • bean validation + allow-lists for sort/filter fields
  • PasswordEncoder with strong algorithms
  • safer deserialization and strict type handling

C++

High-signal danger words and APIs

  • system, popen, exec*
  • strcpy, strcat, sprintf, gets, unchecked memcpy
  • raw pointer arithmetic around buffers
  • archive extraction with path concatenation
  • manual crypto or homegrown token logic
  • insecure random sources (rand) for security decisions
  • deserialization of attacker-controlled binary formats with weak bounds checks

Dangerous section patterns

  • fixed-size char buffers receiving unbounded input
  • archive/file extraction logic that joins base dir + entry name blindly
  • integer length fields used for allocation/copy without validating range
  • unsafe narrowing conversions before buffer use
  • network services parsing custom binary protocols without strict framing

What to say out loud

  • "I would look for memory-safety bugs and trust-boundary mistakes before syntax details."
  • "The extraction path is vulnerable to traversal unless canonicalized against the intended root."
  • "Even if this is not obviously exploitable RCE, a denial-of-service or overwrite primitive is still serious."

Typical safe replacements

  • safer abstractions (std::string, bounded APIs)
  • canonical path validation using filesystem APIs
  • strict bounds checking before allocation and copy
  • least-privilege runtime and sandboxing where native parsing is unavoidable

TypeScript / JavaScript

High-signal danger words and APIs

  • eval, new Function, dynamic import from untrusted path
  • child_process.exec, execSync, spawn with shell or interpolated args
  • template rendering with unescaped HTML
  • direct use of req.query, req.body, req.headers in SQL/NoSQL/redirects
  • prototype pollution via merge helpers or unsafe deep merge
  • JWT verification bugs, alg=none anti-patterns, weak secret handling
  • NoSQL queries built directly from user objects

Dangerous section patterns

  • spreading req.body into trusted objects ({...req.body}) without allow-listing
  • Mongo queries using attacker-provided JSON directly
  • Express middleware checking only if (user.isAdmin) without strong session or auth source
  • SSR or template code returning attacker-controlled HTML snippets

What to say out loud

  • "I am checking both data injection and object-shape abuse, because JavaScript ecosystems are also vulnerable to prototype pollution and auth bypass through implicit trust in object fields."
  • "In Node.js, shell execution and broad object merges are both high-signal danger zones."

Typical safe replacements

  • schema validation at boundaries
  • allow-listed object mapping instead of mass assignment
  • parameterized DB calls and ORM safe APIs
  • safe templating defaults and output encoding

PHP

High-signal danger words and APIs

  • include, require, include_once, require_once with user-controlled path
  • raw $_GET, $_POST, $_COOKIE, $_REQUEST reaching SQL or file paths
  • unserialize, dynamic function names, variable variables
  • weak file upload handling, move_uploaded_file into web root
  • md5, sha1 for passwords or security tokens
  • loose comparisons (==) in auth/token logic
  • shell_exec, backticks, exec, passthru

Dangerous section patterns

  • file paths built from request params
  • session booleans used as the only control on admin actions
  • report export features returning attacker-selected files
  • comparison of secrets or auth values with loose equality
  • upload handlers trusting extension or client MIME type

What to say out loud

  • "I am checking for local file inclusion, traversal, and weak session assumptions first because those are common interview traps in PHP snippets."
  • "Loose comparison is dangerous here if token or hash values are compared with ==."

Typical safe replacements

  • strict comparisons (===)
  • allow-listed template identifiers
  • password_hash / password_verify
  • file storage outside web root with server-side generated names

Go

High-signal danger words and APIs

  • exec.Command with attacker-controlled program or arguments
  • SQL built with fmt.Sprintf or concatenation
  • unsafe deserialization or custom parsers without bounds checks
  • http.HandleFunc handlers mutating state without authz/CSRF checks
  • crypto/md5, crypto/sha1 used for passwords or security decisions
  • archive extraction or filesystem writes without path normalization
  • direct exposure of internal errors in HTTP responses

Dangerous section patterns

  • path params or JSON fields passed into SQL or filesystem helpers
  • privileged admin endpoints missing actor/role verification
  • http.ListenAndServe with weak TLS posture for sensitive services
  • goroutines masking time-of-check/time-of-use state bugs in approval flows

What to say out loud

  • "In Go I would still follow the same model: source, sink, authz, state transition, then exact fix."
  • "If I see fmt.Sprintf around SQL, that is an immediate injection smell."

Typical safe replacements

  • prepared statements / query parameterization
  • strict object mapping from request DTOs to domain structs
  • safe archive extraction helpers and canonical path checks
  • explicit authz middleware and policy services

SQL

High-signal danger words and patterns

  • dynamic SQL with concatenated user input
  • EXEC, sp_executesql, procedural dynamic query builders
  • wildcard privileges and broad grants
  • missing tenant filters in multi-tenant tables
  • functions or procedures performing privileged actions without caller checks
  • ad hoc reporting queries with attacker-controlled ORDER BY, table, or column names

What dangerous sections look like

  • procedure parameters inserted into SQL text directly
  • audit or export procedures not scoped by actor or tenant
  • grants to application accounts that exceed CRUD needs
  • maintenance procedures callable by application identities

What to say out loud

  • "I am checking both injection and authorization inside the data layer. Even a safe query can still be a security bug if it ignores tenant or ownership filters."
  • "Structural elements such as sort fields, table names, or procedure names must be allow-listed, not escaped."

Typical safe replacements

  • parameters for values, allow-lists for structural SQL choices
  • least-privilege DB accounts
  • views or row-level security where appropriate
  • immutable audit patterns for sensitive state changes

One-page answer structure for any language

When the interviewer asks you to explain the snippet, use this shape:

  1. Call out the trust boundary. "Input comes from request body / path / cookie / queue message."
  2. Name the sink. "That input reaches SQL / shell / file include / deserializer / approval action."
  3. Name the weakness precisely. SQL injection, IDOR, broken authz, path traversal, weak crypto, insecure deserialization, mass assignment.
  4. State the impact in business terms. Data theft, cross-tenant access, admin takeover, workflow abuse, privilege escalation.
  5. Give the first fix and the durable fix. Parameterize now, then add policy guardrails / tests / lint rules / codeowners / safer abstractions.
  6. Mention how you would verify. Unit test, negative test, integration test, policy gate, regression rule.

The short list of danger words to memorize

  • Execution: exec, system, shell, Runtime.exec, subprocess, ProcessBuilder, child_process
  • Deserialization: pickle, unserialize, readObject, unsafe yaml.load
  • Crypto: md5, sha1, custom crypto, static IVs, ECB, hardcoded keys
  • Filesystem: include, require, open, fopen, Paths.get, archive extract, temp file helpers
  • Database: string-built SELECT/UPDATE/DELETE, ORDER BY, LIMIT, dynamic SQL, raw query APIs
  • Auth: isAdmin, role, tenantId, ownerId, approved, reset, invite, token, session
  • Web: redirect, template, HTML, upload, webhook, callback, origin, CORS

Final coaching note

Interviewers are usually not looking for every issue in the snippet. They are looking for whether you can:

  • find the highest-risk issue first;
  • explain it in clear engineering language;
  • propose a realistic fix;
  • show you understand blast radius, trust boundaries, and how to prevent recurrence.

A calm, prioritized answer beats a chaotic list of ten possible bugs every time.