By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.
18px_cookie
e-remove
Blog

Secure Code Review Checklist

Published on
November 12, 2026
Updated on
November 12, 2026

TL;DR

  • A secure code review checklist is applied to a diff before it merges. Check only the areas the PR touches, mark the rest N/A, and cite each item's OWASP Top 10:2025, ASVS 5.0, or CWE reference in your review comment.
  • Authorization gets the most checks because it produces the most missed flaws: scope every record lookup to the caller, and take the tenant ID from the session, never the request.
  • AI-generated PRs need a few extra checks: confirm new packages exist on their registry, and verify that security controls from the ticket or earlier iterations survived the final diff.
  • Automate the pattern, dataflow, secrets, dependency, and configuration checks, and save reviewers for intent, policy, and design. Endor Labs AI Security Review runs the automated checks on every PR, with triage reasoning and proposed fixes.

This checklist gives you short, specific security checks to run against a pull request before it merges. Each item comes with a one-line reason and an OWASP or CWE reference you can cite in a review comment, and a paste-ready PR template sits near the end.

What does a secure code review checklist cover?

A secure code review checklist is a list of security checks a reviewer applies to a code change before it merges, grouped by the areas a diff can touch. A secure coding checklist does a different job: it guides developers while they write code, before any diff exists. This post covers the review side. For explanations of each vulnerability class and a full review workflow, read our guide to code security review.

If you went looking for "the OWASP checklist," you may have found the Secure Coding Practices Quick Reference Guide. OWASP has archived that project, and its checklists now live in the OWASP Developer Guide. Use the Developer Guide as your current OWASP source.

How to use this checklist on a pull request

Check only the areas the diff touches, and mark the rest N/A. A PR that changes a CSS file doesn't need a crypto review. A PR that touches authentication, data access, secrets, dependencies, or infrastructure gets the full relevant section.

Each item follows the same shape:

  • A checkbox with something to verify in the diff
  • Why: one line on what goes wrong if the check fails
  • Ref: the OWASP Top 10:2025 category, the OWASP ASVS 5.0 chapter, and a MITRE CWE ID where one maps cleanly

For example:

  • Database queries use parameterized statements or a safe ORM API, with no string concatenation. Why: concatenated input lets an attacker rewrite the query. Ref: A05:2025 Injection, ASVS V1, CWE-89

Input validation and injection

Use these checks when untrusted data can reach an interpreter: SQL, the shell, an HTML template, a URL fetcher, or a deserializer.

  • Queries use parameterization or a safe ORM API, never string building. Why: built strings turn data into code. Ref: A05:2025 Injection, ASVS V1, CWE-89
  • Output is encoded for its context: HTML body, attribute, JavaScript, or URL. Why: the wrong encoder leaves XSS open. Ref: A05:2025, ASVS V1, CWE-79
  • No shell commands are built from input. Use argument arrays or a library call. Why: one unescaped character becomes command execution. Ref: A05:2025, CWE-78
  • Outbound requests to user-supplied URLs go through an allowlist of hosts and schemes. Why: open fetchers reach internal services and cloud metadata endpoints. Ref: A01:2025 Broken Access Control, ASVS V1, CWE-918
  • No untrusted data is passed to unsafe deserializers such as pickle, Java native serialization, or YAML full loaders. Why: deserialization gadgets can run arbitrary code. Ref: A08:2025 Software or Data Integrity Failures, CWE-502
  • Validation runs server-side and uses allowlists for type, length, and format. Why: client-side checks are a suggestion, not a control. Ref: ASVS V2, CWE-20

A red flag you can spot in seconds:

cursor.execute(f"SELECT * FROM orders WHERE id = {order_id}")

References for this section: Query Parameterization, Cross Site Scripting Prevention, OS Command Injection Defense, Server Side Request Forgery Prevention, Deserialization, and Input Validation Cheat Sheets.

Authentication

Use these checks for anything that establishes who the caller is.

  • New routes require authentication by default, and any public route is an explicit, documented exception. Why: a forgotten decorator exposes the endpoint to everyone. Ref: A07:2025 Authentication Failures, ASVS V6, CWE-306
  • Authentication uses an established library or identity provider, not hand-rolled logic. Why: custom auth code repeats bugs the libraries fixed years ago. Ref: ASVS V6
  • Passwords are hashed with Argon2id, bcrypt, or scrypt using current work factors. Why: fast hashes like SHA-256 fall to offline cracking. Ref: A04:2025 Cryptographic Failures, CWE-916
  • Login, password reset, and MFA endpoints are rate limited. Why: unlimited attempts invite credential stuffing and code guessing. Ref: A07:2025, CWE-307
  • MFA steps can't be skipped by calling the next endpoint directly. Why: step-skipping bypasses MFA with no cracking required. Ref: A07:2025, ASVS V6
  • Password reset tokens are single-use, random, and expire quickly. Why: reusable or long-lived tokens hand over accounts. Ref: ASVS V6, CWE-640

References for this section: Authentication, Password Storage, Multifactor Authentication, and Forgot Password Cheat Sheets.

Authorization and access control

Authorization gets the most items because it produces the most missed flaws. A role check tells you the caller is a "user." It doesn't tell you the caller owns record 4812, or that record 4812 belongs to their tenant. Pattern-based scanners can't express that rule, and hurried reviewers skip it.

Function-level checks:

  • Every new route, controller action, and GraphQL resolver performs a server-side authorization check. Why: hiding a button in the UI doesn't stop a direct request. Ref: A01:2025 Broken Access Control, ASVS V8, CWE-862
  • Access is denied by default when no rule matches. Why: allow-by-default turns every missing rule into an exposure. Ref: A01:2025, ASVS V8
  • Admin functions check the role inside the function or service, not only in middleware for one path. Why: a second route to the same function often skips the check. Ref: A01:2025, CWE-285

Object-level and tenant-level checks:

  • Records fetched by ID are scoped to the caller, for example WHERE id = ? AND owner_id = ?. Why: an unscoped lookup is an IDOR. Ref: A01:2025, ASVS V8, CWE-639
  • Tenant ID comes from the authenticated session, never from the request body, query string, or path. Why: a user-controlled tenant ID lets one customer read another's data. Ref: A01:2025, CWE-639
  • List, search, and export endpoints filter by tenant and ownership. Why: bulk endpoints leak whole tables when a single-record endpoint would leak one row. Ref: A01:2025, ASVS V8
  • Update and delete operations check ownership, not only reads. Why: write-side IDORs let attackers change or destroy other users' data. Ref: A01:2025, CWE-863
  • Background jobs, queue consumers, and webhook handlers re-check ownership before acting on an ID. Why: they run outside the request context where the original check happened. Ref: A01:2025, CWE-863
  • State-changing requests that use cookie auth have CSRF protection. Why: the browser attaches cookies to forged requests too. Ref: A01:2025, CWE-352

References for this section: Authorization and Insecure Direct Object Reference Prevention Cheat Sheets.

Session and token handling

Use these checks when the diff touches session IDs, cookies, or bearer tokens.

  • The session ID rotates on login and on privilege change. Why: a fixed session ID lets an attacker plant one before login. Ref: A07:2025, ASVS V7, CWE-384
  • Session cookies set HttpOnly, Secure, and an appropriate SameSite value. Why: missing flags expose cookies to scripts and cross-site requests. Ref: ASVS V7, CWE-1004
  • JWT signatures are verified, the none algorithm is rejected, and exp, aud, and iss are checked. Why: an unverified token is a claim anyone can write. Ref: ASVS V9, CWE-347
  • Tokens never appear in URLs or logs. Why: URLs end up in browser history, proxies, and referrer headers. Ref: ASVS V7, CWE-598
  • Logout and password change revoke server-side sessions. Why: a stolen session should die when the user acts. Ref: ASVS V7, CWE-613
  • Refresh tokens rotate on use, and reuse of an old token is detected. Why: rotation limits how long a stolen refresh token works. Ref: ASVS V9

References for this section: Session Management and JSON Web Token for Java Cheat Sheets. The JWT guidance applies beyond Java.

Cryptography

Use these checks to catch crypto misuse in a diff. 

  • No custom cryptographic algorithms or protocols. Why: homemade crypto fails in ways tests won't show. Ref: A04:2025 Cryptographic Failures, ASVS V11, CWE-327
  • Only vetted algorithms and modes appear, with no ECB, MD5, or SHA-1 for security purposes. Why: these leak patterns or allow collisions. Ref: A04:2025, CWE-327
  • IVs and nonces are random or unique per operation, never static or reused. Why: nonce reuse breaks AES-GCM confidentiality and integrity. Ref: A04:2025, CWE-329
  • Security tokens, IDs, and reset codes come from a CSPRNG such as secrets or crypto.randomBytes. Why: Math.random() output is predictable. Ref: A04:2025, CWE-338
  • TLS certificate verification is never disabled, including in "temporary" code. Why: verify=False invites man-in-the-middle attacks. Ref: ASVS V12, CWE-295
  • Keys come from a KMS or secrets manager, not source code or config files. Why: a key in the repo is a key in every clone. Ref: ASVS V11, CWE-321

References for this section: Cryptographic Storage Cheat Sheet.

Secrets

Use these checks for credentials anywhere in the change, including files nobody reads closely.

  • No hardcoded credentials in code, tests, fixtures, Dockerfiles, CI files, or sample configs. Why: test credentials often work in production. Ref: A07:2025, CWE-798
  • Any secret committed at any point in the branch is rotated, not only deleted. Why: deletion leaves it in Git history. Ref: CWE-798
  • .env files and local config files are listed in .gitignore. Why: one git add . publishes them. Ref: ASVS V13
  • New secrets load from a secrets manager or injected environment at runtime. Why: secrets baked into images leak through registries. Ref: ASVS V13

References for this section: Secrets Management Cheat Sheet.

Error handling and logging

Use these checks on failure paths and audit trails.

  • User-facing errors contain no stack traces, SQL, file paths, or internal hostnames. Why: error detail maps your internals for an attacker. Ref: A10:2025 Mishandling of Exceptional Conditions, ASVS V16, CWE-209
  • Exceptions in authentication and authorization paths fail closed. Why: a catch that returns true grants access on error. Ref: A10:2025, CWE-636
  • Authentication failures, authorization denials, and admin actions are logged with actor and target. Why: you can't investigate what you didn't record. Ref: A09:2025 Security Logging and Alerting Failures, ASVS V16, CWE-778
  • Logs contain no secrets, tokens, or personal data. Why: logs travel to more systems and people than the database does. Ref: A09:2025, CWE-532
  • Input written to logs is neutralized for newlines and control characters. Why: log injection forges entries and breaks parsers. Ref: A09:2025, CWE-117

References for this section: Logging and Error Handling Cheat Sheets.

Data protection and privacy

Use these checks when the diff adds or changes how sensitive data is stored, returned, or shared.

  • New personal or sensitive fields are identified and classified. Why: unclassified data skips every control built for it. Ref: ASVS V14
  • Collection is limited to what the feature needs, and retention is defined. Why: data you don't keep can't leak. Ref: ASVS V14, CWE-359
  • API responses return only the fields the client needs, not the whole model. Why: over-returned fields expose data to any curious user. Ref: API3:2023, CWE-213
  • Sensitive data is encrypted at rest where policy requires it. Why: backups and snapshots outlive access controls. Ref: A04:2025, CWE-311
  • No personal data goes to third-party analytics or LLM prompts without approval. Why: each new processor is a new place the data lives. Ref: ASVS V14

Dependencies

Use these checks for packages added, removed, or bumped in the PR.

  • Each new dependency is justified and actively maintained. Why: abandoned packages don't get security fixes. Ref: A03:2025 Software Supply Chain Failures, CWE-1104
  • Package names match the intended project, with no near-miss spellings. Why: typosquats rely on one wrong letter. Ref: A03:2025, CWE-1357
  • The lockfile is updated and versions are pinned. Why: floating versions pull in whatever ships next. Ref: A03:2025, ASVS V15
  • Install scripts in new packages have been reviewed. Why: postinstall runs on every developer laptop and CI runner. Ref: A03:2025, CWE-829
  • Licenses on new packages are compatible with your policy. Why: an incompatible license can force a rewrite or a legal fix after release. Ref: ASVS V15
  • When a new or bumped package has a known vulnerability, check whether the vulnerable function is reachable from this code before blocking or ignoring it. Why: reachability turns a CVE ID into a decision you can act on. Ref: A03:2025, CWE-1395

A CVE in a JSON parser your code never calls on untrusted input doesn't carry the same risk as one on your request path. Reachability analysis that follows call paths from your code into the dependency answers that question with evidence, so the reviewer doesn't have to guess.

References for this section: Vulnerable Dependency Management Cheat Sheet.

Configuration and infrastructure as code

Use these checks for app config, IaC, containers, and CI files in the diff.

  • Debug modes and verbose error pages are off for production. Why: debug endpoints often allow code execution. Ref: A02:2025 Security Misconfiguration, ASVS V13, CWE-489
  • CORS doesn't combine a wildcard or reflected origin with credentials. Why: that combination lets any site read authenticated responses. Ref: A02:2025, CWE-942
  • Security headers such as CSP, HSTS, and X-Content-Type-Options are set. Why: without headers, the browser allows whole classes of attacks including XSS, clickjacking, and protocol downgrades. Ref: A02:2025, ASVS V3
  • IaC creates no public storage buckets and no 0.0.0.0/0 rules on admin ports. Why: these are the misconfigurations internet scanners look for first. Ref: A02:2025, CWE-732
  • IAM policies avoid wildcard actions and resources. Why: "Action": "*" turns a small compromise into a large one. Ref: A01:2025, CWE-250
  • Containers run as a non-root user. Why: root in the container makes escape and lateral movement easier. Ref: A02:2025, CWE-250
  • CI workflows use minimal token permissions and never interpolate untrusted PR input, like titles or branch names, into run steps. Why: script injection in CI leaks secrets and write tokens. Ref: A03:2025, CWE-78

References for this section: Infrastructure as Code Security and Docker Security Cheat Sheets.

API security

Use these checks for new or changed API surfaces. Tags here also reference the OWASP API Security Top 10 2023.

  • Every new endpoint requires authentication, including internal and versioned ones. Why: /v1/ copies and "internal" APIs often lose their auth. Ref: API2:2023 Broken Authentication, API9:2023 Improper Inventory Management
  • Object-level authorization applies to every ID in the request. Why: one unchecked ID exposes another user’s record. Ref: API1:2023 Broken Object Level Authorization, CWE-639
  • Property-level authorization blocks mass assignment of fields like role or is_admin. Why: binding the whole request body to a model lets clients set anything. Ref: API3:2023 Broken Object Property Level Authorization, CWE-915
  • Request bodies are validated against a schema. Why: unexpected types and fields reach code that never planned for them. Ref: ASVS V4, CWE-20
  • Rate limits and pagination caps are set. Why: unbounded queries become denial of service and data scraping. Ref: API4:2023 Unrestricted Resource Consumption, CWE-770
  • GraphQL depth and cost limits are set, and introspection is off in production unless required. Why: nested queries can exhaust the server. Ref: API4:2023
  • Incoming webhook signatures are verified before the payload is processed. Why: anyone can POST to a webhook URL. Ref: ASVS V4, CWE-345

References for this section: REST Security, Mass Assignment, and GraphQL Cheat Sheets.

File handling

Use these checks for uploads, downloads, file paths, and parsers.

  • File paths built from input are canonicalized and confined to an expected directory. Why: ../ reads files you never meant to serve. Ref: A01:2025, ASVS V5, CWE-22
  • Archive extraction rejects entries that resolve outside the target directory. Why: zip slip writes files anywhere on disk. Ref: ASVS V5, CWE-22
  • Uploads are validated by content type and size, not file extension. Why: report.pdf.php is still PHP. Ref: ASVS V5, CWE-434
  • Uploaded files are stored outside the webroot with generated names. Why: user-chosen names and served directories lead to execution and overwrite. Ref: ASVS V5, CWE-434
  • XML parsers have external entities and DTDs disabled. Why: XXE reads local files and makes server-side requests. Ref: A02:2025, CWE-611

References for this section: File Upload and XML External Entity Prevention Cheat Sheets.

Business logic

Use these checks for abuse of intended behavior. No pattern describes these bugs, so you have to know what the feature is supposed to do.

  • Workflow state transitions are enforced server-side, so an order can't jump from cart to paid. Why: clients will call any endpoint in any order. Ref: A06:2025 Insecure Design, ASVS V2, CWE-841
  • Amounts and quantities are checked for negative values, zero, and overflow. Why: a quantity of -5 can turn a purchase into a refund. Ref: A06:2025, CWE-20
  • Balance, credit, and inventory updates are safe under concurrency. Why: two parallel requests can spend the same balance twice. Ref: A06:2025, CWE-362
  • Sensitive operations are idempotent or replay-protected. Why: replayed payment or transfer requests repeat the effect. Ref: A06:2025, ASVS V2
  • Limits such as coupon use, free trials, or invite counts can't be bypassed through an alternate endpoint. Why: limits enforced in one place get skipped through another. Ref: API6:2023 Unrestricted Access to Sensitive Business Flows

AI-generated code

AI coding agents learn from code that contains vulnerabilities, and they reproduce those patterns: hardcoded secrets, unsafe deserialization, weak crypto, and calls to APIs that don't exist. In Endor Labs' Agent Security League benchmark, over 80% of the functionally correct solutions that AI coding agents produced still contained security vulnerabilities. Every check above applies to agent-written PRs, and these few extras cover failure modes human code rarely has.

  • Every newly added package exists on its registry, with a plausible release history and a real maintainer. Why: models suggest package names that don't exist, and attackers can register those names. Ref: A03:2025 Software Supply Chain Failures
  • Security controls present in earlier iterations or required by the ticket are still in the final diff: auth middleware or decorators, input validation, CSRF protection, TLS verification, and rate limits. Why: agents drop controls while "fixing" an unrelated error, and the loss is invisible unless you compare. Ref: A06:2025, ASVS V15
  • No placeholder secrets such as sk-test-... or changeme remain in code or config. Why: placeholders tend to ship. Ref: CWE-798
  • No TODO: add auth comments or stubbed permission checks that return true. Why: a stub that compiles passes CI. Ref: A01:2025, CWE-862
  • Tests cover failure and abuse cases, not only the happy path. Why: agent-written tests often assert that the code does what the code does. Ref: ASVS V15

Reviewers miss the dropped-controls check most often. Open the original ticket or the first version of the PR, list the security-relevant pieces, and confirm each one survived. If you review agent PRs end to end, our guide to reviewing a pull request an agent wrote covers the full process.

Webinar

Give Your AI Coding Assistants the Security Tools They Deserve

Find out More

Which checks to automate and which need a person

Most pattern, dataflow, secrets, dependency, and configuration checks should run automatically on every PR, and the checks about intent, policy, and design need a reviewer. At high PR volume, human attention is the scarce resource, so spend it where a tool can't know the answer.

Run automatically on every PR Needs a person's judgment
Injection dataflow: SQL, command, XSS, SSRF, deserialization (Input validation and injection) Whether the authorization model matches the business rule (Authorization and access control)
Unscoped object lookups and missing ownership checks along traced data paths (Authorization and access control) Tenant isolation design for new data models and shared resources (Authorization and access control)
Hardcoded credentials and secrets in history (Secrets) Which new fields count as sensitive, and retention decisions (Data protection and privacy)
Known-vulnerable, unmaintained, typosquatted, and nonexistent packages, plus reachability (Dependencies, AI-generated code) Whether a new dependency is worth its risk (Dependencies)
Crypto API misuse: weak algorithms, static IVs, non-CSPRNG, disabled TLS verification (Cryptography) Workflow abuse, limit bypass, and race-sensitive flows (Business logic)
IaC, container, CORS, debug, and CI workflow misconfigurations (Configuration and infrastructure as code) Whether data should go to a third party or an LLM at all (Data protection and privacy)
JWT verification, cookie flags, and session rotation patterns (Session and token handling) Whether an exception to a policy is acceptable (any area)
Path traversal, XXE, and upload handling patterns (File handling) Whether controls dropped across agent iterations were required (AI-generated code)

Pattern-matching SAST is noisy on syntax and blind to logic flaws like IDOR, authentication bypass, and business logic errors, because no regex describes "this user doesn't own this record." Dataflow-aware AI analysis can trace a request parameter through several files to an unscoped query and flag it, which puts many object-level authorization checks in the automated column. A tool still can't decide whether a support agent should see every tenant's records. That's a policy question, and it stays with a person. For the mechanics of how automated review reaches design and logic flaws, read our whitepaper on detecting security design flaws.

Copy-paste PR template

On GitHub, save this as .github/pull_request_template.md. On GitLab, save it in .gitlab/merge_request_templates/ (for example, Security.md), then pick it when you open a merge request or set it as the project default.

## Security review

Does this PR touch auth, data access, secrets, dependencies, or infra?
If yes, complete the relevant sections. Mark untouched sections N/A.

### Input and injection
- [ ] Queries are parameterized; no string-built SQL or shell commands
- [ ] Output is encoded for its context; user-supplied URLs are allowlisted
- [ ] No untrusted data reaches unsafe deserializers

### Authentication and sessions
- [ ] New routes require auth by default
- [ ] Session ID rotates on login; cookies set HttpOnly, Secure, SameSite
- [ ] JWTs: signature verified, `none` rejected, exp/aud/iss checked

### Authorization
- [ ] Server-side check on every new route or resolver; deny by default
- [ ] Records fetched by ID are scoped to the caller
- [ ] Tenant ID comes from the session, never the request
- [ ] List, search, export, jobs, and webhooks filter or re-check ownership

### Crypto and secrets
- [ ] No custom crypto; CSPRNG for tokens; TLS verification on
- [ ] No hardcoded credentials anywhere; committed secrets rotated

### Errors, logging, and data
- [ ] No internal detail in user-facing errors; auth errors fail closed
- [ ] Security events logged; no secrets or personal data in logs
- [ ] Responses return only needed fields; new sensitive fields classified

### Dependencies and config
- [ ] New packages exist, are maintained, and have the expected name
- [ ] Vulnerable functions in new or bumped packages checked for reachability
- [ ] No debug mode, wildcard CORS with credentials, public buckets, or wildcard IAM
- [ ] CI workflows use minimal permissions; no untrusted input in run steps

### AI-generated changes
- [ ] Security controls from earlier iterations or the ticket are still present

Trim or add to the template to match your stack, and keep the trigger line at the top so authors know when it applies.

How Endor Labs AI Security Review automates the checklist

Endor Labs AI Security Review runs the automated column of this checklist on every pull request. It applies the Endor Labs AI SAST engine to each change, with detection agents that trace how data moves across files and functions. That tracing catches broken access control, IDOR, injection, and insecure design, which map to the Authorization, Input validation, and Business logic sections above.

A triage agent labels each finding as true positive, false positive, or unknown, with reasoning your team can audit. Remediation agents propose a fix matched to your codebase for every confirmed vulnerability. The same PR scan covers the dependencies the change modified (SCA) and any secrets it introduced, and Secrets Detection validates whether a leaked credential is live or revoked, which tells you how urgently to rotate it.

Results post as PR comments and connect to your CI/CD pipeline and policy engine on GitHub, GitLab, and Bitbucket. After rollout, fewer than 10% of PRs need human security review, which leaves reviewers time for the right-hand column: authorization models, tenant design, privacy decisions, and workflow abuse.

FAQs

What's the difference between a secure code review checklist and a secure coding checklist?

A secure code review checklist is applied to a diff during review, and a secure coding checklist guides developers while they write code. The review version asks whether something is present in the change, and the coding version says how to write it. Many teams use both: the coding checklist sets expectations, and the review checklist verifies them on each PR.

Should I use the OWASP Code Review Guide or OWASP ASVS as my checklist source?

Use OWASP ASVS as the source for checklist items and tags, and the OWASP Code Review Guide for background on how to review. ASVS 5.0 is a maintained verification standard with numbered requirements, which makes it easy to trace review coverage for auditors. For coding checklists, the OWASP Developer Guide is the current home.

Can ChatGPT or another AI tool run a secure code review?

A general-purpose chat model can review a pasted diff and flag common patterns, but it works without your repository context. It doesn't see the middleware three files away, the ORM scope helper, or how your tenant ID gets set, which leads to missed cross-file flaws and confident false positives. Tools built for PR review trace data across the whole codebase and show their reasoning. Neither kind replaces a person for the intent and policy checks in the right-hand column above.