Most security incidents do not come from exotic zero-days. They come from ordinary pull requests that nobody read closely: a missing signature check on a webhook, a query built with string concatenation, a token logged by accident. The PR is the last cheap place to catch these. After merge, the same bug costs a hotfix, an incident review, and sometimes a customer notification.
This guide covers what to look for in a diff, why human review alone keeps missing it, and how to make a security pass happen on every pull request rather than only the ones a senior engineer has time for.
Why security bugs slip through normal review
Code review is optimised for "does this do what the ticket says". Reviewers read the happy path, check the tests, and approve. Security bugs live off the happy path:
- They are absences, not mistakes. A missing authorisation check produces no diff line to comment on. You have to notice what is not there.
- They hide in boring files. Webhook handlers, config loaders, and logging helpers rarely get a careful read.
- Reviewers are busy. When the queue is long, "LGTM" is the path of least resistance.
- Knowledge is uneven. Not every reviewer knows what a timing-safe comparison is or why
evalon user input is a problem.
Linters do not fix this. A linter catches formatting and a handful of known-bad calls. It has no idea that the new endpoint touches billing data and should require an admin role.
The review checklist
Run through these for every PR that touches a request handler, a database query, an integration, or anything that reads configuration.
1. Inputs and injection
- Is every value from a request, webhook, queue message, or file treated as untrusted?
- Are database queries parameterised, or is anything concatenated into SQL, shell commands, or LDAP filters?
- Are paths built from user input checked for
..traversal? - Does HTML rendering escape user content, or is anything passed through
dangerouslySetInnerHTMLor its equivalent?
2. Authentication and authorisation
- Does the new endpoint sit behind the same auth guard as its neighbours?
- Is the object being accessed checked against the caller, not just the caller's login? Insecure direct object reference is still the most common web bug:
GET /invoices/123where 123 belongs to another tenant. - Are role checks enforced on the server, or only hidden in the UI?
3. Secrets and tokens
- Are API keys, private keys, or passwords read from environment or a secret store, never committed?
- Is anything sensitive written to logs, error messages, or analytics events?
- Are tokens compared with a constant-time function? A plain
===on an HMAC leaks timing. - Are stored credentials encrypted at rest, and is the encryption key itself managed properly?
4. Integrations and webhooks
- Is the webhook signature verified before the payload is parsed or acted on?
- Is there replay protection, such as an idempotency key or delivery ID?
- Are outbound calls to third parties retried safely, without duplicating side effects like charges?
5. Data handling
- Does the change widen what a query returns? A new
includeorselect *can leak columns that were never meant to leave the database. - Is personal data minimised and deleted on the schedule your policy promises?
Making it happen on every PR
A checklist is only useful if someone applies it, every time, including at 6pm on a Friday. That is where automated first-pass review earns its place.
The pattern that works:
- An automated reviewer reads the diff the moment the PR opens. It runs the checklist above, plus general quality checks, and posts findings inline on the exact lines.
- Security findings are tagged and weighted. A missing signature check should look different from a naming nitpick. In DiffNova, security findings carry a separate flag, add an extra penalty to the PR score, and trigger a high-severity email so a human sees them even if nobody is watching the PR.
- Humans review the findings, not the raw diff. The senior engineer starts from a triaged list. They spend their time on design and on the two findings that need judgment, not on re-deriving what a machine can spot.
- The score makes the trend visible. A repository whose security-weighted score drops week over week is telling you something before an incident does.
What to expect from an AI reviewer
Be realistic. An AI reviewer reading a diff will reliably flag:
- Unverified webhook signatures and missing auth guards on new routes
- String-built queries and shell commands
- Secrets in code, tokens in logs
- Obvious IDOR patterns where an ID is used without an ownership check
- Unsafe deserialisation,
eval, and HTML injection points
It will not replace a threat model or a penetration test. It sees the diff, not your whole architecture. What it does do is guarantee that the checklist runs on every PR, with no reviewer fatigue, at a cost of a few cents per review.
Start with the repositories that matter
You do not need to switch on automated security review everywhere on day one. Start with the repositories that handle authentication, payments, or personal data. Turn on review-on-every-commit for those, keep PR-open-only for the rest, and watch the scores for a fortnight. The findings you see in the first week are usually enough to justify the rest.
DiffNova does exactly this as a GitHub App: inline security-tagged findings on every PR, a security-weighted score per repository and contributor, and a free 7-day trial to see it on your own code.
