VibeSecurity

Tools and workflow

How to Secure AI Generated Code: A Review Workflow

Learning how to secure AI generated code is mostly a process problem, not a tooling problem. The model writes plausible code fast, and plausible is not the same as safe. What protects you is a short workflow you follow every time: shape the prompt, review the diff, add a test, and let CI say no. This article lays out that loop, including what to do when you genuinely cannot read the code.

By the VibeSecurity team5 min read

Why AI-generated code needs a different review habit

When you write code yourself, you know what you meant. When an assistant writes it, you only know what you asked. The gap between the two is where security bugs live: the assistant satisfied the request, and nobody stated the unstated rule that users must not see each other's data.

So the review habit shifts from 'is this correct?' to 'what did this change quietly decide for me?'. Each diff may add a route, a package, a permission or a way to reach the network. Those are the decisions to look for.

Prompt-time habits that reduce risk

Prompts are cheap and they shape everything downstream. Three habits pay off most.

  • State the rule, not just the feature. 'Add an orders page' produces a page. 'Add an orders page where a signed-in user can only see their own orders, enforced on the server' produces a page and a check.
  • Ask for the negative case. Follow the feature prompt with 'now write a test showing a different user cannot access this'.
  • Constrain dependencies. Say 'use the libraries already in package.json unless you tell me why a new one is needed'. New packages are new supply chain risk.
  • Keep secrets out of the conversation. Do not paste real keys into prompts or into files the assistant can read. Use placeholder values locally.

How to review a diff when you are not a security expert

You do not need to audit syntax. You need to scan for a short list of change types. Read the file list first, then open only the files that touch these areas.

  1. 1New or changed routes and API handlers. Ask: who can call this, and where does the server check that?
  2. 2Anything that builds a database query from input. Ask: is the input passed as a parameter, or joined into a string?
  3. 3New environment variable reads and hardcoded strings that look like keys. Ask: could this reach the browser?
  4. 4package.json and lockfile changes. Ask: did I want this package, and is the name spelled correctly?
  5. 5Deleted code. Assistants often remove a check when it gets in the way of making a test pass, and a deleted check is invisible unless you look at the removed lines.

The tests worth adding

You do not need a large suite. Add one test per security rule, written as a sentence a non-engineer could read. These catch the classic AI regression, where a later prompt rewrites a file and drops an earlier guard.

  • An anonymous request to each protected endpoint returns 401 or 403.
  • User A requesting user B's record gets a refusal.
  • A normal user calling an admin endpoint gets a refusal.
  • The public JSON your app returns never contains fields such as password hashes or internal notes.
  • Invalid input is rejected with a clear error rather than a crash.
Vitest example: object-level access
import { describe, it, expect } from "vitest";

describe("orders API", () => {
  it("refuses another user's order", async () => {
    const res = await fetch(`${process.env.TEST_BASE_URL}/api/orders/${orderIdOfUserB}`, {
      headers: { Authorization: `Bearer ${tokenOfUserA}` },
    });
    expect([403, 404]).toContain(res.status);
  });
});

CI gates that say no for you

A gate works because it does not depend on you remembering. Three are worth setting up before any others.

Dependency audit: npm audit compares your dependency tree against known vulnerability advisories, and its audit-level option changes only the failure threshold, not the report. That makes it practical to fail the build on high severity and above while still seeing everything.

Secret scanning: a tool such as gitleaks can scan a repository and its git history, and can run as a pre-commit hook. GitHub also offers secret scanning and push protection, which blocks a push containing a recognized credential before it reaches the repository.

Linting with security rules: a linter will not find business logic flaws, but it will flag dangerous patterns such as eval or string-built queries. Treat a lint failure as a build failure.

.github/workflows/security.yml
name: security
on: [pull_request]
jobs:
  checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm audit --audit-level=high
      - run: npm run lint
      - run: npm test

What if you cannot read the code at all?

Many founders using these tools are not developers, and that is a legitimate way to work. The answer is to move your review from the code to the behavior.

Test the running app from the outside with two accounts and a logged-out window. Ask the assistant to explain each new route in plain language, and to list every place data leaves the app. Ask it to write the tests described above, then run them and watch them pass. When something is beyond you, such as payments or health data, pay a human reviewer for that slice rather than trusting a green checkmark.

One caution: do not treat the assistant's own claim that code is secure as evidence. Ask for a test that would fail if the code were insecure, and run it.

A loop you can repeat in ten minutes

  1. 1Write the prompt with the security rule in it.
  2. 2Read the file list and open only risky files.
  3. 3Run the tests, including the negative ones.
  4. 4Push to a branch and let CI run the audit, secret scan and lint.
  5. 5Merge only when everything is green, and never by disabling a check.

Frequently asked questions

Is AI generated code less secure than human code?

The honest answer is that it depends on the review process, not the author. Assistants reproduce common patterns, including insecure ones, and they optimize for making your request work. Human code needs the same gates. What changes is the volume and speed, which makes automated checks more important.

Can I use an AI tool to review its own code?

You can use a second pass to find issues, and it often does. But treat it as a helper, not a sign-off. Ask it for failing tests rather than opinions, and run those tests yourself against the deployed app.

Does npm audit catch everything?

No. It reports known vulnerabilities in packages your project depends on. It does not find flaws in your own code, misconfigured database rules, or malicious packages that have not yet been reported. It is one gate among several.

What is the minimum viable setup for a solo builder?

Turn on secret scanning with push protection, add npm audit to CI, and write five tests covering anonymous access, cross-user access and role checks. That takes a few hours and blocks the most common mistakes.

Put it into practice

Sources

  1. 1.npm docs: npm audit
  2. 2.Gitleaks project on GitHub
  3. 3.GitHub docs: About push protection
  4. 4.GitHub docs: About secret scanning
  5. 5.OWASP Source Code Analysis Tools