prodlint 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -102
- package/dist/cli.js +635 -77
- package/dist/index.d.ts +7 -0
- package/dist/index.js +593 -54
- package/dist/mcp.js +593 -54
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,130 +1,175 @@
|
|
|
1
1
|
# prodlint
|
|
2
2
|
|
|
3
|
-
[](https://github.com/prodlint/prodlint/actions/workflows/ci.yml)
|
|
4
3
|
[](https://www.npmjs.com/package/prodlint)
|
|
5
4
|
[](https://www.npmjs.com/package/prodlint)
|
|
6
|
-
[](https://prodlint.com)
|
|
7
5
|
[](https://opensource.org/licenses/MIT)
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
Catch the bugs AI leaves behind.
|
|
10
8
|
|
|
11
|
-
prodlint
|
|
12
|
-
|
|
13
|
-
## Why?
|
|
14
|
-
|
|
15
|
-
AI code generators (Cursor, Copilot, v0, Bolt) ship code that works in demos but breaks in production. Hardcoded secrets, hallucinated packages, missing auth checks, XSS vectors — these issues slip through because they're syntactically valid and pass type-checks.
|
|
16
|
-
|
|
17
|
-
prodlint catches what TypeScript and ESLint miss: **production readiness gaps**.
|
|
18
|
-
|
|
19
|
-
## Quick Start
|
|
9
|
+
prodlint scans AI-generated JavaScript and TypeScript projects for production readiness issues — hallucinated imports, missing auth, exposed secrets, N+1 queries, and more. No LLM required, just fast static analysis against known failure modes.
|
|
20
10
|
|
|
21
11
|
```bash
|
|
22
12
|
npx prodlint
|
|
23
13
|
```
|
|
24
14
|
|
|
25
|
-
## Example Output
|
|
26
|
-
|
|
27
15
|
```
|
|
28
|
-
prodlint v0.
|
|
29
|
-
Scanned
|
|
16
|
+
prodlint v0.5.0
|
|
17
|
+
Scanned 148 files · 3 critical · 5 warnings
|
|
30
18
|
|
|
31
|
-
src/app/api/
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
src/app/api/checkout/route.ts
|
|
20
|
+
12:1 CRIT No rate limiting — anyone could spam this endpoint and run up your API costs rate-limiting
|
|
21
|
+
28:5 WARN Empty catch block silently swallows error shallow-catch
|
|
34
22
|
|
|
35
|
-
src/
|
|
36
|
-
|
|
23
|
+
src/actions/submit.ts
|
|
24
|
+
5:3 CRIT Server action uses formData without validation next-server-action-validation
|
|
25
|
+
↳ Validate with Zod: const data = schema.safeParse(Object.fromEntries(formData))
|
|
37
26
|
|
|
38
27
|
src/lib/db.ts
|
|
39
|
-
|
|
28
|
+
1:1 CRIT Package "drizzle-orm" is imported but not in package.json hallucinated-imports
|
|
40
29
|
|
|
41
30
|
Scores
|
|
42
|
-
security
|
|
43
|
-
reliability
|
|
44
|
-
performance 95 ███████████████████░
|
|
45
|
-
ai-quality
|
|
31
|
+
security 72 ████████████████░░░░ (8 issues)
|
|
32
|
+
reliability 85 █████████████████░░░ (4 issues)
|
|
33
|
+
performance 95 ███████████████████░ (1 issue)
|
|
34
|
+
ai-quality 90 ██████████████████░░ (3 issues)
|
|
46
35
|
|
|
47
|
-
Overall:
|
|
36
|
+
Overall: 82/100 (weighted)
|
|
48
37
|
|
|
49
|
-
3 critical ·
|
|
38
|
+
3 critical · 5 warnings · 3 info
|
|
50
39
|
```
|
|
51
40
|
|
|
52
|
-
##
|
|
41
|
+
## Why?
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
npx prodlint --json # JSON output
|
|
58
|
-
npx prodlint --ignore "*.test.ts" # Ignore patterns
|
|
59
|
-
```
|
|
43
|
+
AI code generators (Cursor, Copilot, v0, Bolt, Claude) write code that works in demos but breaks in production. Hardcoded secrets, hallucinated packages, missing auth, XSS vectors — these pass type-checks and look correct but aren't.
|
|
44
|
+
|
|
45
|
+
prodlint catches what TypeScript and ESLint miss: **production readiness gaps**.
|
|
60
46
|
|
|
61
|
-
##
|
|
62
|
-
|
|
63
|
-
prodlint runs **11 rules** across 3 categories:
|
|
64
|
-
|
|
65
|
-
### Security
|
|
66
|
-
| Rule | Severity | What it detects |
|
|
67
|
-
|------|----------|----------------|
|
|
68
|
-
| `secrets` | critical | Hardcoded API keys (Stripe, AWS, Supabase, OpenAI, GitHub, SendGrid) |
|
|
69
|
-
| `env-exposure` | critical | Server env vars in client components, `.env` not in `.gitignore` |
|
|
70
|
-
| `auth-checks` | critical | API routes without authentication (middleware-aware) |
|
|
71
|
-
| `unsafe-html` | critical | `dangerouslySetInnerHTML`, direct `innerHTML` assignment |
|
|
72
|
-
| `sql-injection` | critical | SQL queries built with template literals or string concatenation |
|
|
73
|
-
| `input-validation` | warning | API routes accessing request body without validation |
|
|
74
|
-
| `rate-limiting` | warning | API routes without rate limiting |
|
|
75
|
-
| `cors-config` | warning | `Access-Control-Allow-Origin: *`, `cors()` with no config |
|
|
76
|
-
|
|
77
|
-
### Reliability
|
|
78
|
-
| Rule | Severity | What it detects |
|
|
79
|
-
|------|----------|----------------|
|
|
80
|
-
| `hallucinated-imports` | critical | Imports of packages not in `package.json` and not Node built-ins |
|
|
81
|
-
| `error-handling` | warning | API routes without try/catch, empty catch blocks |
|
|
82
|
-
|
|
83
|
-
### AI Quality
|
|
84
|
-
| Rule | Severity | What it detects |
|
|
85
|
-
|------|----------|----------------|
|
|
86
|
-
| `ai-smells` | mixed | TODOs, placeholder functions, console.log spam, excessive `any`, commented-out code |
|
|
47
|
+
## Install
|
|
87
48
|
|
|
88
|
-
|
|
49
|
+
```bash
|
|
50
|
+
npx prodlint # Run directly (no install)
|
|
51
|
+
npx prodlint ./my-app # Scan specific path
|
|
52
|
+
npx prodlint --json # JSON output for CI
|
|
53
|
+
npx prodlint --ignore "*.test.ts" # Ignore patterns
|
|
54
|
+
npx prodlint --min-severity warning # Only warnings and criticals
|
|
55
|
+
npx prodlint --quiet # Suppress badge output
|
|
56
|
+
```
|
|
89
57
|
|
|
90
|
-
|
|
58
|
+
Or install it:
|
|
91
59
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
60
|
+
```bash
|
|
61
|
+
npm i -D prodlint # Project dependency
|
|
62
|
+
npm i -g prodlint # Global install
|
|
63
|
+
```
|
|
95
64
|
|
|
96
|
-
|
|
65
|
+
## 32 Rules across 4 Categories
|
|
66
|
+
|
|
67
|
+
### Security (14 rules)
|
|
68
|
+
|
|
69
|
+
| Rule | What it catches |
|
|
70
|
+
|------|----------------|
|
|
71
|
+
| `secrets` | API keys, tokens, passwords hardcoded in source |
|
|
72
|
+
| `auth-checks` | API routes with no authentication |
|
|
73
|
+
| `env-exposure` | `NEXT_PUBLIC_` on server-only secrets |
|
|
74
|
+
| `input-validation` | Request body used without validation |
|
|
75
|
+
| `cors-config` | `Access-Control-Allow-Origin: *` |
|
|
76
|
+
| `unsafe-html` | `dangerouslySetInnerHTML` with user data |
|
|
77
|
+
| `sql-injection` | String-interpolated SQL queries (ORM-aware) |
|
|
78
|
+
| `open-redirect` | User input passed to `redirect()` |
|
|
79
|
+
| `rate-limiting` | API routes with no rate limiter |
|
|
80
|
+
| `phantom-dependency` | Packages in node_modules but missing from package.json |
|
|
81
|
+
| `insecure-cookie` | Session cookies missing httpOnly/secure/sameSite |
|
|
82
|
+
| `leaked-env-in-logs` | `process.env.*` inside console.log calls |
|
|
83
|
+
| `insecure-random` | `Math.random()` used for tokens, secrets, or session IDs |
|
|
84
|
+
| `next-server-action-validation` | Server actions using formData without Zod/schema validation |
|
|
85
|
+
|
|
86
|
+
### Reliability (7 rules)
|
|
87
|
+
|
|
88
|
+
| Rule | What it catches |
|
|
89
|
+
|------|----------------|
|
|
90
|
+
| `hallucinated-imports` | Imports of packages not in package.json |
|
|
91
|
+
| `error-handling` | Async operations without try/catch |
|
|
92
|
+
| `unhandled-promise` | Floating promises with no await or .catch |
|
|
93
|
+
| `shallow-catch` | Empty catch blocks that swallow errors |
|
|
94
|
+
| `missing-loading-state` | Client components that fetch without a loading state |
|
|
95
|
+
| `missing-error-boundary` | Route layouts without a matching error.tsx |
|
|
96
|
+
| `missing-transaction` | Multiple Prisma writes without `$transaction` |
|
|
97
|
+
|
|
98
|
+
### Performance (4 rules)
|
|
99
|
+
|
|
100
|
+
| Rule | What it catches |
|
|
101
|
+
|------|----------------|
|
|
102
|
+
| `no-sync-fs` | `readFileSync` in API routes |
|
|
103
|
+
| `no-n-plus-one` | Database calls inside loops |
|
|
104
|
+
| `no-unbounded-query` | `.findMany()` / `.select('*')` with no limit |
|
|
105
|
+
| `no-dynamic-import-loop` | `import()` inside loops |
|
|
106
|
+
|
|
107
|
+
### AI Quality (7 rules)
|
|
108
|
+
|
|
109
|
+
| Rule | What it catches |
|
|
110
|
+
|------|----------------|
|
|
111
|
+
| `ai-smells` | `any` types, `console.log`, TODO comments piling up |
|
|
112
|
+
| `placeholder-content` | Lorem ipsum, example emails, "your-api-key-here" left in production code |
|
|
113
|
+
| `hallucinated-api` | `.flatten()`, `.contains()`, `.substr()` — methods AI invents |
|
|
114
|
+
| `stale-fallback` | `localhost:3000` hardcoded in production code |
|
|
115
|
+
| `comprehension-debt` | Functions over 80 lines, deep nesting, too many parameters |
|
|
116
|
+
| `codebase-consistency` | Mixed naming conventions across the project |
|
|
117
|
+
| `dead-exports` | Exported functions that nothing imports |
|
|
97
118
|
|
|
98
119
|
## Smart Detection
|
|
99
120
|
|
|
100
121
|
prodlint avoids common false positives:
|
|
101
122
|
|
|
102
|
-
- **
|
|
103
|
-
- **
|
|
104
|
-
- **
|
|
123
|
+
- **AST parsing** — Babel-based analysis for loops, imports, and SQL templates with regex fallback
|
|
124
|
+
- **Framework awareness** — Prisma, Drizzle, Supabase, Knex, and Sequelize whitelists prevent false SQL injection flags
|
|
125
|
+
- **Middleware detection** — Clerk, NextAuth, Supabase middleware detected — auth findings downgraded
|
|
126
|
+
- **Block comment awareness** — patterns inside `/* */` are ignored
|
|
127
|
+
- **Path alias support** — `@/`, `~/`, and tsconfig paths aren't flagged as hallucinated imports
|
|
105
128
|
- **Route exemptions** — auth, webhook, health, and cron routes are exempt from auth/rate-limit checks
|
|
129
|
+
- **Test/script file awareness** — lower severity for non-production files
|
|
130
|
+
- **Fix suggestions** — findings include actionable `fix` hints with remediation code
|
|
131
|
+
|
|
132
|
+
## Scoring
|
|
133
|
+
|
|
134
|
+
Each category starts at 100. Deductions per finding:
|
|
135
|
+
|
|
136
|
+
| Severity | Deduction | Per-rule cap |
|
|
137
|
+
|----------|-----------|--------------|
|
|
138
|
+
| critical | -8 | max 1 |
|
|
139
|
+
| warning | -2 | max 2 |
|
|
140
|
+
| info | -0.5 | max 3 |
|
|
141
|
+
|
|
142
|
+
**Diminishing returns**: after 30 points deducted in a category, further deductions are halved; after 50, quartered.
|
|
143
|
+
|
|
144
|
+
**Weighted overall**: security 40%, reliability 30%, performance 15%, ai-quality 15%. Floor at 0. Exit code `1` if any critical findings exist.
|
|
106
145
|
|
|
107
146
|
## GitHub Action
|
|
108
147
|
|
|
109
|
-
Add
|
|
148
|
+
Add to `.github/workflows/prodlint.yml`:
|
|
110
149
|
|
|
111
150
|
```yaml
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
151
|
+
name: Prodlint
|
|
152
|
+
on: [pull_request]
|
|
153
|
+
|
|
154
|
+
jobs:
|
|
155
|
+
scan:
|
|
156
|
+
runs-on: ubuntu-latest
|
|
157
|
+
steps:
|
|
158
|
+
- uses: actions/checkout@v4
|
|
159
|
+
- uses: prodlint/prodlint@v1
|
|
160
|
+
with:
|
|
161
|
+
threshold: 50
|
|
117
162
|
```
|
|
118
163
|
|
|
119
|
-
|
|
164
|
+
Posts a score breakdown as a PR comment and fails the build if below threshold.
|
|
165
|
+
|
|
120
166
|
| Input | Default | Description |
|
|
121
167
|
|-------|---------|-------------|
|
|
122
168
|
| `path` | `.` | Path to scan |
|
|
123
169
|
| `threshold` | `0` | Minimum score to pass (0-100) |
|
|
124
|
-
| `ignore` |
|
|
125
|
-
| `comment` | `true` | Post
|
|
170
|
+
| `ignore` | | Comma-separated glob patterns to ignore |
|
|
171
|
+
| `comment` | `true` | Post PR comment with results |
|
|
126
172
|
|
|
127
|
-
**Outputs:**
|
|
128
173
|
| Output | Description |
|
|
129
174
|
|--------|-------------|
|
|
130
175
|
| `score` | Overall score (0-100) |
|
|
@@ -132,46 +177,33 @@ Add prodlint to your CI pipeline. It posts a score summary as a PR comment and c
|
|
|
132
177
|
|
|
133
178
|
## MCP Server
|
|
134
179
|
|
|
135
|
-
prodlint
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
npx prodlint-mcp
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Claude Code
|
|
180
|
+
Use prodlint inside Cursor, Claude Code, or any MCP-compatible editor:
|
|
142
181
|
|
|
182
|
+
**Claude Code:**
|
|
143
183
|
```bash
|
|
144
184
|
claude mcp add prodlint npx prodlint-mcp
|
|
145
185
|
```
|
|
146
186
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
Add to your MCP config:
|
|
150
|
-
|
|
187
|
+
**Cursor / Windsurf:**
|
|
151
188
|
```json
|
|
152
189
|
{
|
|
153
190
|
"mcpServers": {
|
|
154
191
|
"prodlint": {
|
|
155
192
|
"command": "npx",
|
|
156
|
-
"args": ["prodlint-mcp"]
|
|
193
|
+
"args": ["-y", "prodlint-mcp"]
|
|
157
194
|
}
|
|
158
195
|
}
|
|
159
196
|
}
|
|
160
197
|
```
|
|
161
198
|
|
|
162
|
-
|
|
199
|
+
Ask your AI: *"Run prodlint on this project"* and it calls the `scan` tool directly.
|
|
163
200
|
|
|
164
|
-
##
|
|
201
|
+
## Suppression
|
|
165
202
|
|
|
166
203
|
Suppress a single line:
|
|
167
204
|
```ts
|
|
168
205
|
// prodlint-disable-next-line secrets
|
|
169
|
-
const key = "
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Suppress multiple rules:
|
|
173
|
-
```ts
|
|
174
|
-
// prodlint-disable-next-line secrets, auth-checks
|
|
206
|
+
const key = "sk_test_example_for_docs"
|
|
175
207
|
```
|
|
176
208
|
|
|
177
209
|
Suppress an entire file (place at top):
|
|
@@ -189,9 +221,11 @@ console.log(result.overallScore) // 0-100
|
|
|
189
221
|
console.log(result.findings) // Finding[]
|
|
190
222
|
```
|
|
191
223
|
|
|
192
|
-
##
|
|
224
|
+
## Badge
|
|
193
225
|
|
|
194
|
-
|
|
226
|
+
```md
|
|
227
|
+
[](https://prodlint.com)
|
|
228
|
+
```
|
|
195
229
|
|
|
196
230
|
## License
|
|
197
231
|
|