AI code review security is the practice of reviewing AI-assisted pull requests for security regressions before they merge. The checklist should catch newly introduced vulnerabilities, but it also needs to catch something easier to miss: security controls that used to exist and disappeared in the diff.
That second category is where AI-assisted refactors get dangerous.
A PR can look clean. Tests can pass. The route can still return data. But the new version may have quietly removed:
- an auth decorator
- a permission branch
- a tenant filter
- input validation
- rate limiting
- CSRF protection
- audit logging
- secret handling
That is why teams using Cursor, Claude Code, GitHub Copilot, Codex, or other AI coding tools need a PR security checklist that starts with the diff, not just the final file state.
TL;DR: the AI code review security checklist
Use this checklist before approving an AI-generated or AI-assisted PR:
| Check | What to look for | Why it matters |
|---|---|---|
| Auth still exists | Deleted decorators, middleware, guards, session checks, or role checks | Missing auth is often a broken access control issue |
| Tenant scoping still exists | Removed tenant_id, org_id, workspace, account, or owner filters | Cross-tenant data leaks often look like normal query refactors |
| Validation still runs | Validation moved after use, deleted schemas, weaker serializer checks | Valid shape is not the same as safe input |
| Rate limits still apply | Removed rate-limit decorators or gateway config | Login, export, webhook, and AI endpoints become abuse surfaces |
| CSRF and CORS did not weaken | Removed CSRF middleware, wider CORS origins, disabled same-site policy | Browser-facing routes can become exploitable without code looking obviously dangerous |
| Audit logging still records sensitive actions | Deleted security logs around admin, billing, export, auth, or permission changes | Incident response gets worse when logs disappear |
| Secrets were not added | API keys, tokens, passwords, private URLs, debug config | AI tools often fill examples with realistic placeholders |
| Imports and calls are real | Hallucinated packages, phantom helper functions, nonexistent APIs | Plausible names can hide broken or unsafe code paths |
| Negative tests exist | Tests for unauthenticated, unauthorized, cross-tenant, invalid input, and rate-limit behavior | Happy-path tests can pass while controls are gone |
| CI blocks high-confidence regressions | Static analysis, diff-aware scan, and PR gate | Reviewer memory does not scale with AI-generated volume |
Why AI-assisted PRs fail differently
AI coding tools make it cheap to generate large, plausible changes. That changes the review problem.
Before AI, the reviewer usually asked:
What bug did this developer introduce?
With AI-generated code, the reviewer also has to ask:
What protection did this refactor delete?
The adoption numbers explain why this is becoming a normal engineering problem, not a niche concern. Stack Overflow's 2025 Developer Survey reported that 84% of developers use or plan to use AI tools, while 46% said they do not trust the accuracy of AI-tool output. Veracode's 2026 GenAI Code Security Report found an average security pass rate of 56% across more than 100 models tested since 2023.
Those numbers do not mean every AI-generated PR is unsafe. They mean human review and automated gates need to adapt.
The highest-risk misses are not always obvious injection sinks or eval() calls. They are often deleted controls in ordinary application code.
OWASP's 2025 Top 10 still lists Broken Access Control as A01. That maps directly to the failures this checklist is trying to catch: missing permission checks, IDOR-style data access, and APIs that expose protected operations without the expected server-side enforcement.
A concrete example: tenant isolation removed by a clean refactor
This is the kind of AI-assisted PR that can pass a quick review.
Before:
@router.get("/customers/{customer_id}")
async def get_customer(
customer_id: str,
user: User = Depends(require_user),
db: Session = Depends(get_db),
):
return (
db.query(Customer)
.filter(Customer.id == customer_id)
.filter(Customer.tenant_id == user.tenant_id)
.one()
)
After:
@router.get("/customers/{customer_id}")
async def get_customer(
customer_id: str,
db: Session = Depends(get_db),
):
return (
db.query(Customer)
.filter(Customer.id == customer_id)
.one()
)
The new code is shorter.
The endpoint still works.
A happy-path test can still pass.
But two controls are gone:
require_userCustomer.tenant_id == user.tenant_id
That is no longer the same security model. It is now possible for the route to return a customer record without proving the caller belongs to the same tenant.
This is why AI code review security needs to prioritize before-and-after evidence. The danger is not only what the new code contains. It is what the new code no longer contains.
The checklist in detail
1. Did auth or permission checks disappear?
Start here. Do not read helper extraction first. Do not review formatting first.
Search the diff for removed:
@login_required@permission_requiredDepends(require_user)current_useris_adminhas_permissioncan_readcan_writerequire_rolerequire_scope
Then ask:
- Does every protected route still prove who the caller is?
- Does every sensitive action still prove what the caller can do?
- Did the check move to a shared layer, or did it simply disappear?
Treat any AI reviewer, Copilot code review and Claude Code Review included, as a useful assistant, not the final security authority. Its findings can vary between runs, and Claude Code Review's check, for example, always completes as neutral, so it never blocks a merge on its own.
2. Did tenant scoping disappear?
Tenant scoping is one of the easiest controls to lose in a refactor because it often looks like "query cleanup".
Look for deleted filters involving:
tenant_idorg_idorganization_idworkspace_idaccount_idteam_idowner_idproject_id
High-risk patterns:
# Before
query = query.filter(Document.tenant_id == user.tenant_id)
# After
query = query.filter(Document.id == document_id)
// Before
await db.invoice.findFirst({
where: { id, organizationId: session.organizationId },
});
// After
await db.invoice.findFirst({
where: { id },
});
If a PR touches a query for customer data, billing records, exports, files, admin actions, or AI context retrieval, tenant scoping should be reviewed as a first-class security control.
3. Did validation move after the dangerous operation?
AI refactors often move logic into helpers. That is fine until validation moves after the value is used.
Look for:
- request body schemas removed
- serializer validation weakened
allow_anystyle configs added- regex or allowlist validation deleted
- URL validation replaced with plain string checks
- file extension checks replacing content-type checks
Bad pattern:
def import_webhook(payload: dict):
process_webhook(payload)
validate_webhook(payload)
The validation exists, but it is too late.
4. Did rate limiting disappear?
Rate limits are easy to delete because they often look like non-business logic.
Review this especially on:
- login
- password reset
- signup
- token refresh
- export endpoints
- webhook endpoints
- expensive search
- AI chat or agent endpoints
Look for removed:
@rate_limitlimiter.limitthrottle- gateway annotations
- queue caps
- token or spend caps
For LLM-powered products, rate limits are both a security control and a cost-control boundary.
5. Did CSRF, CORS, or browser security weaken?
This matters when the PR touches routes, middleware, cookies, sessions, forms, or frontend-backed APIs.
Look for:
- CSRF middleware removed
csrf_exemptadded- CORS widened from one origin to
* - cookies changed away from
HttpOnly,Secure, orSameSite - security headers removed
- auth moved from server-side cookies to local storage without a threat model
Not every API needs CSRF protection in the same way. But every weakening should be intentional and documented.
6. Did audit logging disappear?
Deleted logging can be a security regression even when runtime behavior is unchanged.
Sensitive actions should usually leave an audit trail:
- admin role changes
- permission changes
- billing changes
- customer data exports
- API key creation
- password reset
- user impersonation
- data deletion
- security policy changes
If the PR removes an audit event, the reviewer should ask why.
7. Did the PR add secrets or fake examples?
AI tools often produce realistic examples:
OPENAI_API_KEY = "sk-proj-example"
DATABASE_URL = "postgres://admin:password@prod-db.internal/app"
JWT_SECRET = "change-me"
Even placeholder-looking secrets are a problem because placeholders get copied, reused, and forgotten.
For local scanning, Skylos can check for secrets, dangerous code and AI-generated mistakes before the PR gets reviewed:
pipx install skylos
skylos . --danger --secrets --quality --ai-defects
8. Did the AI hallucinate imports, packages, or helpers?
This is common in Python and JavaScript/TypeScript.
Look for:
- packages not in
requirements.txt,pyproject.toml,package.json, or lockfiles - helper functions with plausible names but no definition
- library methods that do not exist in your installed version
- code comments saying "assume this exists"
This is both a reliability problem and a supply-chain problem. A hallucinated import can become a malicious dependency risk if someone installs a similarly named package just to make the code run.
With --ai-defects, Skylos flags imports of packages that don't exist on PyPI (SKY-D222) and third-party imports your dependency files don't declare (SKY-D223). These are reported, not blocking: the default --gate allows up to 10 AI-defect findings, so use skylos . --select SKY-D222,SKY-D223 --strict in CI if they must fail the PR. If the code comes from a coding agent, skylos agent install-hooks blocks installs of packages that don't exist before they run.
For a deeper workflow, see How to Verify AI-Generated Python Code and Catch Hallucinated Imports.
9. Did tests cover the security-negative path?
Happy-path tests are not enough for AI-assisted PRs.
For a protected route, require tests for:
- unauthenticated request
- authenticated but unauthorized request
- cross-tenant request
- invalid input
- missing required scope
- rate-limit behavior where relevant
The test that matters for the earlier tenant example is not:
def test_get_customer_returns_200():
...
It is:
def test_get_customer_rejects_cross_tenant_access():
...
10. Is there a CI gate, or is this all reviewer memory?
Manual review is a weak control if the same issue appears every week.
Use the manual checklist to learn the patterns. Then move the high-confidence checks into automation.
For Skylos:
skylos . --danger --secrets --quality --ai-defects
skylos defend .
skylos debt .
For pull requests, scan only the files the branch changed:
skylos . --danger --secrets --quality --ai-defects --diff-base origin/main
This scan reports a deleted @login_required as SKY-L021 (we checked on Skylos 4.47.1). The default gate doesn't fail on it, and the changed-line gate that skylos cicd init generates doesn't report it at all, so use the removed-controls job if a removed control must block the merge. If an agent wrote the change, skylos done --base origin/main checks that tests weren't deleted, skipped or weakened (how).
For a full local bundle:
skylos suite .
To upload suite results to Skylos Cloud as separate scan families:
skylos suite . --upload
If you want the GitHub Actions workflow, see Python Security Scanner for GitHub Actions. For removed auth checks specifically, see How to Catch Removed Auth Checks and Security Regressions in AI-Generated PRs; for a hardened workflow for agent pull requests, see GitHub Actions PR Gate for AI-Generated Code. Reference: Skylos CI/CD docs.
Where static analysis fits
Static analysis is still necessary. The question is what job each tool is doing.
CodeQL is strong for semantic analysis and GitHub code scanning. Semgrep taint analysis is useful for tracking untrusted data from sources to sinks. Snyk, SonarQube, and other AppSec tools cover broader platform workflows.
But AI code review security adds another question:
Did this PR remove a security control that used to exist?
That is why diff-aware scanning matters. A whole-repo scan can tell you what exists now. A diff-aware review can tell you what disappeared.
Skylos is designed to sit beside those tools as a local-first PR guardrail, especially for:
- modeled auth, validation, rate-limit, and other named control removals
- dead code and cleanup risk
- AI-generated code verification
- LLM app defense checks
- technical debt hotspots
- CI and optional cloud upload workflows
Tenant filters, billing rules, arbitrary CORS changes, and other business controls still need targeted tests and human review unless the repository defines an explicit contract for them.
For a broader tool comparison, read Best AI Code Security Tools in 2026 Compared.
The minimum review policy for AI-assisted PRs
If you want a simple policy, use this:
- Any PR touching auth, billing, admin, exports, tenants, files, webhooks, or AI endpoints gets security review.
- The reviewer checks deleted controls before reviewing new helper code.
- Cross-tenant negative tests are required for tenant-scoped data.
- High-confidence static findings block merge.
- Removed-control findings (SKY-L021) block merge, through a CI step that fails on them.
- Reviewer comments that repeat twice become CI rules.
That last point matters. The goal is not to turn senior engineers into permanent AI babysitters. The goal is to convert repeated review pain into automated gates.
Running this across a team? These checks are free in the CLI. Skylos Cloud keeps the results in one place: the Free plan adds a pass/fail check on pull requests and scan history for one project, and the Workspace plan adds up to 10 projects, approvals for findings you decide not to fix, pull request comments, and audit exports.
Final takeaway
AI code review security is not about distrusting every AI-generated line. It is about recognizing that AI-assisted PRs create a new review shape.
The risky change may be the thing that disappeared.
Start with the manual checklist:
- auth still exists
- tenant scoping still exists
- validation still runs before use
- rate limits still apply
- CSRF, CORS, cookies, and headers did not weaken
- audit logging still records sensitive actions
- secrets were not added
- imports and helpers are real
- negative tests cover the security path
Then enforce the repeatable parts with local scanning and CI.
Skylos is open source and local-first. Try it here: github.com/duriantaco/skylos.
pipx install skylos
skylos . --danger --secrets --quality --ai-defects
skylos . --danger --secrets --quality --ai-defects --diff-base origin/main
If your team wants one command for static analysis, AI defense, technical debt, and optional cloud upload:
skylos suite .
skylos suite . --upload