Reviewing a Security-Critical Change

The interviewer hands you a diff for a service that moves money and asks “what's missing?” The senior answer is a checklist you run, not a vibe you have.

Student edition · understand, apply, verify

Review threats, object-level authorization, input boundaries, retries, and rollout before trusting a transaction path.

What you should be able to do: Prioritize material security/privacy/correctness gaps with evidence and a safe concrete repair.

A context-dependent practice guide, not a universal policy or verified personal history. Examples and numbers are illustrative. Use genuine, authorized evidence; personnel/privacy/legal decisions follow applicable organization policies and qualified review.

Concepts explained in this guide

Read these definitions first, or use the linked terms as you go. The examples and limitations are local to this guide; companion pages are optional.

Authentication, authorization, and IDOR

Authentication establishes who is calling. Authorization determines what that principal may do to a particular object. IDOR/BOLA occurs when a caller can use an object ID to access or mutate another principal's data.

Example. A parameterized query safely treats an input as data, but still needs an owner/permission predicate from trusted authentication context.

Boundary. Never accept caller identity solely from request fields. Input validation, authentication, per-object checks, least privilege, and race-safe policy enforcement are separate controls.

Privacy, minimization, and pseudonymity

Personal data can identify or link a person directly or with other information. Minimize data to an approved purpose. Pseudonymity replaces an identifier but leaves possible linkage; anonymity requires a much stronger contextual assessment.

Example. Hashing an email preserves a stable value and may be dictionary-reversed. HMAC can reduce that attack for parties without the key, but remains linkable pseudonymous data.

Boundary. Legal basis, retention, rights/exceptions, audit access, and deletion of derived/backup copies require qualified context-specific review. Encryption or hashing alone is not compliance.

Idempotency keys

A client-generated identity for one logical operation. The server binds it to caller scope, endpoint, request fingerprint, state, and result. A retry uses the same key and reuses the original outcome rather than repeating the effect.

Example. Payment p42 times out after the provider charged it. Retrying with p42 lets the provider return the same charge instead of creating a new one.

Boundary. A unique local claim is insufficient if the business effect and saved result can diverge. Keep unknown outcomes pending, use downstream idempotency, and state the retention/retry horizon.

Optimistic concurrency and compare-and-set

Update state only if the expected version or status still matches, in the same atomic operation that changes it. A failed comparison means a conflicting operation won; reread or return a conflict.

Example. UPDATE seats SET status='held', version=version+1 WHERE id=? AND status='available' allows only one concurrent claimant to update the row.

Boundary. Application-level GET then unconditional SET is not equivalent. Include acquisition identity to reject stale callbacks, and couple related invariants in a transaction.

Preconditions and postconditions

A precondition states required inputs/state before an operation. A postcondition states the result promised when those requirements hold and execution terminates normally. Together they make requirements testable.

Example. For sum_first(b,n), require integer 0≤n≤len(b) and numeric elements; promise the sum of b[0:n].

Boundary. State exceptions, mutation, numeric domains, and empty cases. A mathematical array range does not automatically validate a runtime input.

This is the depth question in the code-review track. You're handed a pull request for a microservice that handles secure transactions — a payments path, a consent write, a credential exchange — and asked what a senior reviewer would flag. The trap is to comment on style and a missing null check and stop. The signal is that you run the change through a repeatable security-and-correctness checklist, name the classes of risk that would cause a SEV or a breach, and then describe how you'd deliver that feedback so it lands and the diff still ships. What follows is the checklist a senior reviewer runs, plus a wrong-versus-right snippet, plus how to give the feedback.

The security-critical review checklist: tests and failure paths, error handling, concurrency, secrets, authn/authz, input validation, idempotency, encryption, no PII in logs and an audit trail, rate limiting, dependencies, safe rollout
The checklist a senior reviewer runs on a security-critical change: missing tests on the failure paths, unhandled errors, concurrency on shared state, secrets handling, authn/authz, input validation, idempotency for money movement, encryption, PII in logs plus an audit trail, rate limiting, supply-chain risk, and a safe rollout.

What this question is really testing

Whether you have an internalized threat model for code that touches money, secrets, or user data — and the discipline to apply it the same way every time rather than relying on catching something clever in the moment. The interviewer wants to hear that you review for the classes of failure that hurt (a breach, a double-charge, a leaked secret, a data-corruption SEV), that you weight the failure and error paths as heavily as the happy path, and that you can prioritize: what actually blocks the land versus what's a follow-up. They're also listening for whether you'd deliver a security block without shaming the author, because a reviewer who's feared gets routed around.

How to answer What the interviewer is looking for

The checklist a senior reviewer runs

Group your findings the way you'd group them in the review so the author can act on them. Everything above the fold blocks a land; the operational hardening is where you separate must-fix from fast-follow.

AreaWhat you look forWhy it’s here
Tests & failure pathsUnit and integration tests that exercise the error branches, retries, and partial failures — not just the happy pathThe bugs that hurt live in the paths no one tested
Error handlingNo unhandled exceptions; no bare except; errors surfaced, not swallowed; resources released on the failure pathA swallowed error in a money path is a silent data bug
ConcurrencyRaces on shared mutable state; check-then-act on balances; correct locking or an atomic compare-and-setTwo concurrent requests must not both spend the same funds
Secrets & credentialsNo hardcoded secrets or keys; pull from a secrets manager; least-privilege scopes; rotation possibleA committed secret is a breach waiting in git history
AuthN / AuthZCaller is authenticated and authorized for this resource; no missing object-level checks (IDOR)Authn without per-object authz is the classic escalation
Input validation & injectionValidate and bound all external input; parameterized queries; no shell string-building; no unsafe deserializationSQL, command, and deserialization injection all start here
IdempotencyMoney movement carries an idempotency key; a retry or duplicate delivery cannot double-applyAt-least-once delivery means retries will happen
EncryptionTLS in transit; sensitive fields encrypted at rest; correct algorithms, no home-rolled cryptoTransactions and PII must be protected on the wire and on disk
PII in logs & auditNo card numbers, tokens, or PII in logs or error messages; a tamper-evident audit trail of who did whatLogs leak, and money movement needs a forensic record
Rate limiting & abusePer-caller limits, bounded retries with backoff, and abuse/fraud guards on the endpointAn unbounded money endpoint is a fraud and DoS surface
DependenciesNew libraries vetted, pinned, and scanned; no unmaintained or typo-squatted packagesSupply-chain risk enters through a one-line import
Safe rolloutBehind a gate/flag; canary or staged ramp; a fast, tested rollbackYou want to turn a bad change off in minutes, not redeploy

Wrong versus right

The runnable lab uses synthetic data and no credentials. A complete production payment handler additionally needs trusted authentication, per-object authorization, amount/currency validation, a transaction/provider idempotency contract, durable unknown-outcome reconciliation, classified audit policy, and recovery tests.

# Architecture contract, not a production handler:
principal = trusted_authentication_context()
validate_amount_currency_and_operation_identity()
authorize(principal, requested_account)
execute_local_transaction_or_provider_keyed_operation()
# Timeout: persist unknown/reconciling; do not claim the effect failed.
# Diagnostics omit sensitive data; authorized audit is separately governed.

Common follow-ups

You have limited time — what do you check first?

How to answer

How do you review the parts you're not a security expert in?

How to answer

The author says the missing tests will come in a follow-up diff. Do you block?

How to answer

How do you deliver a hard security block without alienating the author?

How to answer
Evidence to collect with your approved tools

Worked example: authenticated is not authorized

This is an illustrative situation. The point is the reasoning and evidence, not copying the numbers or first-person story.

authenticated is not authorizedAuthenticate the principal: Trusted middleware establishes caller identity; do not accept caller/owner identity from an arbitrary payload.. Authorize the object: The lookup/update includes the caller's permitted object scope. SQL parameters prevent injection but do not themselves provide authorization.. Check atomic money/retry semantics: A conditional debit and durable idempotency outcome share their boundary; remote timeout remains unknown until reconciled.. Inspect secrets/logging/rollout: No embedded credential examples; diagnostic identifiers can still be classified personal data. Missing material log/privacy controls are not automatically harmless fast-follows.1Authenticate the principal2Authorize the object3Check atomic money/retry semantics4Inspect secrets/logging/rollout
Sequence for this example. The table explains the evidence and decision at each step.
StepEvidence, decision, and expected result
Authenticate the principalTrusted middleware establishes caller identity; do not accept caller/owner identity from an arbitrary payload.
Authorize the objectThe lookup/update includes the caller's permitted object scope. SQL parameters prevent injection but do not themselves provide authorization.
Check atomic money/retry semanticsA conditional debit and durable idempotency outcome share their boundary; remote timeout remains unknown until reconciled.
Inspect secrets/logging/rolloutNo embedded credential examples; diagnostic identifiers can still be classified personal data. Missing material log/privacy controls are not automatically harmless fast-follows.
Check your understanding: Does a parameterized query fix IDOR?

No. IDOR is unauthorized object access; include trusted-principal authorization and object scope in addition to parameterization.

Run and change the example

Download guide_labs.py. Python 3.10+, standard library only. In its directory, run:

python3 guide_labs.py security
python3 guide_labs.py test

The synthetic SQLite lab checks parameterized values and caller-scoped object lookup, including an injection-like string and a different owner.

The program tests its stated finite contracts. It is an educational model, not an implementation of the named production product or a proof of unmodeled distributed faults.

Sources and further verification
⌂