Verifying AI-Generated Code Before You Push
Use Case: Verifying AI-Generated Code Before You Push
The everyday problem: You (or your AI assistant) just generated a working feature — it runs, the demo passes, you're ready to commit. But AI-generated code carries a distinct set of risks that "it works" never reveals: a real API key left in a config file, a dependency the model invented that an attacker has since registered as malware, a vulnerable package version, insecure crypto the model defaulted to. Research in 2026 found AI-generated pull requests contain roughly 1.7× more issues than human-only ones, and that around 20% of AI-suggested package names don't exist on the registry — a brand-new attack surface. The challenge is verifying AI-generated code before it leaves your machine, because the cheapest place to catch these problems is locally, before the push — once a secret is in git history it is compromised even after you delete it, and once a hallucinated package is installed the damage may already be done.
This playbook is a pre-push routine: a set of independent checks, each targeting one class of AI-generated mistake, each with a real, free tool and the exact command to run it on Windows. The checks are ordered by how dangerous and how reliably-catchable the problem is. You don't need all of them every time, but together they form a complete gate. The final section is the honest part most guides skip: what these tools cannot check, and what you must verify yourself.
A note on honesty and scope: the commands below are drawn from each tool's current official documentation and reflect 2026 usage. Tool syntax changes (Gitleaks, for example, recently moved from gitleaks detect to gitleaks git / gitleaks dir) — so where a command is version-sensitive it is flagged, and you should confirm against the tool's current docs when you run it. Treat this as a verification-grounded routine you run and confirm yourself, not a guarantee that every flag behaves identically on every version.
The quick checklist (the whole routine at a glance)
Before you push AI-generated code, run through these. Each has a dedicated section below.
Prefer a quick tick-off version? Use the interactive checklist →
- 1. Leaked secrets — did a real key/password end up in the code? →
gitleaks - 2. Hallucinated dependencies — did the AI invent a package name? →
slopcheck/slop-scan - 3. Vulnerable dependencies — are any installed packages known-vulnerable? →
npm audit/pip-audit - 4. Insecure code patterns — injection, weak crypto, unsafe defaults? →
semgrep - 5. Committed config/artifact leaks — is a
.envor build artifact about to be pushed? → git + gitleaks - 6. What tools can't check — auth, business logic, privacy, architecture → manual review
Check 1: Leaked Secrets (Gitleaks)
The problem: AI assistants read your whole workspace for context and will happily write a real key directly into code, or replace a placeholder like "your-api-key-here" with a real value inline instead of using an environment variable. Because the model doesn't distinguish "this should be a secret" from "this is just a string," credentials end up hardcoded. Once committed, a secret lives in git history permanently — deleting the line later does not remove it.
The tool — Gitleaks: a fast, free, open-source secret scanner (written in Go) that detects hardcoded credentials by matching against a large set of known key formats. It can scan your working directory, your git history, or run as a pre-commit/pre-push hook.
Install on Windows (any one of these):
# Option A — Scoop (recommended on Windows) scoop install gitleaks # Option B — Chocolatey choco install gitleaks # Option C — download the Windows binary from the releases page and add it to PATH: # https://github.com/gitleaks/gitleaks/releases
Run it (note the current subcommands — dir scans files as-is, git scans history):
# Scan the current folder's files as-is (no git history needed) — best for a quick pre-push check gitleaks dir . --verbose # Scan the git history of the repo (catches secrets committed earlier) gitleaks git . --verbose # Output a report you can review or feed into other tools gitleaks dir . --report-format json --report-path gitleaks-report.json # NOTE: older guides say `gitleaks detect` — that is the OLD syntax. Current versions use # `gitleaks git` and `gitleaks dir`. Run `gitleaks --help` to confirm what your version supports.
Try it yourself (prove it works):
# 1. In a test folder, create a file with a FAKE but realistic-looking key: echo const key = "sk-ABCD1234ABCD1234ABCD1234ABCD1234ABCD1234ABCD1234"; > test-leak.js # 2. Scan: gitleaks dir . --verbose # 3. You should see a finding for test-leak.js identifying an OpenAI-style key. # Delete the test file afterward. (Use clearly fake values — never a real key.)
If it finds something: remove the secret from the file, move it to an environment variable (a .env file that is gitignored, or your platform's secret store), and — critically — rotate the key (regenerate it at the provider), because if it was ever committed or shared it must be considered compromised. Don't investigate first then rotate; rotate first.
Honest limits: Gitleaks matches patterns, so it produces false positives on test/placeholder values and example keys, and it can miss a secret in an unusual format. It is excellent at the common, dangerous cases (provider API keys, private keys, connection strings) — treat its findings as "verify each," not "every one is a live breach."
Check 2: Hallucinated Dependencies / Slopsquatting (slopcheck)
The problem: This is the most distinctly-AI risk, and it's serious. AI models confidently suggest installing packages that do not exist. Research presented at USENIX Security 2025 found ~20% of AI-generated code references non-existent packages, and that the same hallucinated names recur consistently across prompts — making them predictable. Attackers register those exact phantom names as malware and wait ("slopsquatting"). A real example: a hallucinated package react-codeshift spread through 237 repositories via AI-generated agent files in January 2026 with nobody deliberately planting it. If you run an npm install the AI suggested without checking, you can install attacker code directly.
The tools: a small category of free CLIs exists specifically for this — they parse your dependency files and verify each package name against the real registry. Good options (all open-source, 2026):
slopcheck— checks names against npm, PyPI, crates.io and Go modules; coverspackage.json,requirements.txt,pyproject.toml, and more. Useful because your projects span Node and Python.slop-scan— annpxtool / GitHub Action that parses code and docs fornpm install,npx,require(), andimportstatements and verifies the names against the npm registry.
Install & run on Windows:
# slop-scan — no install needed, runs via npx (requires Node.js 18+) npx slop-scan . # slopcheck — install globally, then auto-detect dependency files in the current project npm install -g slopcheck slopcheck . # auto-detects ecosystem (package.json = npm, etc.) slopcheck requirements.txt # scan a specific Python file # slopcheck can also gate an install — verify names BEFORE actually installing: slopcheck install express lodash some-ai-suggested-package # (clean packages install normally; hallucinated/suspicious ones are blocked)
Try it yourself:
# 1. Add an obviously-fake package the AI "might" hallucinate to a test package.json:
# "dependencies": { "react-super-helper-9000": "^1.0.0" }
# 2. Run:
npx slop-scan .
# 3. It should flag react-super-helper-9000 as not found on the registry.
If it flags a package: do not install it. Check the real package name on npmjs.com (or pypi.org) directly — find the legitimate package the AI was approximating, or implement the small helper yourself. Never install a name just because the AI suggested it; verify existence, download counts, maintainer history, and last-publish date first.
Honest limits: these tools confirm a package exists and flag suspicious newness/low adoption — they do not prove an existing package is safe (a real package can still be compromised). They close the specific slopsquatting gap, not all supply-chain risk. Pair with Check 3.
Check 3: Vulnerable Dependencies (npm audit / pip-audit)
The problem: Beyond hallucinated packages, AI often pins outdated versions or pulls in packages with known published vulnerabilities (CVEs). The package is real, but the version has a documented security hole. AI also tends to use loose version specifiers (wildcards, latest), which quietly drift into vulnerable territory.
The tools: you already have these — they're built into the ecosystems, free, no install:
# Node / Next.js / React / TypeScript projects: npm audit # lists known vulnerabilities by severity npm audit --json # machine-readable npm audit fix # auto-update where a safe fix exists (review the changes!) # Python projects — pip-audit checks installed packages against advisory databases: pip install pip-audit pip-audit # scans the current environment pip-audit -r requirements.txt # scan a requirements file
Reading the output: each finding gives the package, the vulnerable version range, the patched version (if one exists), and a severity (critical / high / moderate / low). Prioritise critical and high. Note that npm audit fix can introduce breaking changes when it bumps major versions — review what it changed and test, don't blindly accept.
Try it yourself: run npm audit in any existing Node project you have — most real projects surface at least a few advisories, so you'll see live output immediately.
Honest limits: npm audit only knows about published, known vulnerabilities — a brand-new malicious package (Check 2) or a zero-day won't appear. It also famously over-reports transitive dev-dependency issues that may not be exploitable in your context. Use it as a baseline, not a verdict.
Check 4: Insecure Code Patterns (Semgrep)
The problem: AI is trained to make code work, and security constraints are often the first thing it skips for speed. Common AI-introduced patterns: SQL built by string interpolation (injection), weak hashing (MD5/SHA1), using a non-cryptographic random for tokens, missing output escaping (XSS), and overly permissive CORS. A 2026 study found ~45% of AI-generated code contained a security weakness of some kind.
The tool — Semgrep: a fast, free static-analysis scanner with strong support for exactly your stack (JavaScript/TypeScript, React, Python). It ships with community rulesets that catch the common vulnerability patterns, and it's noted in 2026 guidance as effective at spotting API hallucinations and deprecated/insecure patterns.
# Semgrep runs most smoothly on Linux/Mac; on Windows the recommended path is Docker or WSL. # Option A — Docker (works on Windows): docker run --rm -v "%cd%:/src" semgrep/semgrep semgrep --config=auto /src # Option B — WSL (Windows Subsystem for Linux), then pip install: pip install semgrep semgrep --config=auto . # 'auto' picks rulesets based on your languages # Target specific concerns: semgrep --config="p/security-audit" . semgrep --config="p/owasp-top-ten" . semgrep --config="p/secrets" .
Try it yourself:
# Create a file with a classic AI-style injection pattern and scan it: # const q = "SELECT * FROM users WHERE id = " + userInput; // string-built SQL # semgrep --config=auto . should flag it as a possible injection.
Honest limits: static analysis catches patterns, not intent. It will flag a likely SQL-injection shape but can't know whether userInput is actually attacker-controlled in your flow, so it produces both false positives and false negatives. It is a strong net for the common insecure idioms AI reaches for — treat findings as "review this," and don't assume a clean run means the code is secure.
Check 5: Committed Config & Artifact Leaks (git + gitleaks)
The problem: AI-generated projects routinely leave dangerous things about to be committed: a real .env file that isn't gitignored, private keys, build artifacts, or — as in the well-known 2026 Claude Code incident — packaging misconfigurations that ship internal source maps. The code itself can be fine while the thing you're about to push contains secrets. AI tools also don't reliably set up .gitignore correctly.
The check: before pushing, confirm what's actually staged, and that secret files are ignored.
# See exactly what is staged to be committed — eyeball it for anything sensitive git status git diff --cached --name-only # Confirm .env (and similar) are ignored, NOT tracked: git ls-files | findstr /I ".env .pem .key credentials" # ^ On Windows, findstr is the grep equivalent. If this lists a real secrets file, # it is tracked and will be pushed — fix it. # If a secret file IS tracked, stop tracking it and ignore it: git rm --cached .env echo .env>> .gitignore # then rotate any secrets it contained, and commit the removal.
How this differs from Check 1: Check 1 scans file contents for secret strings; this check is about whether sensitive files (and build outputs) are about to enter the repo at all. Gitleaks' git mode plus a manual git status review covers both the "is it in the content" and "is it in the package" angles.
Honest limits: this is partly manual — a quick discipline rather than a single tool. The habit (always run git status and scan before pushing AI-generated changes) is the point.
Check 6: What the Tools CANNOT Check (manual verification)
This is the section most guides leave out, and it's the most important for honesty. The tools above catch pattern-detectable problems. They cannot judge intent, correctness, or context — and AI-generated code fails in exactly those judgment-based ways. No scanner will reliably tell you any of the following; you have to verify them:
- Does the code actually do what you asked? AI produces plausible code that compiles and runs but solves a subtly different problem, or misses edge cases (empty input, large input, concurrent access). Read it, and test the edge cases — "it ran once in the demo" is not verification.
- Authentication & authorization correctness. A scanner can flag a route with no auth attribute, but it cannot tell you whether your authorization logic is right — whether user A can reach user B's data. This needs you to trace the access rules yourself.
- Business-logic correctness. Whether the discount calculation, the state transition, or the permission rule matches your actual requirements is pure domain judgment. AI has no way to know your intent beyond the prompt.
- Data privacy & compliance (GDPR, etc.). No tool can certify GDPR/PII compliance — it's legal and contextual. A scanner might flag PII being logged, but whether your data collection, consent, and retention are lawful is a human (and often legal) judgment. Never assume a clean scan means you're compliant.
- Architecture & maintainability. Duplicate logic, poor separation of concerns, and "works but will be a nightmare to change" are quality judgments. (Linters and tools like SonarQube help with some of this, but the architectural call is yours.)
- Observability adequacy. Tools can detect the absence of a logging library, but not whether you log the right things to debug a production incident. That's a design decision.
The discipline: treat the AI as a fast junior developer whose output you review, not as an authority you trust. The tools handle the mechanical checks so your attention is free for the judgment ones. The single most valuable habit is reading the code the AI produced and asking "do I understand what every line does, and is this actually what I wanted?" — because that question is the one no scanner can answer for you.
What data does each tool send? (privacy & data handling)
A fair question before you point any of these at real code, especially proprietary or client work: does the tool phone home, and does your source code leave your machine? The honest answer differs by tool. Here is exactly what each one does, with links to the official documentation so you can verify rather than take this on trust. The short version: none of the recommended tools upload your source code in their basic local mode — but a few necessarily send your dependency names and versions to a registry, because checking those against a public database is the entire point of what they do.
- Gitleaks — fully local, nothing leaves your machine. It is a single Go binary that scans your files and git history with regex patterns entirely on your computer. No account, no API key, no network call. Safe for proprietary code, and it works with no internet connection (a good way to verify: disconnect and it still runs). Docs:
github.com/gitleaks/gitleaksandgitleaks.io. - npm audit — sends your dependency list (not your code) to the registry. Per npm's own documentation, it "submits a description of the dependencies configured in your package to your default registry and asks for a report of known vulnerabilities." Technically it POSTs your dependency tree (package names, versions, integrity hashes) to
registry.npmjs.org; your application source code is not sent. If even your dependency list is sensitive, be aware it is transmitted. Docs:docs.npmjs.com/cli/commands/npm-auditand npm's privacy policy atdocs.npmjs.com/policies/privacy. - pip-audit — same model for Python (official PyPA tool). Per its official docs, it "uses the Python Packaging Advisory Database via the PyPI JSON API as a source of vulnerability reports" — it sends your package names and versions to query the advisory database (PyPA Advisory DB by default, or OSV), and does not upload your source code. It does require a network connection in its default modes (it queries those databases over the internet). Docs:
github.com/pypa/pip-audit. - slopcheck / slop-scan — must contact the registry by design. Their whole job is to ask "does this package name exist?", so they send the package names from your dependency files (and, for slop-scan, names parsed from your code/docs) to the public npm/PyPI registry. Package names only — not your source. This network call is unavoidable for the check to work. Docs:
github.com/0xToxSec/slopcheckandgithub.com/erayaha/slop-scan. - Semgrep — the local scan runs on your machine; Semgrep's own docs state "by default, code is never uploaded." The plain
semgrep --config=auto .command analyzes your code locally — however,--config=autodownloads rule definitions from Semgrep's registry over the network (rules come down; your code does not go up). Semgrep also has an optional connected platform (semgrep login/semgrep ci) where, per their FAQ, "only run metadata is sent" for standard CI runs, though certain opt-in features (Managed Scans, Multimodal) do require code access — none of which apply to the plain local command here. For a fully offline run, point it at local rule files instead ofauto. Docs:semgrep.dev/docsand their privacy notice atsemgrep.dev/legal/privacy. - ggshield (GitGuardian) — sends content to a third-party API; deliberately NOT in the core routine. ggshield is API-based: it transmits content to GitGuardian's servers for detection and requires an API key. That is a legitimate design, but it means your code/diffs leave your machine to a third party — which is exactly why this playbook leads with Gitleaks (fully local) instead. If you choose ggshield, review GitGuardian's data-handling docs first:
docs.gitguardian.com.
How to verify any of this yourself (don't just trust a single source, including this page): read each tool's official privacy/docs page (linked above), and/or run the tool with your network disconnected — a fully-local tool like Gitleaks keeps working offline, while a registry-checking tool like slopcheck will fail, which itself proves it was making outbound calls. For sensitive or client code, this five-minute check is worth doing before the first real run.
Reference links (verify against the current versions): Gitleaks — github.com/gitleaks/gitleaks · npm audit — docs.npmjs.com/cli/commands/npm-audit · npm privacy — docs.npmjs.com/policies/privacy · pip-audit — github.com/pypa/pip-audit · slopcheck — github.com/0xToxSec/slopcheck · slop-scan — github.com/erayaha/slop-scan · Semgrep — semgrep.dev/docs and semgrep.dev/legal/privacy · GitGuardian/ggshield — docs.gitguardian.com. Tool behaviour can change between versions; confirm on the official page before relying on it for sensitive code.
Putting it together: the pre-push routine
A practical sequence to run before pushing AI-generated changes (Node/Python projects on Windows):
gitleaks dir . --verbose # 1. leaked secrets npx slop-scan . # 2. hallucinated dependencies npm audit # 3. vulnerable dependencies (pip-audit for Python) semgrep --config=auto . # 4. insecure code patterns (via Docker/WSL on Windows) git status # 5. what's actually about to be committed # 6. then READ the code and verify the judgment-based items yourself
Automate it (optional but recommended): wire the deterministic checks into a pre-commit or pre-push git hook so they run automatically and block the push on a critical finding — this is the "shift left" principle: the earlier a problem is caught, the cheaper it is to fix, and a secret caught before the push never enters history at all. The pre-commit framework (pre-commit.com) can run Gitleaks and others on every commit; for teams, a shared, version-pinned hook config gives everyone the same gate. For a hands-on walkthrough — including the exact gotchas, CI setup, and branch protection — see From Playbook to Pipeline →
The honest bottom line: none of these tools is novel or exclusive — they're free, established, and you should use them precisely because they're proven. The skill isn't owning a magic scanner; it's knowing which check catches which class of AI mistake, running them before you push, and reserving your own judgment for the things no tool can verify. AI writes code fast; this routine is how you make sure what it wrote is safe to ship.
Note: this is an informational engineering overview. Tool commands and behaviour change between versions and platforms — confirm against each tool's current official documentation when you run it, and always use clearly fake values when testing secret detection. The statistics cited reflect 2026 research and are included for context, not as guarantees about your specific codebase.