@kernlang/review 3.1.4 → 3.1.6
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/LICENSE +17 -0
- package/dist/cache.js +12 -3
- package/dist/cache.js.map +1 -1
- package/dist/concept-rules/index.d.ts +5 -1
- package/dist/concept-rules/index.js +2 -2
- package/dist/concept-rules/index.js.map +1 -1
- package/dist/concept-rules/unguarded-effect.js +37 -1
- package/dist/concept-rules/unguarded-effect.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +103 -7
- package/dist/index.js.map +1 -1
- package/dist/rules/base.js +43 -0
- package/dist/rules/base.js.map +1 -1
- package/dist/rules/cli.d.ts +7 -0
- package/dist/rules/cli.js +95 -0
- package/dist/rules/cli.js.map +1 -0
- package/dist/rules/fastapi.d.ts +10 -0
- package/dist/rules/fastapi.js +149 -0
- package/dist/rules/fastapi.js.map +1 -0
- package/dist/rules/index.d.ts +17 -1
- package/dist/rules/index.js +178 -2
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/ink.d.ts +8 -0
- package/dist/rules/ink.js +87 -0
- package/dist/rules/ink.js.map +1 -0
- package/dist/rules/nuxt.d.ts +9 -0
- package/dist/rules/nuxt.js +169 -0
- package/dist/rules/nuxt.js.map +1 -0
- package/dist/rules/terminal.d.ts +8 -0
- package/dist/rules/terminal.js +127 -0
- package/dist/rules/terminal.js.map +1 -0
- package/package.json +3 -3
package/dist/rules/index.js
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Layers:
|
|
5
5
|
* [base] Always active — universal TS/KERN rules
|
|
6
|
-
* [react] Active when target = nextjs | tailwind | web | native
|
|
6
|
+
* [react] Active when target = nextjs | tailwind | web | native | ink
|
|
7
|
+
* [ink] Active when target = ink (on top of react)
|
|
7
8
|
* [vue] Active when target = vue | nuxt
|
|
8
9
|
* [express] Active when target = express
|
|
10
|
+
* [cli] Active when target = cli
|
|
11
|
+
* [terminal] Active when target = terminal
|
|
12
|
+
* [fastapi] Active when target = fastapi (Python concept layer)
|
|
9
13
|
* [nextjs] Active when target = nextjs (on top of react)
|
|
14
|
+
* [nuxt] Active when target = nuxt (on top of vue)
|
|
10
15
|
*/
|
|
11
16
|
import { baseRules } from './base.js';
|
|
12
17
|
import { securityRules } from './security.js';
|
|
@@ -17,9 +22,14 @@ import { deadLogicRules } from './dead-logic.js';
|
|
|
17
22
|
import { reactRules } from './react.js';
|
|
18
23
|
import { vueRules } from './vue.js';
|
|
19
24
|
import { nextjsRules } from './nextjs.js';
|
|
25
|
+
import { nuxtRules } from './nuxt.js';
|
|
20
26
|
import { expressRules } from './express.js';
|
|
27
|
+
import { cliRules } from './cli.js';
|
|
28
|
+
import { terminalRules } from './terminal.js';
|
|
29
|
+
import { inkRules } from './ink.js';
|
|
30
|
+
import { fastapiRules } from './fastapi.js';
|
|
21
31
|
import { nullSafetyRules } from './null-safety.js';
|
|
22
|
-
const REACT_TARGETS = new Set(['nextjs', 'tailwind', 'web', 'native']);
|
|
32
|
+
const REACT_TARGETS = new Set(['nextjs', 'tailwind', 'web', 'native', 'ink']);
|
|
23
33
|
const VUE_TARGETS = new Set(['vue', 'nuxt']);
|
|
24
34
|
/**
|
|
25
35
|
* Get all active review rules for a given target.
|
|
@@ -36,9 +46,175 @@ export function getActiveRules(target) {
|
|
|
36
46
|
if (target === 'nextjs') {
|
|
37
47
|
rules.push(...nextjsRules);
|
|
38
48
|
}
|
|
49
|
+
if (target === 'nuxt') {
|
|
50
|
+
rules.push(...nuxtRules);
|
|
51
|
+
}
|
|
39
52
|
if (target === 'express') {
|
|
40
53
|
rules.push(...expressRules);
|
|
41
54
|
}
|
|
55
|
+
if (target === 'cli') {
|
|
56
|
+
rules.push(...cliRules);
|
|
57
|
+
}
|
|
58
|
+
if (target === 'terminal') {
|
|
59
|
+
rules.push(...terminalRules);
|
|
60
|
+
}
|
|
61
|
+
if (target === 'ink') {
|
|
62
|
+
rules.push(...inkRules);
|
|
63
|
+
}
|
|
64
|
+
if (target === 'fastapi') {
|
|
65
|
+
rules.push(...fastapiRules);
|
|
66
|
+
}
|
|
42
67
|
return rules;
|
|
43
68
|
}
|
|
69
|
+
const REGISTRY = [
|
|
70
|
+
// Base (always active)
|
|
71
|
+
{ id: 'floating-promise', layer: 'base', severity: 'error', description: 'Unresolved async operation — missing await/void/return' },
|
|
72
|
+
{ id: 'state-mutation', layer: 'base', severity: 'error', description: 'Illegal state mutation outside designated setter' },
|
|
73
|
+
{ id: 'empty-catch', layer: 'base', severity: 'warning', description: 'Catch block swallows exception silently' },
|
|
74
|
+
{ id: 'machine-gap', layer: 'base', severity: 'warning', description: 'Unreachable state or missing transition in state machine' },
|
|
75
|
+
{ id: 'config-default-mismatch', layer: 'base', severity: 'warning', description: 'Config schema default does not match type' },
|
|
76
|
+
{ id: 'event-map-mismatch', layer: 'base', severity: 'warning', description: 'Event handler type mismatch with event map' },
|
|
77
|
+
{ id: 'non-exhaustive-switch', layer: 'base', severity: 'warning', description: 'Switch/map missing cases for known variants' },
|
|
78
|
+
{ id: 'cognitive-complexity', layer: 'base', severity: 'warning', description: 'Function exceeds cognitive complexity threshold' },
|
|
79
|
+
{ id: 'template-available', layer: 'base', severity: 'info', description: 'Pattern matches a registered KERN template' },
|
|
80
|
+
{ id: 'handler-extraction', layer: 'base', severity: 'info', description: 'Handler-like pattern could be extracted to KERN' },
|
|
81
|
+
{ id: 'memory-leak', layer: 'base', severity: 'error', description: 'Event listener added without cleanup' },
|
|
82
|
+
{ id: 'unhandled-async', layer: 'base', severity: 'warning', description: 'Async function without error handling' },
|
|
83
|
+
{ id: 'sync-in-async', layer: 'base', severity: 'warning', description: 'Synchronous blocking call inside async function' },
|
|
84
|
+
{ id: 'bare-rethrow', layer: 'base', severity: 'warning', description: 'Catch rethrows error without adding context' },
|
|
85
|
+
// Security
|
|
86
|
+
{ id: 'xss-unsafe-html', layer: 'security', severity: 'error', description: 'innerHTML/dangerouslySetInnerHTML with untrusted data' },
|
|
87
|
+
{ id: 'hardcoded-secret', layer: 'security', severity: 'error', description: 'API key, password, or secret in source code' },
|
|
88
|
+
{ id: 'command-injection', layer: 'security', severity: 'error', description: 'exec/spawn with user-controlled input' },
|
|
89
|
+
{ id: 'no-eval', layer: 'security', severity: 'error', description: 'eval() or Function() constructor usage' },
|
|
90
|
+
{ id: 'insecure-random', layer: 'security', severity: 'warning', description: 'Math.random() used for security-sensitive operations' },
|
|
91
|
+
{ id: 'cors-wildcard', layer: 'security', severity: 'warning', description: 'CORS wildcard (*) origin allowed' },
|
|
92
|
+
{ id: 'helmet-missing', layer: 'security', severity: 'warning', description: 'Express app without helmet security headers' },
|
|
93
|
+
{ id: 'open-redirect', layer: 'security', severity: 'error', description: 'Unvalidated redirect target from user input' },
|
|
94
|
+
// Security v2
|
|
95
|
+
{ id: 'jwt-weak-verification', layer: 'security-v2', severity: 'warning', description: 'JWT verified without algorithm restriction' },
|
|
96
|
+
{ id: 'cookie-hardening', layer: 'security-v2', severity: 'error', description: 'Cookie missing secure/httpOnly/sameSite flags' },
|
|
97
|
+
{ id: 'csrf-detection', layer: 'security-v2', severity: 'error', description: 'State-changing endpoint without CSRF protection' },
|
|
98
|
+
{ id: 'csp-strength', layer: 'security-v2', severity: 'warning', description: 'Weak Content-Security-Policy headers' },
|
|
99
|
+
{ id: 'path-traversal', layer: 'security-v2', severity: 'error', description: 'File path from user input without sanitization' },
|
|
100
|
+
{ id: 'weak-password-hashing', layer: 'security-v2', severity: 'error', description: 'MD5/SHA1 for password hashing instead of bcrypt/argon2' },
|
|
101
|
+
// Security v3 — OWASP gap closure
|
|
102
|
+
{ id: 'regex-dos', layer: 'security-v3', severity: 'warning', description: 'Regex vulnerable to catastrophic backtracking (ReDoS)' },
|
|
103
|
+
{ id: 'missing-input-validation', layer: 'security-v3', severity: 'warning', description: 'User input used without validation' },
|
|
104
|
+
{ id: 'prototype-pollution', layer: 'security-v3', severity: 'error', description: 'Object.prototype mutation via user-controlled keys' },
|
|
105
|
+
{ id: 'information-exposure', layer: 'security-v3', severity: 'error', description: 'Stack traces or internal details in error responses' },
|
|
106
|
+
{ id: 'prompt-injection', layer: 'security-v3', severity: 'warning', description: 'User input concatenated into LLM prompts' },
|
|
107
|
+
// Security v4 — LLM attack surface
|
|
108
|
+
{ id: 'indirect-prompt-injection', layer: 'security-v4', severity: 'warning', description: 'LLM prompt includes data from external/DB sources' },
|
|
109
|
+
{ id: 'llm-output-execution', layer: 'security-v4', severity: 'error', description: 'LLM-generated code executed without sandboxing' },
|
|
110
|
+
{ id: 'system-prompt-leakage', layer: 'security-v4', severity: 'warning', description: 'System prompt exposed in error paths or responses' },
|
|
111
|
+
{ id: 'rag-poisoning', layer: 'security-v4', severity: 'warning', description: 'RAG documents injected without provenance check' },
|
|
112
|
+
{ id: 'tool-calling-manipulation', layer: 'security-v4', severity: 'error', description: 'Tool/function call parameters from untrusted LLM output' },
|
|
113
|
+
{ id: 'encoding-bypass', layer: 'security-v4', severity: 'warning', description: 'Base64/unicode encoding used to bypass prompt filters' },
|
|
114
|
+
{ id: 'delimiter-injection', layer: 'security-v4', severity: 'warning', description: 'Prompt delimiter breakout via user input' },
|
|
115
|
+
{ id: 'unsanitized-history', layer: 'security-v4', severity: 'warning', description: 'Chat history concatenated without sanitization' },
|
|
116
|
+
{ id: 'json-output-manipulation', layer: 'security-v4', severity: 'warning', description: 'LLM JSON output used without schema validation' },
|
|
117
|
+
{ id: 'missing-output-validation', layer: 'security-v4', severity: 'warning', description: 'LLM output consumed without validation' },
|
|
118
|
+
// Dead logic
|
|
119
|
+
{ id: 'identical-conditions', layer: 'dead-logic', severity: 'error', description: 'Duplicate conditions in if/else chain' },
|
|
120
|
+
{ id: 'identical-expressions', layer: 'dead-logic', severity: 'error', description: 'Same expression on both sides of operator' },
|
|
121
|
+
{ id: 'all-identical-branches', layer: 'dead-logic', severity: 'error', description: 'All branches produce identical code' },
|
|
122
|
+
{ id: 'constant-condition', layer: 'dead-logic', severity: 'warning', description: 'Condition is always true or always false' },
|
|
123
|
+
{ id: 'one-iteration-loop', layer: 'dead-logic', severity: 'warning', description: 'Loop body always exits on first iteration' },
|
|
124
|
+
{ id: 'unused-collection', layer: 'dead-logic', severity: 'warning', description: 'Collection created but never read' },
|
|
125
|
+
{ id: 'empty-collection-access', layer: 'dead-logic', severity: 'warning', description: 'Accessing elements of provably empty collection' },
|
|
126
|
+
{ id: 'redundant-jump', layer: 'dead-logic', severity: 'info', description: 'Unreachable code after return/break/continue' },
|
|
127
|
+
// Null safety
|
|
128
|
+
{ id: 'unchecked-find', layer: 'null-safety', severity: 'warning', description: 'array.find() result used without null check' },
|
|
129
|
+
{ id: 'optional-chain-bang', layer: 'null-safety', severity: 'warning', description: 'Optional chain (?) immediately negated by non-null assertion (!)' },
|
|
130
|
+
{ id: 'unchecked-cast', layer: 'null-safety', severity: 'warning', description: 'Unsafe type assertion without runtime guard' },
|
|
131
|
+
// React (target: nextjs, tailwind, web, native, ink)
|
|
132
|
+
{ id: 'async-effect', layer: 'react', severity: 'error', description: 'Async function passed directly to useEffect' },
|
|
133
|
+
{ id: 'render-side-effect', layer: 'react', severity: 'error', description: 'Side effect (fetch, mutation) during render' },
|
|
134
|
+
{ id: 'unstable-key', layer: 'react', severity: 'warning', description: 'Non-stable key prop (index, random, Date.now)' },
|
|
135
|
+
{ id: 'stale-closure', layer: 'react', severity: 'warning', description: 'Stale variable captured in hook closure' },
|
|
136
|
+
{ id: 'state-explosion', layer: 'react', severity: 'warning', description: 'Excessive useState calls — consider useReducer' },
|
|
137
|
+
{ id: 'hook-order', layer: 'react', severity: 'error', description: 'React hook called inside condition or loop' },
|
|
138
|
+
{ id: 'effect-self-update-loop', layer: 'react', severity: 'error', description: 'useEffect updates its own dependency — infinite loop' },
|
|
139
|
+
// CLI (target: cli)
|
|
140
|
+
{ id: 'cli-missing-shebang', layer: 'cli', severity: 'warning', description: 'Commander CLI entrypoint missing #!/usr/bin/env node' },
|
|
141
|
+
{ id: 'cli-missing-parse', layer: 'cli', severity: 'error', description: 'Command instance created without parse()/parseAsync()' },
|
|
142
|
+
{ id: 'cli-async-parse-sync', layer: 'cli', severity: 'error', description: 'Async Commander action paired with parse() instead of parseAsync()' },
|
|
143
|
+
{ id: 'cli-process-exit-in-action', layer: 'cli', severity: 'warning', description: 'Commander action handler calls process.exit() directly' },
|
|
144
|
+
// Vue (target: vue, nuxt)
|
|
145
|
+
{ id: 'missing-ref-value', layer: 'vue', severity: 'warning', description: 'ref() used without .value in script setup' },
|
|
146
|
+
{ id: 'missing-onUnmounted', layer: 'vue', severity: 'error', description: 'watch/addEventListener without onUnmounted cleanup' },
|
|
147
|
+
{ id: 'setup-side-effect', layer: 'vue', severity: 'warning', description: 'Top-level await in setup without onMounted' },
|
|
148
|
+
{ id: 'reactive-destructure', layer: 'vue', severity: 'warning', description: 'Destructuring reactive() loses reactivity' },
|
|
149
|
+
// Terminal (target: terminal)
|
|
150
|
+
{ id: 'terminal-missing-tty-guard', layer: 'terminal', severity: 'warning', description: 'Interactive terminal code runs without TTY guard' },
|
|
151
|
+
{ id: 'terminal-raw-mode-no-restore', layer: 'terminal', severity: 'error', description: 'stdin raw mode enabled without restore on exit' },
|
|
152
|
+
{ id: 'terminal-readline-no-close', layer: 'terminal', severity: 'warning', description: 'Readline interface never closed — process can hang' },
|
|
153
|
+
{ id: 'terminal-alt-screen-no-restore', layer: 'terminal', severity: 'warning', description: 'Alternate screen entered without restore on exit' },
|
|
154
|
+
{ id: 'terminal-missing-signal-handler', layer: 'terminal', severity: 'warning', description: 'No SIGINT/SIGTERM handler for cleanup' },
|
|
155
|
+
{ id: 'terminal-cursor-not-restored', layer: 'terminal', severity: 'warning', description: 'Cursor hidden without restore on exit' },
|
|
156
|
+
{ id: 'terminal-unthrottled-render', layer: 'terminal', severity: 'warning', description: 'Render loop with excessive refresh rate' },
|
|
157
|
+
// Ink (target: ink, on top of React)
|
|
158
|
+
{ id: 'ink-console-output', layer: 'ink', severity: 'warning', description: 'console.* output corrupts Ink terminal rendering' },
|
|
159
|
+
{ id: 'ink-direct-stdout', layer: 'ink', severity: 'error', description: 'Direct stdout/stderr writes bypass Ink renderer' },
|
|
160
|
+
{ id: 'ink-process-exit', layer: 'ink', severity: 'warning', description: 'process.exit() used instead of useApp().exit()' },
|
|
161
|
+
{ id: 'ink-stdin-bypass', layer: 'ink', severity: 'warning', description: 'Raw stdin/readline listeners bypass Ink useInput()' },
|
|
162
|
+
{ id: 'ink-uncleared-interval', layer: 'ink', severity: 'warning', description: 'setInterval without cleanup in Ink component' },
|
|
163
|
+
{ id: 'ink-missing-error-boundary', layer: 'ink', severity: 'warning', description: 'Ink render() without error handling' },
|
|
164
|
+
// Next.js (target: nextjs)
|
|
165
|
+
{ id: 'server-hook', layer: 'nextjs', severity: 'error', description: 'React hook used in Server Component' },
|
|
166
|
+
{ id: 'hydration-mismatch', layer: 'nextjs', severity: 'warning', description: 'Nondeterministic expression causes SSR/client mismatch' },
|
|
167
|
+
{ id: 'missing-use-client', layer: 'nextjs', severity: 'warning', description: 'Event handler in Server Component — needs use client' },
|
|
168
|
+
// Nuxt (target: nuxt)
|
|
169
|
+
{ id: 'missing-ssr-guard', layer: 'nuxt', severity: 'error', description: 'Browser global accessed without SSR guard' },
|
|
170
|
+
{ id: 'nuxt-direct-fetch', layer: 'nuxt', severity: 'warning', description: 'Raw fetch() instead of $fetch/useFetch in Nuxt component' },
|
|
171
|
+
{ id: 'server-route-leak', layer: 'nuxt', severity: 'error', description: 'Server API route may expose sensitive fields' },
|
|
172
|
+
// Express (target: express)
|
|
173
|
+
{ id: 'unvalidated-input', layer: 'express', severity: 'error', description: 'req.body/params/query used without validation' },
|
|
174
|
+
{ id: 'missing-error-middleware', layer: 'express', severity: 'warning', description: 'Express app without error-handling middleware' },
|
|
175
|
+
{ id: 'sync-in-handler', layer: 'express', severity: 'warning', description: 'Blocking I/O in request handler' },
|
|
176
|
+
{ id: 'double-response', layer: 'express', severity: 'error', description: 'Response sent twice without early return' },
|
|
177
|
+
// FastAPI (target: fastapi, concept-based Python pipeline)
|
|
178
|
+
{ id: 'fastapi-missing-response-model', layer: 'fastapi', severity: 'warning', description: 'Endpoint without response_model — undocumented response' },
|
|
179
|
+
{ id: 'fastapi-blocking-sync-route', layer: 'fastapi', severity: 'warning', description: 'Blocking call in async route stalls event loop' },
|
|
180
|
+
{ id: 'fastapi-shared-state', layer: 'fastapi', severity: 'error', description: 'Route mutates global/module state — race condition' },
|
|
181
|
+
{ id: 'fastapi-broad-except', layer: 'fastapi', severity: 'warning', description: 'Broad except without re-raising HTTPException' },
|
|
182
|
+
// Concept rules (always active, language-agnostic)
|
|
183
|
+
{ id: 'boundary-mutation', layer: 'concept', severity: 'warning', description: 'Global/shared state mutation across boundaries' },
|
|
184
|
+
{ id: 'ignored-error', layer: 'concept', severity: 'warning', description: 'Caught exception silently ignored' },
|
|
185
|
+
{ id: 'unguarded-effect', layer: 'concept', severity: 'warning', description: 'Network/DB effect without auth/validation guard' },
|
|
186
|
+
{ id: 'unrecovered-effect', layer: 'concept', severity: 'warning', description: 'Network/DB effect without error recovery' },
|
|
187
|
+
];
|
|
188
|
+
/** Layer → target mapping for filtering */
|
|
189
|
+
const LAYER_TARGET_MAP = {
|
|
190
|
+
base: null, // always active
|
|
191
|
+
security: null,
|
|
192
|
+
'security-v2': null,
|
|
193
|
+
'security-v3': null,
|
|
194
|
+
'security-v4': null,
|
|
195
|
+
'dead-logic': null,
|
|
196
|
+
'null-safety': null,
|
|
197
|
+
concept: null,
|
|
198
|
+
react: ['nextjs', 'tailwind', 'web', 'native', 'ink'],
|
|
199
|
+
cli: ['cli'],
|
|
200
|
+
vue: ['vue', 'nuxt'],
|
|
201
|
+
ink: ['ink'],
|
|
202
|
+
terminal: ['terminal'],
|
|
203
|
+
nextjs: ['nextjs'],
|
|
204
|
+
nuxt: ['nuxt'],
|
|
205
|
+
express: ['express'],
|
|
206
|
+
fastapi: ['fastapi'],
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Get the rule registry, optionally filtered by target.
|
|
210
|
+
* Returns all rules active for the given target (universal + framework-specific).
|
|
211
|
+
*/
|
|
212
|
+
export function getRuleRegistry(target) {
|
|
213
|
+
if (!target)
|
|
214
|
+
return [...REGISTRY];
|
|
215
|
+
return REGISTRY.filter(r => {
|
|
216
|
+
const targets = LAYER_TARGET_MAP[r.layer];
|
|
217
|
+
return targets === null || targets.includes(target);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
44
220
|
//# sourceMappingURL=index.js.map
|
package/dist/rules/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;AACvE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7C;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,MAAM,KAAK,GAAiB,CAAC,GAAG,SAAS,EAAE,GAAG,aAAa,EAAE,GAAG,eAAe,EAAE,GAAG,eAAe,EAAE,GAAG,eAAe,EAAE,GAAG,cAAc,EAAE,GAAG,eAAe,CAAC,CAAC;IAEhK,IAAI,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ink review rules — active when target = ink (on top of React rules).
|
|
3
|
+
*
|
|
4
|
+
* Focused on Ink terminal rendering / input handling pitfalls.
|
|
5
|
+
* Codex base rules + Claude extras (uncleared-interval, missing-error-boundary).
|
|
6
|
+
*/
|
|
7
|
+
import type { ReviewRule } from '../types.js';
|
|
8
|
+
export declare const inkRules: ReviewRule[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ink review rules — active when target = ink (on top of React rules).
|
|
3
|
+
*
|
|
4
|
+
* Focused on Ink terminal rendering / input handling pitfalls.
|
|
5
|
+
* Codex base rules + Claude extras (uncleared-interval, missing-error-boundary).
|
|
6
|
+
*/
|
|
7
|
+
import { finding } from './utils.js';
|
|
8
|
+
function lineForIndex(text, index) {
|
|
9
|
+
return text.slice(0, index).split('\n').length;
|
|
10
|
+
}
|
|
11
|
+
function findAll(text, pattern) {
|
|
12
|
+
const lines = [];
|
|
13
|
+
let match;
|
|
14
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
15
|
+
lines.push(lineForIndex(text, match.index));
|
|
16
|
+
}
|
|
17
|
+
return lines;
|
|
18
|
+
}
|
|
19
|
+
function isInkFile(text) {
|
|
20
|
+
return /from\s+['"]ink['"]|require\s*\(\s*['"]ink['"]\s*\)/.test(text);
|
|
21
|
+
}
|
|
22
|
+
// ── Rule: ink-console-output (Codex) ────────────────────────────────────
|
|
23
|
+
function consoleOutput(ctx) {
|
|
24
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
25
|
+
if (!isInkFile(fullText))
|
|
26
|
+
return [];
|
|
27
|
+
return findAll(fullText, /\bconsole\.(?:log|warn|error|info)\s*\(/g).map(line => finding('ink-console-output', 'warning', 'bug', 'Ink app writes through console.* — direct console output corrupts the rendered terminal frame', ctx.filePath, line, 1, { suggestion: 'Render status via <Text>, <Static>, or useStdout()/useStderr() instead of console.*' }));
|
|
28
|
+
}
|
|
29
|
+
// ── Rule: ink-direct-stdout (Codex) ─────────────────────────────────────
|
|
30
|
+
function directStdout(ctx) {
|
|
31
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
32
|
+
if (!isInkFile(fullText))
|
|
33
|
+
return [];
|
|
34
|
+
return findAll(fullText, /\bprocess\.(?:stdout|stderr)\.write\s*\(/g).map(line => finding('ink-direct-stdout', 'error', 'bug', 'Ink app writes directly to stdout/stderr — bypassing Ink output breaks layout reconciliation', ctx.filePath, line, 1, { suggestion: 'Use Ink components or useStdout()/useStderr() so output stays inside Ink\'s renderer' }));
|
|
35
|
+
}
|
|
36
|
+
// ── Rule: ink-process-exit (Codex) ──────────────────────────────────────
|
|
37
|
+
function processExit(ctx) {
|
|
38
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
39
|
+
if (!isInkFile(fullText))
|
|
40
|
+
return [];
|
|
41
|
+
return findAll(fullText, /\bprocess\.exit\s*\(/g).map(line => finding('ink-process-exit', 'warning', 'pattern', 'Ink app calls process.exit() directly — this skips Ink cleanup and can leave the terminal in a dirty state', ctx.filePath, line, 1, { suggestion: 'Use const { exit } = useApp(); exit(); so Ink can clean up properly' }));
|
|
42
|
+
}
|
|
43
|
+
// ── Rule: ink-stdin-bypass (Codex) ──────────────────────────────────────
|
|
44
|
+
function stdinBypass(ctx) {
|
|
45
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
46
|
+
if (!isInkFile(fullText))
|
|
47
|
+
return [];
|
|
48
|
+
const pattern = /\bprocess\.stdin\.(?:on|addListener|once|resume|setRawMode)\s*\(|\breadline\.createInterface\s*\(/g;
|
|
49
|
+
return findAll(fullText, pattern).map(line => finding('ink-stdin-bypass', 'warning', 'pattern', 'Ink app bypasses useInput() with raw stdin/readline listeners — input handling will fight with Ink\'s renderer', ctx.filePath, line, 1, { suggestion: 'Handle keyboard input with useInput() or useStdin() instead of process.stdin/readline listeners' }));
|
|
50
|
+
}
|
|
51
|
+
// ── Rule: ink-uncleared-interval (Claude) ───────────────────────────────
|
|
52
|
+
function unclearedInterval(ctx) {
|
|
53
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
54
|
+
if (!isInkFile(fullText))
|
|
55
|
+
return [];
|
|
56
|
+
// Already has cleanup
|
|
57
|
+
if (fullText.includes('clearInterval'))
|
|
58
|
+
return [];
|
|
59
|
+
return findAll(fullText, /\bsetInterval\s*\(/g).map(line => finding('ink-uncleared-interval', 'warning', 'bug', 'setInterval without clearInterval in Ink component — timer leaks on unmount', ctx.filePath, line, 1, { suggestion: 'Store the interval ID and clear it in useEffect cleanup: return () => clearInterval(id)' }));
|
|
60
|
+
}
|
|
61
|
+
// ── Rule: ink-missing-error-boundary (Claude) ───────────────────────────
|
|
62
|
+
function missingErrorBoundary(ctx) {
|
|
63
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
64
|
+
if (!isInkFile(fullText))
|
|
65
|
+
return [];
|
|
66
|
+
// Check for Ink render() call — this is the app entry point
|
|
67
|
+
const renderMatch = fullText.match(/\brender\s*\(\s*<\w+/);
|
|
68
|
+
if (!renderMatch)
|
|
69
|
+
return [];
|
|
70
|
+
const hasErrorBoundary = fullText.includes('ErrorBoundary') || fullText.includes('errorBoundary');
|
|
71
|
+
const hasExitHandler = /\.waitUntilExit\s*\(\s*\)\s*\.catch/.test(fullText) ||
|
|
72
|
+
/try\s*\{[^}]*render/.test(fullText);
|
|
73
|
+
if (!hasErrorBoundary && !hasExitHandler) {
|
|
74
|
+
const line = lineForIndex(fullText, renderMatch.index);
|
|
75
|
+
return [finding('ink-missing-error-boundary', 'warning', 'pattern', 'Ink render() without error handling — uncaught errors will corrupt terminal state', ctx.filePath, line, 1, { suggestion: 'Add .waitUntilExit().catch() or wrap with an ErrorBoundary component' })];
|
|
76
|
+
}
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
export const inkRules = [
|
|
80
|
+
consoleOutput,
|
|
81
|
+
directStdout,
|
|
82
|
+
processExit,
|
|
83
|
+
stdinBypass,
|
|
84
|
+
unclearedInterval,
|
|
85
|
+
missingErrorBoundary,
|
|
86
|
+
];
|
|
87
|
+
//# sourceMappingURL=ink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ink.js","sourceRoot":"","sources":["../../src/rules/ink.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nuxt review rules — active when target = nuxt (on top of Vue rules).
|
|
3
|
+
*
|
|
4
|
+
* Catches Nuxt 3 SSR / auto-import / server-route pitfalls.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReviewFinding, RuleContext } from '../types.js';
|
|
7
|
+
declare function missingSsrGuard(ctx: RuleContext): ReviewFinding[];
|
|
8
|
+
export declare const nuxtRules: (typeof missingSsrGuard)[];
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nuxt review rules — active when target = nuxt (on top of Vue rules).
|
|
3
|
+
*
|
|
4
|
+
* Catches Nuxt 3 SSR / auto-import / server-route pitfalls.
|
|
5
|
+
*/
|
|
6
|
+
import { SyntaxKind } from 'ts-morph';
|
|
7
|
+
import { createFingerprint } from '../types.js';
|
|
8
|
+
function span(file, line, col = 1) {
|
|
9
|
+
return { file, startLine: line, startCol: col, endLine: line, endCol: col };
|
|
10
|
+
}
|
|
11
|
+
function finding(ruleId, severity, category, message, file, line, extra) {
|
|
12
|
+
return {
|
|
13
|
+
source: 'kern',
|
|
14
|
+
ruleId,
|
|
15
|
+
severity,
|
|
16
|
+
category,
|
|
17
|
+
message,
|
|
18
|
+
primarySpan: span(file, line),
|
|
19
|
+
fingerprint: createFingerprint(ruleId, line, 1),
|
|
20
|
+
...extra,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// ── Rule: missing-ssr-guard ─────────────────────────────────────────────
|
|
24
|
+
// window / document / localStorage access without process.client or import.meta.client guard
|
|
25
|
+
const BROWSER_GLOBALS = ['window', 'document', 'localStorage', 'sessionStorage', 'navigator', 'location'];
|
|
26
|
+
function missingSsrGuard(ctx) {
|
|
27
|
+
const findings = [];
|
|
28
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
29
|
+
// Skip client-only files
|
|
30
|
+
if (fullText.includes("'use client'") || fullText.includes('"use client"'))
|
|
31
|
+
return findings;
|
|
32
|
+
// Skip .client.ts/.client.vue files (Nuxt convention)
|
|
33
|
+
if (ctx.filePath.includes('.client.'))
|
|
34
|
+
return findings;
|
|
35
|
+
// Build safe ranges: if (process.client) { ... } or if (import.meta.client) { ... }
|
|
36
|
+
const safeRanges = [];
|
|
37
|
+
const guardRegex = /(?:process\.client|import\.meta\.client|typeof window\s*!==?\s*['"]undefined['"])/g;
|
|
38
|
+
let guardMatch;
|
|
39
|
+
while ((guardMatch = guardRegex.exec(fullText)) !== null) {
|
|
40
|
+
// Find the enclosing block — walk up to the if statement and capture the block
|
|
41
|
+
const ifStart = fullText.lastIndexOf('if', guardMatch.index);
|
|
42
|
+
if (ifStart === -1)
|
|
43
|
+
continue;
|
|
44
|
+
let depth = 0;
|
|
45
|
+
let blockStart = -1;
|
|
46
|
+
let blockEnd = -1;
|
|
47
|
+
for (let i = guardMatch.index; i < fullText.length; i++) {
|
|
48
|
+
if (fullText[i] === '{') {
|
|
49
|
+
if (depth === 0)
|
|
50
|
+
blockStart = i;
|
|
51
|
+
depth++;
|
|
52
|
+
}
|
|
53
|
+
if (fullText[i] === '}') {
|
|
54
|
+
depth--;
|
|
55
|
+
if (depth === 0) {
|
|
56
|
+
blockEnd = i;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (blockStart !== -1 && blockEnd !== -1) {
|
|
62
|
+
safeRanges.push([blockStart, blockEnd]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Also mark onMounted callbacks as safe (they only run on client)
|
|
66
|
+
const mountedRegex = /onMounted\s*\(/g;
|
|
67
|
+
let mountedMatch;
|
|
68
|
+
while ((mountedMatch = mountedRegex.exec(fullText)) !== null) {
|
|
69
|
+
let depth = 0;
|
|
70
|
+
let start = mountedMatch.index;
|
|
71
|
+
for (let i = start; i < fullText.length; i++) {
|
|
72
|
+
if (fullText[i] === '(')
|
|
73
|
+
depth++;
|
|
74
|
+
if (fullText[i] === ')') {
|
|
75
|
+
depth--;
|
|
76
|
+
if (depth === 0) {
|
|
77
|
+
safeRanges.push([start, i]);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const isInSafeRange = (idx) => safeRanges.some(([s, e]) => idx >= s && idx <= e);
|
|
84
|
+
// Find browser global usages
|
|
85
|
+
const reported = new Set();
|
|
86
|
+
for (const global of BROWSER_GLOBALS) {
|
|
87
|
+
const regex = new RegExp(`\\b${global}\\b(?!\\s*(?:!==?|===?)\\s*['"]?undefined)`, 'g');
|
|
88
|
+
let match;
|
|
89
|
+
while ((match = regex.exec(fullText)) !== null) {
|
|
90
|
+
if (isInSafeRange(match.index))
|
|
91
|
+
continue;
|
|
92
|
+
// Skip type-only contexts
|
|
93
|
+
const lineText = fullText.split('\n')[fullText.substring(0, match.index).split('\n').length - 1] || '';
|
|
94
|
+
if (lineText.trim().startsWith('//') || lineText.trim().startsWith('*'))
|
|
95
|
+
continue;
|
|
96
|
+
if (lineText.includes('typeof'))
|
|
97
|
+
continue;
|
|
98
|
+
// One finding per global to avoid noise
|
|
99
|
+
if (reported.has(global))
|
|
100
|
+
continue;
|
|
101
|
+
reported.add(global);
|
|
102
|
+
const line = fullText.substring(0, match.index).split('\n').length;
|
|
103
|
+
findings.push(finding('missing-ssr-guard', 'error', 'bug', `'${global}' accessed without SSR guard — will crash during server rendering`, ctx.filePath, line, { suggestion: `Wrap in if (process.client) { ... } or use onMounted()` }));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return findings;
|
|
107
|
+
}
|
|
108
|
+
// ── Rule: nuxt-direct-fetch ─────────────────────────────────────────────
|
|
109
|
+
// Using raw fetch() instead of $fetch / useFetch / useAsyncData in Nuxt
|
|
110
|
+
function nuxtDirectFetch(ctx) {
|
|
111
|
+
const findings = [];
|
|
112
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
113
|
+
// Only flag in pages/, components/, composables/, layouts/ — not in server/ or lib/
|
|
114
|
+
const isComponentFile = /\/(pages|components|composables|layouts)\//.test(ctx.filePath);
|
|
115
|
+
if (!isComponentFile)
|
|
116
|
+
return findings;
|
|
117
|
+
// Skip if file already uses $fetch / useFetch / useAsyncData
|
|
118
|
+
if (fullText.includes('$fetch') || fullText.includes('useFetch') || fullText.includes('useAsyncData')) {
|
|
119
|
+
return findings;
|
|
120
|
+
}
|
|
121
|
+
// Find raw fetch() calls
|
|
122
|
+
for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
123
|
+
const expr = call.getExpression();
|
|
124
|
+
if (expr.getKind() !== SyntaxKind.Identifier)
|
|
125
|
+
continue;
|
|
126
|
+
if (expr.getText() !== 'fetch')
|
|
127
|
+
continue;
|
|
128
|
+
const line = call.getStartLineNumber();
|
|
129
|
+
findings.push(finding('nuxt-direct-fetch', 'warning', 'pattern', `Raw fetch() in Nuxt component — use $fetch or useFetch for SSR support and auto-dedup`, ctx.filePath, line, { suggestion: 'Replace fetch() with $fetch() or useFetch() for proper SSR hydration' }));
|
|
130
|
+
break; // One finding per file
|
|
131
|
+
}
|
|
132
|
+
return findings;
|
|
133
|
+
}
|
|
134
|
+
// ── Rule: server-route-leak ─────────────────────────────────────────────
|
|
135
|
+
// Server API routes returning sensitive fields without filtering
|
|
136
|
+
const SENSITIVE_FIELDS = new Set(['password', 'passwordHash', 'secret', 'token', 'apiKey',
|
|
137
|
+
'api_key', 'accessToken', 'access_token', 'refreshToken', 'refresh_token',
|
|
138
|
+
'ssn', 'creditCard', 'credit_card']);
|
|
139
|
+
function serverRouteLeak(ctx) {
|
|
140
|
+
const findings = [];
|
|
141
|
+
// Only run on server/ API routes
|
|
142
|
+
if (!ctx.filePath.includes('/server/') || !ctx.filePath.includes('/api/'))
|
|
143
|
+
return findings;
|
|
144
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
145
|
+
// Look for return statements or send() calls that spread database objects
|
|
146
|
+
// Pattern: return { ...user } or return user (where user likely has sensitive fields)
|
|
147
|
+
for (const ret of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.ReturnStatement)) {
|
|
148
|
+
const expr = ret.getExpression();
|
|
149
|
+
if (!expr)
|
|
150
|
+
continue;
|
|
151
|
+
const text = expr.getText();
|
|
152
|
+
// Check for direct return of variables that likely contain sensitive data
|
|
153
|
+
for (const field of SENSITIVE_FIELDS) {
|
|
154
|
+
if (text.includes(field)) {
|
|
155
|
+
const line = ret.getStartLineNumber();
|
|
156
|
+
findings.push(finding('server-route-leak', 'error', 'bug', `Server API route may expose '${field}' — filter sensitive fields before returning`, ctx.filePath, line, { suggestion: 'Destructure and return only needed fields: const { password, ...safe } = user; return safe;' }));
|
|
157
|
+
return findings; // One finding per file
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return findings;
|
|
162
|
+
}
|
|
163
|
+
// ── Exported Nuxt Rules ─────────────────────────────────────────────────
|
|
164
|
+
export const nuxtRules = [
|
|
165
|
+
missingSsrGuard,
|
|
166
|
+
nuxtDirectFetch,
|
|
167
|
+
serverRouteLeak,
|
|
168
|
+
];
|
|
169
|
+
//# sourceMappingURL=nuxt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nuxt.js","sourceRoot":"","sources":["../../src/rules/nuxt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal review rules — active when target = terminal.
|
|
3
|
+
*
|
|
4
|
+
* Focused on interactive ANSI / readline terminal apps.
|
|
5
|
+
* Codex base rules + Claude extras (signal-handler, cursor-restore, unthrottled-render).
|
|
6
|
+
*/
|
|
7
|
+
import type { ReviewRule } from '../types.js';
|
|
8
|
+
export declare const terminalRules: ReviewRule[];
|