Package Exports
- prodlint
- prodlint/mcp
Readme
prodlint
The linter for vibe-coded apps.
Cursor, v0, Bolt, and Copilot build fast. prodlint catches what they miss — hallucinated imports, missing auth, hardcoded secrets, unvalidated server actions, and the other production bugs that compile just fine. Zero config, no LLM, fast static analysis.
npx prodlint prodlint v0.6.0
Scanned 148 files · 3 critical · 5 warnings
src/app/api/checkout/route.ts
12:1 CRIT No rate limiting — anyone could spam this endpoint and run up your API costs rate-limiting
28:5 WARN Empty catch block silently swallows error shallow-catch
src/actions/submit.ts
5:3 CRIT Server action uses formData without validation next-server-action-validation
↳ Validate with Zod: const data = schema.safeParse(Object.fromEntries(formData))
src/lib/db.ts
1:1 CRIT Package "drizzle-orm" is imported but not in package.json hallucinated-imports
Scores
security 72 ████████████████░░░░ (8 issues)
reliability 85 █████████████████░░░ (4 issues)
performance 95 ███████████████████░ (1 issue)
ai-quality 90 ██████████████████░░ (3 issues)
Overall: 82/100 (weighted)
3 critical · 5 warnings · 3 infoWhy?
Vibe coding is the fastest way to build. It's also the fastest way to ship hardcoded secrets, hallucinated packages, missing auth, and XSS vectors to production. These pass type-checks and look correct — but they aren't.
prodlint catches what TypeScript and ESLint miss: the bugs AI coding tools consistently write.
Install
npx prodlint # Run directly (no install)
npx prodlint ./my-app # Scan specific path
npx prodlint --json # JSON output for CI
npx prodlint --ignore "*.test.ts" # Ignore patterns
npx prodlint --min-severity warning # Only warnings and criticals
npx prodlint --quiet # Suppress badge outputOr install it:
npm i -D prodlint # Project dependency
npm i -g prodlint # Global install52 Rules across 4 Categories
Security (27 rules)
| Rule | What it catches |
|---|---|
secrets |
API keys, tokens, passwords hardcoded in source |
auth-checks |
API routes with no authentication |
env-exposure |
NEXT_PUBLIC_ on server-only secrets |
input-validation |
Request body used without validation |
cors-config |
Access-Control-Allow-Origin: * |
unsafe-html |
dangerouslySetInnerHTML with user data |
sql-injection |
String-interpolated SQL queries (ORM-aware) |
open-redirect |
User input passed to redirect() |
rate-limiting |
API routes with no rate limiter |
phantom-dependency |
Packages in node_modules but missing from package.json |
insecure-cookie |
Session cookies missing httpOnly/secure/sameSite |
leaked-env-in-logs |
process.env.* inside console.log calls |
insecure-random |
Math.random() used for tokens, secrets, or session IDs |
next-server-action-validation |
Server actions using formData without Zod/schema validation |
env-fallback-secret |
Security-sensitive env vars with hardcoded fallback values |
verbose-error-response |
Error stack traces or messages leaked in API responses |
missing-webhook-verification |
Webhook routes without signature verification |
server-action-auth |
Server actions with mutations but no auth check |
eval-injection |
eval(), new Function(), dynamic code execution |
next-public-sensitive |
NEXT_PUBLIC_ prefix on secret env vars |
ssrf-risk |
User-controlled URLs passed to fetch in server code |
path-traversal |
File system operations with unsanitized user input |
unsafe-file-upload |
File upload handlers without type or size validation |
supabase-missing-rls |
CREATE TABLE in migrations without enabling RLS |
deprecated-oauth-flow |
OAuth Implicit Grant (response_type=token) |
jwt-no-expiry |
JWT tokens signed without an expiration |
client-side-auth-only |
Password comparisons or auth logic in client components |
Reliability (11 rules)
| Rule | What it catches |
|---|---|
hallucinated-imports |
Imports of packages not in package.json |
error-handling |
Async operations without try/catch |
unhandled-promise |
Floating promises with no await or .catch |
shallow-catch |
Empty catch blocks that swallow errors |
missing-loading-state |
Client components that fetch without a loading state |
missing-error-boundary |
Route layouts without a matching error.tsx |
missing-transaction |
Multiple Prisma writes without $transaction |
redirect-in-try-catch |
redirect() inside try/catch — Next.js redirect throws, catch swallows it |
missing-revalidation |
Server actions with DB mutations but no revalidatePath |
missing-useeffect-cleanup |
useEffect with subscriptions/timers but no cleanup return |
hydration-mismatch |
window/Date.now()/Math.random() in server component render path |
Performance (6 rules)
| Rule | What it catches |
|---|---|
no-sync-fs |
readFileSync in API routes |
no-n-plus-one |
Database calls inside loops |
no-unbounded-query |
.findMany() / .select('*') with no limit |
no-dynamic-import-loop |
import() inside loops |
server-component-fetch-self |
Server components fetching their own API routes |
missing-abort-controller |
Fetch calls without timeout or AbortController |
AI Quality (8 rules)
| Rule | What it catches |
|---|---|
ai-smells |
any types, console.log, TODO comments piling up |
placeholder-content |
Lorem ipsum, example emails, "your-api-key-here" left in production code |
hallucinated-api |
.flatten(), .contains(), .substr() — methods AI invents |
stale-fallback |
localhost:3000 hardcoded in production code |
comprehension-debt |
Functions over 80 lines, deep nesting, too many parameters |
codebase-consistency |
Mixed naming conventions across the project |
dead-exports |
Exported functions that nothing imports |
use-client-overuse |
"use client" on files that don't use any client-side APIs |
Smart Detection
prodlint avoids common false positives:
- AST parsing — Babel-based analysis for loops, imports, and SQL templates with regex fallback
- Framework awareness — Prisma, Drizzle, Supabase, Knex, and Sequelize whitelists prevent false SQL injection flags
- Middleware detection — Clerk, NextAuth, Supabase middleware detected — auth findings downgraded
- Block comment awareness — patterns inside
/* */are ignored - Path alias support —
@/,~/, and tsconfig paths aren't flagged as hallucinated imports - Route exemptions — auth, webhook, health, and cron routes are exempt from auth/rate-limit checks
- Test/script file awareness — lower severity for non-production files
- Fix suggestions — findings include actionable
fixhints with remediation code
Scoring
Each category starts at 100. Deductions per finding:
| Severity | Deduction | Per-rule cap |
|---|---|---|
| critical | -8 | max 1 |
| warning | -2 | max 2 |
| info | -0.5 | max 3 |
Diminishing returns: after 30 points deducted in a category, further deductions are halved; after 50, quartered.
Weighted overall: security 40%, reliability 30%, performance 15%, ai-quality 15%. Floor at 0. Exit code 1 if any critical findings exist.
GitHub Action
Add to .github/workflows/prodlint.yml:
name: Prodlint
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: prodlint/prodlint@v1
with:
threshold: 50Posts a score breakdown as a PR comment and fails the build if below threshold.
| Input | Default | Description |
|---|---|---|
path |
. |
Path to scan |
threshold |
0 |
Minimum score to pass (0-100) |
ignore |
Comma-separated glob patterns to ignore | |
comment |
true |
Post PR comment with results |
| Output | Description |
|---|---|
score |
Overall score (0-100) |
critical |
Number of critical findings |
MCP Server
Use prodlint inside Cursor, Claude Code, or any MCP-compatible editor:
Claude Code:
claude mcp add prodlint npx prodlint-mcpCursor / Windsurf:
{
"mcpServers": {
"prodlint": {
"command": "npx",
"args": ["-y", "prodlint-mcp"]
}
}
}Ask your AI: "Run prodlint on this project" and it calls the scan tool directly.
Suppression
Suppress a single line:
// prodlint-disable-next-line secrets
const key = "sk_test_example_for_docs"Suppress an entire file (place at top):
// prodlint-disable secretsProgrammatic API
import { scan } from 'prodlint'
const result = await scan({ path: './my-project' })
console.log(result.overallScore) // 0-100
console.log(result.findings) // Finding[]Badge
[](https://prodlint.com)License
MIT