code-sentinel-mcp 0.1.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 +149 -0
- package/build/analyzers/deceptive.d.ts +2 -0
- package/build/analyzers/deceptive.js +200 -0
- package/build/analyzers/errors.d.ts +2 -0
- package/build/analyzers/errors.js +214 -0
- package/build/analyzers/patterns.d.ts +138 -0
- package/build/analyzers/patterns.js +801 -0
- package/build/analyzers/placeholders.d.ts +2 -0
- package/build/analyzers/placeholders.js +216 -0
- package/build/analyzers/security.d.ts +2 -0
- package/build/analyzers/security.js +238 -0
- package/build/analyzers/strengths.d.ts +2 -0
- package/build/analyzers/strengths.js +169 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +366 -0
- package/build/report.d.ts +2 -0
- package/build/report.js +228 -0
- package/build/types.d.ts +54 -0
- package/build/types.js +2 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# CodeSentinel MCP Server
|
|
2
|
+
|
|
3
|
+
A code quality analysis MCP server that detects security issues, deceptive patterns, placeholders, and highlights code strengths.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔒 **Security Analysis**: Hardcoded secrets, SQL injection, XSS, insecure crypto, and more
|
|
8
|
+
- 🎭 **Deceptive Pattern Detection**: Empty catch blocks, silent failures, error-hiding returns
|
|
9
|
+
- 📝 **Placeholder Detection**: TODO/FIXME, lorem ipsum, test data, incomplete implementations
|
|
10
|
+
- ⚠️ **Error & Code Smell Detection**: Type coercion, null references, async issues
|
|
11
|
+
- 💪 **Strength Recognition**: Highlights good practices like typing, error handling, tests
|
|
12
|
+
- 📊 **HTML Reports**: Beautiful visual reports with scores and suggestions
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Clone or copy the project
|
|
18
|
+
cd code-sentinel
|
|
19
|
+
|
|
20
|
+
# Install dependencies
|
|
21
|
+
npm install
|
|
22
|
+
|
|
23
|
+
# Build
|
|
24
|
+
npm run build
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage with Claude Code
|
|
28
|
+
|
|
29
|
+
Add to your Claude Code MCP configuration:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
claude mcp add code-sentinel -- node /path/to/code-sentinel/build/index.js
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or manually add to your config:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"code-sentinel": {
|
|
41
|
+
"command": "node",
|
|
42
|
+
"args": ["/path/to/code-sentinel/build/index.js"]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Available Tools
|
|
49
|
+
|
|
50
|
+
### `analyze_code`
|
|
51
|
+
Full analysis returning structured JSON with all issues and strengths.
|
|
52
|
+
|
|
53
|
+
### `generate_report`
|
|
54
|
+
Full analysis with HTML report output.
|
|
55
|
+
|
|
56
|
+
### `check_security`
|
|
57
|
+
Security-focused analysis only.
|
|
58
|
+
|
|
59
|
+
### `check_deceptive_patterns`
|
|
60
|
+
Check for error-hiding and deceptive code patterns.
|
|
61
|
+
|
|
62
|
+
### `check_placeholders`
|
|
63
|
+
Find TODOs, dummy data, and incomplete code.
|
|
64
|
+
|
|
65
|
+
## Example Usage in Claude Code
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
Analyze this code for quality issues:
|
|
69
|
+
|
|
70
|
+
const API_KEY = "sk-abc123456789";
|
|
71
|
+
|
|
72
|
+
async function fetchData() {
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(url);
|
|
75
|
+
return response.json();
|
|
76
|
+
} catch (e) {
|
|
77
|
+
// TODO: handle error
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Claude will automatically use CodeSentinel to analyze and report:
|
|
83
|
+
- Critical: Hardcoded API key detected
|
|
84
|
+
- High: Empty catch block (deceptive pattern)
|
|
85
|
+
- Low: TODO comment found
|
|
86
|
+
|
|
87
|
+
## Detection Categories
|
|
88
|
+
|
|
89
|
+
### Security Issues (SEC)
|
|
90
|
+
- Hardcoded secrets (API keys, tokens, passwords)
|
|
91
|
+
- SQL injection patterns
|
|
92
|
+
- XSS vulnerabilities
|
|
93
|
+
- Insecure random, weak crypto
|
|
94
|
+
- Disabled SSL validation
|
|
95
|
+
- Eval and dynamic code execution
|
|
96
|
+
|
|
97
|
+
### Deceptive Patterns (DEC)
|
|
98
|
+
- Empty catch blocks
|
|
99
|
+
- Silent promise rejections
|
|
100
|
+
- Error-hiding fallbacks (|| [], || {})
|
|
101
|
+
- Fake success responses
|
|
102
|
+
- Excessive optional chaining
|
|
103
|
+
- Linter suppression comments
|
|
104
|
+
|
|
105
|
+
### Placeholders (PH)
|
|
106
|
+
- TODO/FIXME/HACK/XXX comments
|
|
107
|
+
- Lorem ipsum text
|
|
108
|
+
- Test emails and passwords
|
|
109
|
+
- Debug console.log
|
|
110
|
+
- Debugger statements
|
|
111
|
+
- Not implemented errors
|
|
112
|
+
|
|
113
|
+
### Errors & Smells (ERR)
|
|
114
|
+
- Loose equality (==)
|
|
115
|
+
- Assignment in conditions
|
|
116
|
+
- parseInt without radix
|
|
117
|
+
- Array mutation during iteration
|
|
118
|
+
- Floating point comparison
|
|
119
|
+
- Await in constructor
|
|
120
|
+
|
|
121
|
+
## Scoring
|
|
122
|
+
|
|
123
|
+
The quality score (0-100) is calculated based on:
|
|
124
|
+
- Critical issues: -25 points each
|
|
125
|
+
- High issues: -15 points each
|
|
126
|
+
- Medium issues: -5 points each
|
|
127
|
+
- Low issues: -1 point each
|
|
128
|
+
- Strengths: +2 points each
|
|
129
|
+
|
|
130
|
+
## Extending
|
|
131
|
+
|
|
132
|
+
Add new patterns by editing the analyzer files in `src/analyzers/`:
|
|
133
|
+
- `security.ts` - Security vulnerability patterns
|
|
134
|
+
- `deceptive.ts` - Error-hiding patterns
|
|
135
|
+
- `placeholders.ts` - Incomplete code patterns
|
|
136
|
+
- `errors.ts` - Code smell patterns
|
|
137
|
+
- `strengths.ts` - Good practice patterns
|
|
138
|
+
|
|
139
|
+
Each pattern includes:
|
|
140
|
+
- `id`: Unique identifier (e.g., SEC001)
|
|
141
|
+
- `pattern`: RegExp to match
|
|
142
|
+
- `title`: Short description
|
|
143
|
+
- `description`: Detailed explanation
|
|
144
|
+
- `severity`: critical | high | medium | low
|
|
145
|
+
- `suggestion`: How to fix (optional)
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// These patterns detect code that hides errors or creates false confidence
|
|
2
|
+
const deceptivePatterns = [
|
|
3
|
+
// Empty catch blocks
|
|
4
|
+
{
|
|
5
|
+
id: 'CS-DEC001',
|
|
6
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*\}/g,
|
|
7
|
+
title: 'Empty Catch Block',
|
|
8
|
+
description: 'Silently swallowing errors makes debugging impossible.',
|
|
9
|
+
severity: 'high',
|
|
10
|
+
category: 'deceptive',
|
|
11
|
+
suggestion: 'At minimum, log the error. Better: handle it appropriately or rethrow.'
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'CS-DEC002',
|
|
15
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*\/\/.*?\s*\}/gs,
|
|
16
|
+
title: 'Catch Block with Only Comments',
|
|
17
|
+
description: 'A catch block with only comments still swallows errors.',
|
|
18
|
+
severity: 'high',
|
|
19
|
+
category: 'deceptive',
|
|
20
|
+
suggestion: 'Add actual error handling, not just comments.'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'CS-DEC003',
|
|
24
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*(?:console\.log|print)\s*\([^)]*\)\s*;?\s*\}/g,
|
|
25
|
+
title: 'Catch with Only Console.log',
|
|
26
|
+
description: 'Logging alone does not handle the error - execution continues as if nothing happened.',
|
|
27
|
+
severity: 'medium',
|
|
28
|
+
category: 'deceptive',
|
|
29
|
+
suggestion: 'Decide: should execution continue? Add recovery logic or rethrow.'
|
|
30
|
+
},
|
|
31
|
+
// Silent promise rejections
|
|
32
|
+
{
|
|
33
|
+
id: 'CS-DEC010',
|
|
34
|
+
pattern: /\.catch\s*\(\s*\(\s*\)\s*=>\s*\{\s*\}\s*\)/g,
|
|
35
|
+
title: 'Empty Promise Catch',
|
|
36
|
+
description: 'Silently ignoring promise rejections hides async errors.',
|
|
37
|
+
severity: 'high',
|
|
38
|
+
category: 'deceptive',
|
|
39
|
+
suggestion: 'Handle the rejection or let it propagate for proper error handling.'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'CS-DEC011',
|
|
43
|
+
pattern: /\.catch\s*\(\s*\(\s*\)\s*=>\s*(?:null|undefined|false|true|''|"")\s*\)/g,
|
|
44
|
+
title: 'Promise Catch Returns Silent Value',
|
|
45
|
+
description: 'Returning a value from catch masks the error - callers won\'t know something failed.',
|
|
46
|
+
severity: 'high',
|
|
47
|
+
category: 'deceptive',
|
|
48
|
+
suggestion: 'Return a distinguishable error state or rethrow.'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'CS-DEC012',
|
|
52
|
+
pattern: /\.catch\s*\(\s*_\s*=>\s*\{?\s*\}?\s*\)/g,
|
|
53
|
+
title: 'Catch with Ignored Error Parameter',
|
|
54
|
+
description: 'Using _ for error parameter signals intentional ignore - but is it really safe to ignore?',
|
|
55
|
+
severity: 'medium',
|
|
56
|
+
category: 'deceptive',
|
|
57
|
+
suggestion: 'Document why ignoring this error is safe, or handle it.'
|
|
58
|
+
},
|
|
59
|
+
// Fallback values that mask failures
|
|
60
|
+
{
|
|
61
|
+
id: 'CS-DEC020',
|
|
62
|
+
pattern: /\|\|\s*\[\s*\]/g,
|
|
63
|
+
title: 'Empty Array Fallback',
|
|
64
|
+
description: 'Falling back to [] can mask failed data fetching - code continues as if data was empty.',
|
|
65
|
+
severity: 'medium',
|
|
66
|
+
category: 'deceptive',
|
|
67
|
+
suggestion: 'Distinguish between "no data" and "failed to fetch". Consider throwing or returning null.'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'CS-DEC021',
|
|
71
|
+
pattern: /\|\|\s*\{\s*\}/g,
|
|
72
|
+
title: 'Empty Object Fallback',
|
|
73
|
+
description: 'Falling back to {} can hide parsing or fetching failures.',
|
|
74
|
+
severity: 'medium',
|
|
75
|
+
category: 'deceptive',
|
|
76
|
+
suggestion: 'Handle the undefined/null case explicitly rather than masking it.'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'CS-DEC022',
|
|
80
|
+
pattern: /\?\?\s*(?:\[\s*\]|\{\s*\}|''|"")/g,
|
|
81
|
+
title: 'Nullish Coalescing to Empty Value',
|
|
82
|
+
description: 'Defaulting to empty values with ?? can mask null responses that indicate errors.',
|
|
83
|
+
severity: 'low',
|
|
84
|
+
category: 'deceptive',
|
|
85
|
+
suggestion: 'Verify that null/undefined truly means "use default" vs "something went wrong".'
|
|
86
|
+
},
|
|
87
|
+
// Optional chaining abuse
|
|
88
|
+
{
|
|
89
|
+
id: 'CS-DEC030',
|
|
90
|
+
pattern: /\?\.\w+\?\.\w+\?\.\w+\?\.\w+/g,
|
|
91
|
+
title: 'Excessive Optional Chaining',
|
|
92
|
+
description: 'Deep optional chaining (4+ levels) often masks structural problems or missing validation.',
|
|
93
|
+
severity: 'medium',
|
|
94
|
+
category: 'deceptive',
|
|
95
|
+
suggestion: 'Validate data shape upfront rather than optional-chaining through uncertain structures.'
|
|
96
|
+
},
|
|
97
|
+
// Error-hiding returns
|
|
98
|
+
{
|
|
99
|
+
id: 'CS-DEC040',
|
|
100
|
+
pattern: /return\s+(?:null|undefined|false)\s*;?\s*\/\/\s*(?:error|fail|todo|fixme)/gi,
|
|
101
|
+
title: 'Silent Error Return',
|
|
102
|
+
description: 'Returning null/false on error with a comment - callers may not check for this.',
|
|
103
|
+
severity: 'high',
|
|
104
|
+
category: 'deceptive',
|
|
105
|
+
suggestion: 'Throw an error or return a Result/Either type that forces handling.'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'CS-DEC041',
|
|
109
|
+
pattern: /if\s*\([^)]*error[^)]*\)\s*\{\s*return\s*;?\s*\}/gi,
|
|
110
|
+
title: 'Silent Return on Error',
|
|
111
|
+
description: 'Returning silently when an error is detected - no logging, no propagation.',
|
|
112
|
+
severity: 'high',
|
|
113
|
+
category: 'deceptive',
|
|
114
|
+
suggestion: 'Log the error or throw it. Silent returns make debugging a nightmare.'
|
|
115
|
+
},
|
|
116
|
+
// Timeout-based "fixes"
|
|
117
|
+
{
|
|
118
|
+
id: 'CS-DEC050',
|
|
119
|
+
pattern: /setTimeout\s*\([^,]+,\s*\d+\s*\)\s*;?\s*\/\/.*?(?:fix|hack|workaround|retry)/gi,
|
|
120
|
+
title: 'Timeout as Error Workaround',
|
|
121
|
+
description: 'Using setTimeout to "fix" timing issues often masks race conditions.',
|
|
122
|
+
severity: 'medium',
|
|
123
|
+
category: 'deceptive',
|
|
124
|
+
suggestion: 'Fix the underlying race condition. Use proper async coordination.'
|
|
125
|
+
},
|
|
126
|
+
// Suppressed warnings/errors
|
|
127
|
+
{
|
|
128
|
+
id: 'CS-DEC060',
|
|
129
|
+
pattern: /\/\/\s*(?:@ts-ignore|@ts-expect-error|eslint-disable|noqa)/g,
|
|
130
|
+
title: 'Linter/Type Check Suppression',
|
|
131
|
+
description: 'Suppressing type errors or linter warnings may hide real issues.',
|
|
132
|
+
severity: 'low',
|
|
133
|
+
category: 'deceptive',
|
|
134
|
+
suggestion: 'Fix the underlying issue. If suppression is needed, document why.'
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'CS-DEC061',
|
|
138
|
+
pattern: /as\s+any(?!\w)/g,
|
|
139
|
+
title: 'TypeScript "as any" Cast',
|
|
140
|
+
description: 'Casting to any defeats TypeScript\'s type safety.',
|
|
141
|
+
severity: 'medium',
|
|
142
|
+
category: 'deceptive',
|
|
143
|
+
suggestion: 'Use proper type definitions or unknown with type guards.'
|
|
144
|
+
},
|
|
145
|
+
// Fake success responses
|
|
146
|
+
{
|
|
147
|
+
id: 'CS-DEC070',
|
|
148
|
+
pattern: /return\s*\{\s*(?:success|ok|status)\s*:\s*true[^}]*\}\s*;?\s*\/\/.*?(?:todo|fixme|hack)/gi,
|
|
149
|
+
title: 'Fake Success Response',
|
|
150
|
+
description: 'Returning success without actually doing the work.',
|
|
151
|
+
severity: 'critical',
|
|
152
|
+
category: 'deceptive',
|
|
153
|
+
suggestion: 'Implement the actual functionality or return an honest error.'
|
|
154
|
+
},
|
|
155
|
+
// Console.error without throwing
|
|
156
|
+
{
|
|
157
|
+
id: 'CS-DEC080',
|
|
158
|
+
pattern: /console\.error\s*\([^)]+\)\s*;?\s*(?!\s*throw)/g,
|
|
159
|
+
title: 'console.error Without Throw',
|
|
160
|
+
description: 'Logging an error but continuing execution - the error may not be handled.',
|
|
161
|
+
severity: 'low',
|
|
162
|
+
category: 'deceptive',
|
|
163
|
+
suggestion: 'Consider if execution should continue. If not, throw after logging.'
|
|
164
|
+
}
|
|
165
|
+
];
|
|
166
|
+
export function analyzeDeceptivePatterns(code, filename) {
|
|
167
|
+
const issues = [];
|
|
168
|
+
const lines = code.split('\n');
|
|
169
|
+
for (const patternDef of deceptivePatterns) {
|
|
170
|
+
patternDef.pattern.lastIndex = 0;
|
|
171
|
+
let match;
|
|
172
|
+
while ((match = patternDef.pattern.exec(code)) !== null) {
|
|
173
|
+
const beforeMatch = code.substring(0, match.index);
|
|
174
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
175
|
+
const lineContent = lines[lineNumber - 1] || '';
|
|
176
|
+
// Build verification with actual values substituted
|
|
177
|
+
let verification = patternDef.verification;
|
|
178
|
+
if (verification) {
|
|
179
|
+
verification = {
|
|
180
|
+
...verification,
|
|
181
|
+
commands: verification.commands?.map(cmd => cmd.replace(/<filename>/g, filename))
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
issues.push({
|
|
185
|
+
id: patternDef.id,
|
|
186
|
+
category: patternDef.category,
|
|
187
|
+
severity: patternDef.severity,
|
|
188
|
+
title: patternDef.title,
|
|
189
|
+
description: patternDef.description,
|
|
190
|
+
line: lineNumber,
|
|
191
|
+
code: lineContent.trim(),
|
|
192
|
+
suggestion: patternDef.suggestion,
|
|
193
|
+
verification
|
|
194
|
+
});
|
|
195
|
+
if (!patternDef.pattern.global)
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return issues;
|
|
200
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
const errorPatterns = [
|
|
2
|
+
// Unhandled promises
|
|
3
|
+
{
|
|
4
|
+
id: 'CS-ERR001',
|
|
5
|
+
pattern: /(?:^|\s)(?:await\s+)?(?:\w+\.)+\w+\s*\([^)]*\)\s*;?\s*$/gm,
|
|
6
|
+
title: 'Potentially Unhandled Promise',
|
|
7
|
+
description: 'Async call without await or .then()/.catch() may silently fail.',
|
|
8
|
+
severity: 'medium',
|
|
9
|
+
category: 'error',
|
|
10
|
+
suggestion: 'Use await with try/catch or add .catch() handler.'
|
|
11
|
+
},
|
|
12
|
+
// Missing error handling
|
|
13
|
+
{
|
|
14
|
+
id: 'CS-ERR010',
|
|
15
|
+
pattern: /async\s+(?:function\s+\w+|\w+\s*=\s*async)\s*\([^)]*\)\s*(?::\s*\w+\s*)?\{(?:(?!try\s*\{).)*\}/gs,
|
|
16
|
+
title: 'Async Function Without Try/Catch',
|
|
17
|
+
description: 'Async function has no error handling.',
|
|
18
|
+
severity: 'medium',
|
|
19
|
+
category: 'error',
|
|
20
|
+
suggestion: 'Wrap async operations in try/catch or handle at call site.'
|
|
21
|
+
},
|
|
22
|
+
// Variable shadowing and redeclaration
|
|
23
|
+
{
|
|
24
|
+
id: 'CS-ERR020',
|
|
25
|
+
pattern: /var\s+(\w+)[\s\S]*?var\s+\1\s*=/g,
|
|
26
|
+
title: 'Variable Redeclaration with var',
|
|
27
|
+
description: 'Same variable declared twice with var - potential bug.',
|
|
28
|
+
severity: 'medium',
|
|
29
|
+
category: 'error',
|
|
30
|
+
suggestion: 'Use let/const and avoid redeclaring variables.'
|
|
31
|
+
},
|
|
32
|
+
// Comparison issues
|
|
33
|
+
{
|
|
34
|
+
id: 'CS-ERR030',
|
|
35
|
+
pattern: /[^!=]==[^=]/g,
|
|
36
|
+
title: 'Loose Equality (==)',
|
|
37
|
+
description: 'Loose equality can cause unexpected type coercion.',
|
|
38
|
+
severity: 'low',
|
|
39
|
+
category: 'error',
|
|
40
|
+
suggestion: 'Use strict equality (===) instead.'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'CS-ERR031',
|
|
44
|
+
pattern: /!=[^=]/g,
|
|
45
|
+
title: 'Loose Inequality (!=)',
|
|
46
|
+
description: 'Loose inequality can cause unexpected type coercion.',
|
|
47
|
+
severity: 'low',
|
|
48
|
+
category: 'error',
|
|
49
|
+
suggestion: 'Use strict inequality (!==) instead.'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'CS-ERR032',
|
|
53
|
+
pattern: /if\s*\(\s*\w+\s*=\s*[^=]/g,
|
|
54
|
+
title: 'Assignment in Condition',
|
|
55
|
+
description: 'Assignment inside if condition - likely meant to use ==.',
|
|
56
|
+
severity: 'high',
|
|
57
|
+
category: 'error',
|
|
58
|
+
suggestion: 'Use === for comparison. If assignment is intentional, wrap in extra parens.'
|
|
59
|
+
},
|
|
60
|
+
// Null/undefined issues
|
|
61
|
+
{
|
|
62
|
+
id: 'CS-ERR040',
|
|
63
|
+
pattern: /(\w+)\.(\w+)\s*(?:\(|\.)/g,
|
|
64
|
+
title: 'Potential Null Reference',
|
|
65
|
+
description: 'Accessing property without null check - may throw.',
|
|
66
|
+
severity: 'low',
|
|
67
|
+
category: 'error',
|
|
68
|
+
suggestion: 'Use optional chaining (?.) or add null checks.'
|
|
69
|
+
},
|
|
70
|
+
// Loop issues
|
|
71
|
+
{
|
|
72
|
+
id: 'CS-ERR050',
|
|
73
|
+
pattern: /for\s*\(\s*(?:var|let)\s+\w+\s+in\s+/g,
|
|
74
|
+
title: 'for...in on Array',
|
|
75
|
+
description: 'for...in iterates over keys, not values - often wrong for arrays.',
|
|
76
|
+
severity: 'medium',
|
|
77
|
+
category: 'error',
|
|
78
|
+
suggestion: 'Use for...of, forEach(), or traditional for loop for arrays.'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'CS-ERR051',
|
|
82
|
+
pattern: /while\s*\(\s*true\s*\)/g,
|
|
83
|
+
title: 'Infinite Loop Pattern',
|
|
84
|
+
description: 'while(true) requires explicit break - easy to create infinite loop.',
|
|
85
|
+
severity: 'medium',
|
|
86
|
+
category: 'error',
|
|
87
|
+
suggestion: 'Ensure there is always a reachable break condition.'
|
|
88
|
+
},
|
|
89
|
+
// Floating point comparison
|
|
90
|
+
{
|
|
91
|
+
id: 'CS-ERR060',
|
|
92
|
+
pattern: /(?:\d+\.\d+|\w+)\s*===?\s*(?:\d+\.\d+|\w+).*?(?:price|amount|total|sum|money|currency|rate)/gi,
|
|
93
|
+
title: 'Floating Point Comparison',
|
|
94
|
+
description: 'Direct comparison of floats (especially money) can fail due to precision.',
|
|
95
|
+
severity: 'high',
|
|
96
|
+
category: 'error',
|
|
97
|
+
suggestion: 'Use epsilon comparison or integer cents for money.'
|
|
98
|
+
},
|
|
99
|
+
// Array mutation issues
|
|
100
|
+
{
|
|
101
|
+
id: 'CS-ERR070',
|
|
102
|
+
pattern: /\.forEach\s*\([^)]*\)\s*\{[^}]*(?:\.push|\.pop|\.shift|\.splice)/g,
|
|
103
|
+
title: 'Array Mutation During Iteration',
|
|
104
|
+
description: 'Modifying array while iterating can cause skipped elements.',
|
|
105
|
+
severity: 'high',
|
|
106
|
+
category: 'error',
|
|
107
|
+
suggestion: 'Create a new array or iterate backwards when mutating.'
|
|
108
|
+
},
|
|
109
|
+
// parseInt without radix
|
|
110
|
+
{
|
|
111
|
+
id: 'CS-ERR080',
|
|
112
|
+
pattern: /parseInt\s*\(\s*[^,)]+\s*\)(?!\s*,)/g,
|
|
113
|
+
title: 'parseInt Without Radix',
|
|
114
|
+
description: 'parseInt without radix can give unexpected results.',
|
|
115
|
+
severity: 'low',
|
|
116
|
+
category: 'error',
|
|
117
|
+
suggestion: 'Always specify radix: parseInt(x, 10).'
|
|
118
|
+
},
|
|
119
|
+
// Unreachable code
|
|
120
|
+
{
|
|
121
|
+
id: 'CS-ERR090',
|
|
122
|
+
pattern: /return\s+[^;]+;\s*\n\s*(?![\s}]|case\s|default:)/g,
|
|
123
|
+
title: 'Potentially Unreachable Code',
|
|
124
|
+
description: 'Code after return statement will never execute.',
|
|
125
|
+
severity: 'medium',
|
|
126
|
+
category: 'error',
|
|
127
|
+
suggestion: 'Remove unreachable code or fix control flow.'
|
|
128
|
+
},
|
|
129
|
+
// Dangerous delete
|
|
130
|
+
{
|
|
131
|
+
id: 'CS-ERR100',
|
|
132
|
+
pattern: /delete\s+\w+\[\w+\]/g,
|
|
133
|
+
title: 'delete Operator on Array',
|
|
134
|
+
description: 'delete leaves holes in arrays - length unchanged.',
|
|
135
|
+
severity: 'medium',
|
|
136
|
+
category: 'error',
|
|
137
|
+
suggestion: 'Use splice() to remove array elements.'
|
|
138
|
+
},
|
|
139
|
+
// This binding issues
|
|
140
|
+
{
|
|
141
|
+
id: 'CS-ERR110',
|
|
142
|
+
pattern: /setTimeout\s*\(\s*(?:this\.\w+|function\s*\([^)]*\)\s*\{[^}]*this\.)/g,
|
|
143
|
+
title: 'Potential "this" Binding Issue',
|
|
144
|
+
description: 'Using "this" in setTimeout callback may not refer to expected context.',
|
|
145
|
+
severity: 'medium',
|
|
146
|
+
category: 'error',
|
|
147
|
+
suggestion: 'Use arrow function or .bind(this).'
|
|
148
|
+
},
|
|
149
|
+
// Constructor without new
|
|
150
|
+
{
|
|
151
|
+
id: 'CS-ERR120',
|
|
152
|
+
pattern: /(?:^|[^.])\b(?:Date|Array|Object|Map|Set|Promise)\s*\(\s*\)/g,
|
|
153
|
+
title: 'Constructor Without "new"',
|
|
154
|
+
description: 'Calling constructor without new may not work as expected.',
|
|
155
|
+
severity: 'low',
|
|
156
|
+
category: 'error',
|
|
157
|
+
suggestion: 'Use "new" keyword with constructors.'
|
|
158
|
+
},
|
|
159
|
+
// Magic numbers
|
|
160
|
+
{
|
|
161
|
+
id: 'CS-ERR130',
|
|
162
|
+
pattern: /(?:if|while|for|===?|!==?|[<>]=?)\s*\(?\s*(?:\d{3,}|\d+\.\d+)\s*(?!\s*(?:px|em|rem|%|vh|vw|s|ms))/g,
|
|
163
|
+
title: 'Magic Number',
|
|
164
|
+
description: 'Hardcoded number without context makes code hard to maintain.',
|
|
165
|
+
severity: 'low',
|
|
166
|
+
category: 'error',
|
|
167
|
+
suggestion: 'Extract magic numbers into named constants.'
|
|
168
|
+
},
|
|
169
|
+
// Async in constructor
|
|
170
|
+
{
|
|
171
|
+
id: 'CS-ERR140',
|
|
172
|
+
pattern: /constructor\s*\([^)]*\)\s*\{[^}]*await\s+/g,
|
|
173
|
+
title: 'Await in Constructor',
|
|
174
|
+
description: 'Constructors cannot be async - await will not work as expected.',
|
|
175
|
+
severity: 'high',
|
|
176
|
+
category: 'error',
|
|
177
|
+
suggestion: 'Use a static factory method for async initialization.'
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
export function analyzeErrors(code, filename) {
|
|
181
|
+
const issues = [];
|
|
182
|
+
const lines = code.split('\n');
|
|
183
|
+
for (const patternDef of errorPatterns) {
|
|
184
|
+
patternDef.pattern.lastIndex = 0;
|
|
185
|
+
let match;
|
|
186
|
+
while ((match = patternDef.pattern.exec(code)) !== null) {
|
|
187
|
+
const beforeMatch = code.substring(0, match.index);
|
|
188
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
189
|
+
const lineContent = lines[lineNumber - 1] || '';
|
|
190
|
+
// Build verification with actual values substituted
|
|
191
|
+
let verification = patternDef.verification;
|
|
192
|
+
if (verification) {
|
|
193
|
+
verification = {
|
|
194
|
+
...verification,
|
|
195
|
+
commands: verification.commands?.map(cmd => cmd.replace(/<filename>/g, filename))
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
issues.push({
|
|
199
|
+
id: patternDef.id,
|
|
200
|
+
category: patternDef.category,
|
|
201
|
+
severity: patternDef.severity,
|
|
202
|
+
title: patternDef.title,
|
|
203
|
+
description: patternDef.description,
|
|
204
|
+
line: lineNumber,
|
|
205
|
+
code: lineContent.trim(),
|
|
206
|
+
suggestion: patternDef.suggestion,
|
|
207
|
+
verification
|
|
208
|
+
});
|
|
209
|
+
if (!patternDef.pattern.global)
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return issues;
|
|
214
|
+
}
|