Skip to content
Top Notchh.

Journal

Verglos · Research

Jul 30, 2026

9 min read

By Top Notchh Team

Reviewed Sep 1, 2026

Sources: verified

Eight patterns AI-assisted code keeps producing

Eight reviewable vulnerability shapes that recur in fast-built JavaScript and TypeScript, plus the control that should catch each one in review.

In brief

  • Plausible code often omits authorization, validation, and deployment boundaries.
  • Pattern detection is a review aid, not proof of exploitability.
  • Each detector needs a corresponding human verification question.
Eight distinct breaks in a miniature application-security system.

\n\nAI 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 the 300 highest-scoring TypeScript and JavaScript repositories in its ideal-customer profile — the SaaS platforms, admin dashboards, AI apps, agent frameworks, CRM/CMS/ecommerce projects, and workflow automation tools that AI-assisted teams actually clone, fork, and ship. Every target had ≥1,000 GitHub stars; the median was in the tens of thousands.

293 scans completed successfully. Among those:

  • 57.7% had at least one critical or high finding
  • 36.5% had at least one critical finding
  • 49.5% had at least one high finding
  • The campaign reported 633 critical and 1,955 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. The aggregate campaign proof is published in the Verglos CLI repo:

This post focuses on reader-useful patterns, not internal detector IDs.

Grid of recurring security patterns in AI-assisted TypeScript and JavaScript.

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.

The campaign surfaced this pattern in dynamic query paths across mature app-layer repositories. We are intentionally not turning the article into a public path index; teams should scan their current repo state and inspect their own results.

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.

In the aggregate campaign, missing ownership checks were the most common serious pattern. When this shape appears, it often repeats because the same generated CRUD template gets copied across routes.

A security review diagram showing authentication separated from object ownership.

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.

Weak token generation was one of the most common critical patterns in the campaign. The pattern is easy to write, easy to miss in review, and impossible to spot at runtime.

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 campaign repeatedly found permissive CORS configuration on backends that also exposed authenticated surfaces.

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. Once the response shape is set, it usually stays that way — no one deletes the debug line before the deploy.

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.

Two things are worth calling out honestly. First, mass assignment can hide behind helper functions, so a scanner may undercount it. Second, a raw spread is not automatically exploitable in typed codebases where a schema parse sits in between. Human review still matters.

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 campaign found secret-related findings across 163 repos in the Data Exposure category alone. Critical titles included exposed database URLs, private keys, Google API keys, Slack tokens, and secrets in git history.

The campaign included secret findings in current files, bundled assets, and git history. The public proof stays aggregate because publishing exact credential locations is not the point of this article.

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 detection alone is not enough. The operational requirement is rotation evidence: what leaked, when it was rotated, and what project state was verified afterward. The full disclosure workflow covers the sequence when the leak is in someone else's public repository.

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 multiple cases where a dependency string in a real package.json or install command referred to a package that npm's registry did not have. Some of these are internal workspace names that leaked into published lockfiles. Some are typos. Some are unregistered names the model invented.

Either way, the shape is the same: an install command that would either fail loudly (if the name never gets registered) or fail silently in the worst possible way (if someone registers the name before you notice).

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. 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.

The distribution across 300 world-class repos is not a fluke. The bugs are structurally cheap to write and structurally hard to catch in review. That is why AI code security cannot rely only on final audits.

It needs three layers:

  1. Before-write guardrails inside the agent. MCP tools that stop Math.random in a token context or refuse a slopsquat install before the code ever lands. This is the layer generic scanners cannot reach because they run after code exists.
  2. Fast local scanning. npx verglos catches the eight patterns in under a minute. No account, no server, no cloud upload — a local scan the developer can trust.
  3. Evidence artifacts for handoff. The scan report is the object that turns trust me into inspect this. Dated, signed, archivable, forwardable.

Verglos is designed around that workflow. The full comparison covers how each of Snyk, GitHub Advanced Security, Semgrep, Socket, and Gitleaks handles these patterns compared to Verglos.

Three layers of defense for AI-assisted code: guardrail, local scan, and evidence artifact.

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 is 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. The category positioning note covers why the evidence artifact — not the scan — is the thing an agency is really selling at handoff.

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 TS/JS campaign showed that serious findings are common enough — and repeat 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.

Where Verglos fits

Verglos knows what AI writes wrong. The scanner is free and unlocked at npx verglos, the source is Apache-2.0, and the full 300-repo campaign documents the aggregate patterns behind this list. The category positioning explains why the evidence artifact — not the scan — is the product.

Run the check:

npx verglos

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

Evidence ledger

Sources and verification

  1. ICP Top 300 TS/JS analysisTop Notchh Solutions · research · checked Sep 1, 2026
  2. Campaign methodologyTop Notchh Solutions · research · checked Sep 1, 2026