Skip to content
TopNotchh.
VerglosJul 23, 20267 min read

The Security Bugs AI Code Keeps Writing: 8 Patterns Found Across 300 Repo Scans

Verglos scanned 300 public repositories and found repeated AI-era vulnerability patterns: SQL injection, weak tokens, IDOR, wildcard CORS, stack leaks, secrets, mass assignment, and slopsquat risk.

By TopNotchh Engineering

AI coding tools are very good at producing plausible software.

That is both the opportunity and the risk.

Plausible code is not the same thing as safe code. A route can work and still leak another user's data. A token can look random and still be predictable. A dependency can install and still be the wrong package. An error handler can help during development and still leak internals in production.

Verglos scanned 300 public repositories to understand the security patterns that show up in modern, fast-built, AI-assisted codebases.

294 scans completed successfully. Among those successful scans:

  • 45.6% had at least one critical or high finding
  • 24.8% had at least one critical finding
  • 36.4% had at least one high finding
  • The campaign reported 395 critical and 1,195 high findings

The findings clustered around familiar mistakes. But the reason they matter now is different: AI coding workflows make these mistakes easier to generate, easier to accept, and easier to ship.

Here are the eight patterns that matter most.

1. SQL Injection From String Concatenation

SQL injection remains one of the oldest and most damaging web application vulnerabilities.

The AI-era version is simple: the generated code builds a query using string interpolation because it is concise and easy to read.

Example shape:

const rows = await db.query(`SELECT * FROM users WHERE email = '${email}'`);

The code looks obvious. It may work in testing. But if user-controlled input becomes part of the SQL grammar, an attacker can change the query.

In the scan campaign, SQL injection from concatenation was the top critical title.

The fix is not complicated:

  • Use parameterized queries.
  • Use an ORM safely.
  • Never let user input become SQL syntax.
  • Treat dynamic table or column names as dangerous even when values are parameterized.

Why AI writes this:

LLMs optimize for directness. Many examples online still show unsafe query building. If the prompt asks for a quick route or handler, the model may produce the shortest working code, not the safest production code.

2. Missing Ownership Checks / IDOR

IDOR means insecure direct object reference.

In plain English:

The route checks that a user is logged in, but does not check that the requested record belongs to that user.

Example shape:

const invoice = await db.invoice.findUnique({
  where: { id: req.params.id },
});

If the route only checks authentication, any logged-in user may be able to request another user's invoice by changing the ID.

This is one of the most important AI-generated code risks because generated CRUD code often follows the same pattern:

  1. Add authentication.
  2. Read id from params.
  3. Fetch record by ID.
  4. Return record.

The missing step is authorization.

The safer shape:

const invoice = await db.invoice.findFirst({
  where: {
    id: req.params.id,
    ownerId: session.user.id,
  },
});

Why AI writes this:

The model understands login flows better than ownership models. It often treats "authenticated" as enough, especially when the prompt says "protect this route" but does not specify tenant boundaries, roles, teams, or ownership rules.

3. Weak Token Generation

Security-sensitive tokens need cryptographic randomness.

AI-generated code often uses easier primitives:

const token = Math.random().toString(36).slice(2);

or:

const resetToken = `${Date.now()}-${user.id}`;

That may look random. It is not good enough for password resets, sessions, OTPs, API keys, CSRF tokens, magic links, or invite codes.

The safer Node.js shape:

import { randomBytes } from "node:crypto";

const token = randomBytes(32).toString("hex");

Why AI writes this:

Math.random() is common in examples. It is short, familiar, and works for UI randomness. But security randomness has a different threat model.

Verglos found critical patterns where Math.random() or Date.now() fed token-like values.

4. Wildcard CORS

Wildcard CORS often appears as:

app.use(cors({ origin: "*", credentials: true }));

or:

res.setHeader("Access-Control-Allow-Origin", "*");

CORS is easy to misunderstand. In development, permissive CORS makes everything work. In production, it can weaken browser-side boundaries and expose APIs to unintended origins.

The safer pattern is explicit origin allowlisting:

const allowedOrigins = ["https://app.example.com"];

Why AI writes this:

The model is often asked to "fix the CORS error." The fastest fix is to allow everything. That is acceptable for a local prototype and dangerous as a production default.

5. Stack Trace And Error Message Leaks

Generated error handlers often return too much detail:

catch (err) {
  res.status(500).json({ error: err.message, stack: err.stack });
}

That is useful during development. It is not appropriate for production.

Leaked stack traces can expose:

  • file paths
  • framework internals
  • database structure
  • package versions
  • environment assumptions
  • hidden implementation details

The safer production shape:

catch (err) {
  logger.error(err);
  res.status(500).json({ error: "Internal server error" });
}

Why AI writes this:

AI coding tools optimize for debuggability unless told otherwise. They often expose errors so the developer can see what went wrong.

6. Mass Assignment

Mass assignment happens when a request body is spread directly into a model update or create call.

Example shape:

await db.user.update({
  where: { id: session.user.id },
  data: { ...req.body },
});

This lets the request decide which fields get written. If the user model includes role, isAdmin, plan, emailVerified, or ownerId, the attacker may be able to change fields the UI never exposed.

The safer pattern is allowlisting:

const input = profileSchema.parse(req.body);

await db.user.update({
  where: { id: session.user.id },
  data: {
    name: input.name,
    avatarUrl: input.avatarUrl,
  },
});

Why AI writes this:

Spreading request bodies is compact. It also appears in many tutorial-style examples. AI tools favor the compact shape unless prompted to enforce field-level authorization.

7. Secrets In Code And Git History

Secrets remain one of the most common real-world security failures.

The scan campaign found secret-related issues across 96 repos. Critical titles included exposed database URLs, private keys, Google API keys, Slack tokens, and secrets in git history.

The hard part is that removing a secret from the current file is not enough.

If the secret was committed, it may still exist in:

  • git history
  • forks
  • CI logs
  • build artifacts
  • package registries
  • developer machines
  • deployment logs

The correct response is:

  1. Remove it from code.
  2. Rotate it at the provider.
  3. Update deployment environment variables.
  4. Verify the app still works.
  5. Archive the rotation evidence.

Why AI makes this worse:

AI tools often copy from examples. Developers paste real keys during debugging. Agents may write .env examples too aggressively. Generated code can blur the line between placeholder and production secret.

This is why Verglos should not only detect secrets. It should eventually help rotate them locally.

8. Slopsquat And Hallucinated Packages

AI tools can suggest packages that do not exist.

That creates a supply-chain attack path:

  1. An LLM suggests a plausible package name.
  2. A developer or agent tries to install it.
  3. An attacker registers the hallucinated package name.
  4. The package executes code during install or runtime.

This is called slopsquat risk.

Verglos found slopsquat-related findings in the campaign, including non-existent npm package patterns.

The important product insight is timing.

Checking after install is useful. Checking before install is better.

That is why a package verdict belongs inside the AI agent workflow:

verglos_check_package

The agent should ask before adding a dependency.

Why AI writes this:

LLMs are trained on package names, documentation, and code patterns. They can combine plausible names that sound real. A human may pause. An agent may install.

Why These Patterns Matter Together

Individually, these are familiar bugs.

Together, they describe a new security workflow problem.

AI-generated code is fast. It often arrives as complete-looking files. The developer reviews for functionality first. If the app works, the unsafe defaults can survive into production.

That is why AI code security cannot rely only on final audits.

It needs three layers:

  1. Before-write guardrails inside the agent.
  2. Fast local scanning in the repo.
  3. Evidence artifacts for handoff, launch, and procurement.

Verglos is designed around that workflow.

How To Use This As A Founder

If you are a founder using AI to build software, the practical checklist is:

  • Scan before launch.
  • Scan before client demos.
  • Scan before handing code to a customer.
  • Scan before enterprise procurement.
  • Fix critical and high findings first.
  • Keep the report.
  • Turn the report into evidence.

The command should be simple:

npx verglos

The goal is not to make founders into security engineers. The goal is to give founders a sane default:

Do not trust AI-built code until it has been checked for the risks AI commonly writes.

How To Use This As An Agency

If you run an agency or product studio, the workflow should be:

  1. Scan every project before handoff.
  2. Resolve critical and high findings.
  3. Generate a client-facing report.
  4. Attach the report to the handoff package.
  5. Keep a dated evidence archive.

This is not only technical hygiene. It is commercial protection.

It tells the client:

We used AI responsibly, checked the code, fixed the serious issues, and can show the evidence.

That message will matter more as clients become more aware of AI-generated software risk.

Final Takeaway

AI coding tools are not unsafe by default.

Unchecked AI coding workflows are unsafe by default.

The difference is evidence.

The 300-repo Verglos scan campaign showed that serious findings are common enough to justify a new default workflow:

  • generate with AI
  • check with an AI-aware scanner
  • prevent risky writes where possible
  • fix critical issues
  • produce evidence before handoff

That is the future of secure AI-assisted development.

Suggested CTA

Run:

npx verglos

Find the AI-era security patterns before they become production incidents.