code-sentinel-mcp 0.2.3 → 0.2.5
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 +206 -21
- package/build/analyzers/core.d.ts +7 -0
- package/build/analyzers/core.js +69 -0
- package/build/analyzers/deceptive.js +5 -198
- package/build/analyzers/errors.js +5 -212
- package/build/analyzers/placeholders.js +5 -214
- package/build/analyzers/security.js +5 -236
- package/build/patterns/builders.d.ts +20 -0
- package/build/patterns/builders.js +335 -0
- package/build/patterns/compiler.d.ts +16 -0
- package/build/patterns/compiler.js +108 -0
- package/build/patterns/definitions/deceptive.d.ts +2 -0
- package/build/patterns/definitions/deceptive.js +226 -0
- package/build/patterns/definitions/errors.d.ts +2 -0
- package/build/patterns/definitions/errors.js +206 -0
- package/build/patterns/definitions/index.d.ts +4 -0
- package/build/patterns/definitions/index.js +5 -0
- package/build/patterns/definitions/placeholders.d.ts +2 -0
- package/build/patterns/definitions/placeholders.js +260 -0
- package/build/patterns/definitions/security.d.ts +2 -0
- package/build/patterns/definitions/security.js +268 -0
- package/build/patterns/index.d.ts +4 -0
- package/build/patterns/index.js +7 -0
- package/build/patterns/types.d.ts +117 -0
- package/build/patterns/types.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,75 @@ AI coding assistants can inadvertently introduce subtle issues: hardcoded secret
|
|
|
12
12
|
- **Balanced analysis**: Detects both issues AND strengths for fair code assessment
|
|
13
13
|
- **Multi-language support**: Works with TypeScript, JavaScript, Python, Go, Rust, Java, and more
|
|
14
14
|
|
|
15
|
+
## Why Not Tree-sitter or AST-Based Tools?
|
|
16
|
+
|
|
17
|
+
CodeSentinel intentionally uses a **pattern-based approach** rather than AST parsing. Here's why:
|
|
18
|
+
|
|
19
|
+
### The Problem We Solve Is Different
|
|
20
|
+
|
|
21
|
+
Traditional linters (ESLint, tree-sitter) detect **syntax errors** and **style violations**. CodeSentinel detects **semantically deceptive patterns** - code that is:
|
|
22
|
+
|
|
23
|
+
- Syntactically valid (passes all linters)
|
|
24
|
+
- Structurally correct (valid AST)
|
|
25
|
+
- **But hides serious issues** that AI agents commonly produce
|
|
26
|
+
|
|
27
|
+
### Examples AST Tools Miss
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// AST sees: valid try-catch block
|
|
31
|
+
// CodeSentinel sees: error swallowing that masks failures
|
|
32
|
+
try { riskyOperation(); } catch(e) { }
|
|
33
|
+
|
|
34
|
+
// AST sees: valid function returning boolean
|
|
35
|
+
// CodeSentinel sees: fake implementation that always succeeds
|
|
36
|
+
function validateUser() { return true; } // TODO: implement
|
|
37
|
+
|
|
38
|
+
// AST sees: valid fallback expression
|
|
39
|
+
// CodeSentinel sees: failure masking - "no data" vs "fetch failed" indistinguishable
|
|
40
|
+
const users = response.data || [];
|
|
41
|
+
|
|
42
|
+
// AST sees: valid return statement
|
|
43
|
+
// CodeSentinel sees: silent failure hiding
|
|
44
|
+
if (error) { return null; } // error case
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### What Each Approach Detects
|
|
48
|
+
|
|
49
|
+
| Issue Type | AST/Tree-sitter | CodeSentinel |
|
|
50
|
+
|:-----------|:----------------|:-------------|
|
|
51
|
+
| Syntax errors | Yes | No (not our goal) |
|
|
52
|
+
| Missing semicolons | Yes | No |
|
|
53
|
+
| Unused variables | Yes | No |
|
|
54
|
+
| **Empty catch blocks** | Partially | Yes |
|
|
55
|
+
| **Silent error returns** | No | Yes |
|
|
56
|
+
| **Fake success responses** | No | Yes |
|
|
57
|
+
| **TODO/placeholder code** | No | Yes |
|
|
58
|
+
| **Error-masking fallbacks** | No | Yes |
|
|
59
|
+
| **Hardcoded secrets** | Limited | Yes |
|
|
60
|
+
| **Deceptive comments** | No | Yes |
|
|
61
|
+
|
|
62
|
+
### The Real Issue: Agent Behavior
|
|
63
|
+
|
|
64
|
+
AI coding agents produce code that **looks correct** but contains subtle deceptions:
|
|
65
|
+
|
|
66
|
+
1. **"Making the error go away"** - Empty catches, silent returns, swallowed exceptions
|
|
67
|
+
2. **Placeholder implementations** - `return true`, `return []`, TODO comments
|
|
68
|
+
3. **False confidence patterns** - `|| []` fallbacks that mask fetch failures
|
|
69
|
+
4. **Suppression abuse** - `@ts-ignore`, `eslint-disable` to hide type errors
|
|
70
|
+
|
|
71
|
+
These patterns pass every linter and compile successfully. AST tools see valid structure. Only pattern-based detection catches the **semantic intent** behind the code.
|
|
72
|
+
|
|
73
|
+
### When to Use What
|
|
74
|
+
|
|
75
|
+
| Tool | Use For |
|
|
76
|
+
|:-----|:--------|
|
|
77
|
+
| ESLint/TSLint | Style consistency, syntax rules, unused code |
|
|
78
|
+
| Tree-sitter | Syntax highlighting, code navigation, refactoring |
|
|
79
|
+
| TypeScript | Type safety, compile-time errors |
|
|
80
|
+
| **CodeSentinel** | Agent-generated deceptions, error hiding, incomplete implementations |
|
|
81
|
+
|
|
82
|
+
CodeSentinel complements these tools - it catches what they structurally cannot.
|
|
83
|
+
|
|
15
84
|
## Features
|
|
16
85
|
|
|
17
86
|
- **Security Analysis** (16 patterns): Hardcoded secrets, SQL injection, XSS, command injection, insecure crypto, disabled SSL, and more
|
|
@@ -32,7 +101,7 @@ npm install -g code-sentinel-mcp
|
|
|
32
101
|
### From source
|
|
33
102
|
|
|
34
103
|
```bash
|
|
35
|
-
git clone https://github.com/
|
|
104
|
+
git clone https://github.com/salrad22/code-sentinel.git
|
|
36
105
|
cd code-sentinel
|
|
37
106
|
npm install
|
|
38
107
|
npm run build
|
|
@@ -67,6 +136,47 @@ Add to your Claude Code MCP configuration file (`~/.claude/claude_desktop_config
|
|
|
67
136
|
}
|
|
68
137
|
```
|
|
69
138
|
|
|
139
|
+
## Remote Server (Cloudflare Workers)
|
|
140
|
+
|
|
141
|
+
CodeSentinel is also available as a remote MCP server on Cloudflare Workers. **No local installation required!**
|
|
142
|
+
|
|
143
|
+
### Quick connect (Claude Code)
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
claude mcp add-remote code-sentinel https://code-sentinel-mcp.sharara.dev/sse
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Or use the Streamable HTTP endpoint (recommended for newer clients):
|
|
150
|
+
```bash
|
|
151
|
+
claude mcp add --transport http code-sentinel https://code-sentinel-mcp.sharara.dev/mcp
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Endpoints
|
|
155
|
+
|
|
156
|
+
| Endpoint | Protocol | Description |
|
|
157
|
+
|:---------|:---------|:------------|
|
|
158
|
+
| `https://code-sentinel-mcp.sharara.dev/mcp` | Streamable HTTP | Recommended |
|
|
159
|
+
| `https://code-sentinel-mcp.sharara.dev/sse` | Server-Sent Events | Legacy support |
|
|
160
|
+
| `https://code-sentinel-mcp.sharara.dev/` | HTTP GET | Health check / server info |
|
|
161
|
+
|
|
162
|
+
### Self-hosting on Cloudflare
|
|
163
|
+
|
|
164
|
+
Deploy your own instance:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
cd cloudflare
|
|
168
|
+
npm install
|
|
169
|
+
npm run dev # Local development at localhost:8787
|
|
170
|
+
npm run deploy # Deploy to your Cloudflare account
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Requirements:**
|
|
174
|
+
- Cloudflare account (free tier works)
|
|
175
|
+
- Wrangler CLI (`npm install -g wrangler`)
|
|
176
|
+
- `wrangler login` to authenticate
|
|
177
|
+
|
|
178
|
+
The server uses Durable Objects for persistent MCP connections. No database required.
|
|
179
|
+
|
|
70
180
|
## Available Tools
|
|
71
181
|
|
|
72
182
|
### `analyze_code`
|
|
@@ -245,32 +355,105 @@ CodeSentinel detects language from file extensions:
|
|
|
245
355
|
|
|
246
356
|
## Extending CodeSentinel
|
|
247
357
|
|
|
248
|
-
|
|
358
|
+
CodeSentinel uses a **data-driven pattern system** that separates pattern definitions from regex generation. This makes adding new patterns easier and more maintainable.
|
|
359
|
+
|
|
360
|
+
### Project Structure
|
|
249
361
|
|
|
250
362
|
```
|
|
251
|
-
src/
|
|
252
|
-
├──
|
|
253
|
-
├──
|
|
254
|
-
├──
|
|
255
|
-
├──
|
|
256
|
-
└──
|
|
363
|
+
src/
|
|
364
|
+
├── patterns/
|
|
365
|
+
│ ├── types.ts # Type definitions for pattern configs
|
|
366
|
+
│ ├── builders.ts # Functions that generate regex from configs
|
|
367
|
+
│ ├── compiler.ts # Compiles definitions to executable patterns
|
|
368
|
+
│ └── definitions/
|
|
369
|
+
│ ├── security.ts # Security vulnerability patterns
|
|
370
|
+
│ ├── deceptive.ts # Error-hiding patterns
|
|
371
|
+
│ ├── placeholders.ts # Incomplete code patterns
|
|
372
|
+
│ ├── errors.ts # Code smell patterns
|
|
373
|
+
│ └── index.ts # Exports all definitions
|
|
374
|
+
├── analyzers/
|
|
375
|
+
│ ├── core.ts # Unified analyzer using compiled patterns
|
|
376
|
+
│ ├── security.ts # Security analyzer (delegates to core)
|
|
377
|
+
│ ├── deceptive.ts # Deceptive analyzer (delegates to core)
|
|
378
|
+
│ ├── placeholders.ts # Placeholder analyzer (delegates to core)
|
|
379
|
+
│ ├── errors.ts # Error analyzer (delegates to core)
|
|
380
|
+
│ └── strengths.ts # Strength analyzer
|
|
381
|
+
└── index.ts # MCP server entry point
|
|
257
382
|
```
|
|
258
383
|
|
|
259
|
-
|
|
384
|
+
### Adding a New Pattern
|
|
385
|
+
|
|
386
|
+
Instead of writing regex manually, you define **what** to detect and the system generates the regex:
|
|
260
387
|
|
|
261
388
|
```typescript
|
|
389
|
+
// Old approach (manual regex)
|
|
390
|
+
{
|
|
391
|
+
id: 'CS-DEC001',
|
|
392
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*\}/g, // Error-prone
|
|
393
|
+
title: 'Empty Catch Block',
|
|
394
|
+
// ...
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// New approach (data-driven)
|
|
262
398
|
{
|
|
263
|
-
id: 'CS-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
399
|
+
id: 'CS-DEC001',
|
|
400
|
+
title: 'Empty Catch Block',
|
|
401
|
+
description: 'Silently swallowing errors makes debugging impossible.',
|
|
402
|
+
severity: 'high',
|
|
403
|
+
category: 'deceptive',
|
|
404
|
+
suggestion: 'At minimum, log the error. Better: handle it appropriately.',
|
|
405
|
+
match: {
|
|
406
|
+
type: 'catch_handler',
|
|
407
|
+
behavior: 'empty'
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Available Match Types
|
|
413
|
+
|
|
414
|
+
| Match Type | Description | Example Config |
|
|
415
|
+
|:-----------|:------------|:---------------|
|
|
416
|
+
| `empty_block` | Empty catch/finally/promise blocks | `{ type: 'empty_block', constructs: ['catch', '.catch'] }` |
|
|
417
|
+
| `function_call` | Function/method calls | `{ type: 'function_call', names: ['eval', 'exec'] }` |
|
|
418
|
+
| `returns_only` | Return statements with specific values | `{ type: 'returns_only', values: ['null', '[]', '{}'] }` |
|
|
419
|
+
| `contains_text` | Text in comments/strings | `{ type: 'contains_text', terms: ['TODO', 'FIXME'], context: 'comment' }` |
|
|
420
|
+
| `fallback_value` | Fallback patterns | `{ type: 'fallback_value', operators: ['\|\|'], values: ['[]'] }` |
|
|
421
|
+
| `catch_handler` | Catch block behaviors | `{ type: 'catch_handler', behavior: 'empty' }` |
|
|
422
|
+
| `promise_catch` | Promise .catch() behaviors | `{ type: 'promise_catch', behavior: 'returns_silent' }` |
|
|
423
|
+
| `comment_marker` | TODO/FIXME/HACK markers | `{ type: 'comment_marker', markers: ['TODO', 'FIXME'] }` |
|
|
424
|
+
| `string_literal` | Patterns inside strings | `{ type: 'string_literal', patterns: ['password', 'secret'] }` |
|
|
425
|
+
| `secret_pattern` | API keys and tokens | `{ type: 'secret_pattern', kind: 'github' }` |
|
|
426
|
+
| `url_pattern` | URL patterns | `{ type: 'url_pattern', protocol: 'http', excludeLocalhost: true }` |
|
|
427
|
+
| `suppression_comment` | Linter suppressions | `{ type: 'suppression_comment', tools: ['ts-ignore', 'eslint-disable'] }` |
|
|
428
|
+
| `type_cast` | Type casts | `{ type: 'type_cast', targets: ['any'] }` |
|
|
429
|
+
| `comparison` | Comparison operators | `{ type: 'comparison', operators: ['==', '!='] }` |
|
|
430
|
+
| `loop_pattern` | Loop patterns | `{ type: 'loop_pattern', kind: 'while_true' }` |
|
|
431
|
+
| `raw_regex` | Escape hatch for complex patterns | `{ type: 'raw_regex', pattern: 'your-regex', flags: 'gi' }` |
|
|
432
|
+
|
|
433
|
+
### Step-by-Step: Adding a Pattern
|
|
434
|
+
|
|
435
|
+
1. **Choose the category** - security, deceptive, placeholder, or error
|
|
436
|
+
2. **Open the definition file** - `src/patterns/definitions/<category>.ts`
|
|
437
|
+
3. **Add a new pattern definition** using the appropriate match type
|
|
438
|
+
4. **Build** - `npm run build`
|
|
439
|
+
5. **Test** - Use the MCP inspector to verify detection
|
|
440
|
+
|
|
441
|
+
### Pattern Definition Structure
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
{
|
|
445
|
+
id: string; // Unique ID: CS-<CAT><NUM> (e.g., CS-SEC001)
|
|
446
|
+
title: string; // Short description (displayed in results)
|
|
447
|
+
description: string; // Detailed explanation of the issue
|
|
448
|
+
severity: Severity; // 'critical' | 'high' | 'medium' | 'low' | 'info'
|
|
449
|
+
category: Category; // 'security' | 'deceptive' | 'placeholder' | 'error'
|
|
450
|
+
suggestion?: string; // How to fix the issue
|
|
451
|
+
match: MatchConfig; // What to detect (see match types above)
|
|
452
|
+
verification?: { // Optional: reduce false positives
|
|
453
|
+
status: 'needs_verification' | 'confirmed';
|
|
454
|
+
assumption?: string;
|
|
455
|
+
confirmIf?: string;
|
|
456
|
+
falsePositiveIf?: string;
|
|
274
457
|
}
|
|
275
458
|
}
|
|
276
459
|
```
|
|
@@ -306,6 +489,8 @@ MIT
|
|
|
306
489
|
|
|
307
490
|
## Links
|
|
308
491
|
|
|
492
|
+
- [GitHub Repository](https://github.com/salrad22/code-sentinel)
|
|
493
|
+
- [npm package](https://www.npmjs.com/package/code-sentinel-mcp)
|
|
494
|
+
- [Remote Server](https://code-sentinel-mcp.sharara.dev/)
|
|
309
495
|
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
310
496
|
- [Claude Code](https://claude.ai/code)
|
|
311
|
-
- [npm package](https://www.npmjs.com/package/code-sentinel-mcp)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Issue, Category } from '../types.js';
|
|
2
|
+
export declare function analyzeWithPatterns(code: string, filename: string, category: Category): Issue[];
|
|
3
|
+
export declare function analyzeSecurityWithPatterns(code: string, filename: string): Issue[];
|
|
4
|
+
export declare function analyzeDeceptiveWithPatterns(code: string, filename: string): Issue[];
|
|
5
|
+
export declare function analyzePlaceholdersWithPatterns(code: string, filename: string): Issue[];
|
|
6
|
+
export declare function analyzeErrorsWithPatterns(code: string, filename: string): Issue[];
|
|
7
|
+
export declare function analyzeAllWithPatterns(code: string, filename: string): Issue[];
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Core analyzer - uses compiled patterns from the data-driven system
|
|
2
|
+
import { getCompiledPatterns } from '../patterns/index.js';
|
|
3
|
+
// Analyze code using compiled patterns
|
|
4
|
+
export function analyzeWithPatterns(code, filename, category) {
|
|
5
|
+
const issues = [];
|
|
6
|
+
const lines = code.split('\n');
|
|
7
|
+
const compiledPatterns = getCompiledPatterns(category);
|
|
8
|
+
for (const pattern of compiledPatterns) {
|
|
9
|
+
for (const regex of pattern.patterns) {
|
|
10
|
+
// Reset regex state for global patterns
|
|
11
|
+
regex.lastIndex = 0;
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = regex.exec(code)) !== null) {
|
|
14
|
+
// Calculate line number
|
|
15
|
+
const beforeMatch = code.substring(0, match.index);
|
|
16
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
17
|
+
const lineContent = lines[lineNumber - 1] || '';
|
|
18
|
+
const matchedValue = match[0];
|
|
19
|
+
// Build verification with actual values substituted
|
|
20
|
+
let verification = pattern.verification;
|
|
21
|
+
if (verification) {
|
|
22
|
+
verification = {
|
|
23
|
+
...verification,
|
|
24
|
+
commands: verification.commands?.map(cmd => cmd
|
|
25
|
+
.replace(/<matched_value>/g, matchedValue.substring(0, 20) + '...')
|
|
26
|
+
.replace(/<filename>/g, filename))
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
issues.push({
|
|
30
|
+
id: pattern.id,
|
|
31
|
+
category: pattern.category,
|
|
32
|
+
severity: pattern.severity,
|
|
33
|
+
title: pattern.title,
|
|
34
|
+
description: pattern.description,
|
|
35
|
+
line: lineNumber,
|
|
36
|
+
code: lineContent.trim(),
|
|
37
|
+
suggestion: pattern.suggestion,
|
|
38
|
+
verification
|
|
39
|
+
});
|
|
40
|
+
// Prevent infinite loops for patterns without global flag
|
|
41
|
+
if (!regex.global)
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return issues;
|
|
47
|
+
}
|
|
48
|
+
// Convenience functions for each category
|
|
49
|
+
export function analyzeSecurityWithPatterns(code, filename) {
|
|
50
|
+
return analyzeWithPatterns(code, filename, 'security');
|
|
51
|
+
}
|
|
52
|
+
export function analyzeDeceptiveWithPatterns(code, filename) {
|
|
53
|
+
return analyzeWithPatterns(code, filename, 'deceptive');
|
|
54
|
+
}
|
|
55
|
+
export function analyzePlaceholdersWithPatterns(code, filename) {
|
|
56
|
+
return analyzeWithPatterns(code, filename, 'placeholder');
|
|
57
|
+
}
|
|
58
|
+
export function analyzeErrorsWithPatterns(code, filename) {
|
|
59
|
+
return analyzeWithPatterns(code, filename, 'error');
|
|
60
|
+
}
|
|
61
|
+
// Analyze all categories at once
|
|
62
|
+
export function analyzeAllWithPatterns(code, filename) {
|
|
63
|
+
return [
|
|
64
|
+
...analyzeSecurityWithPatterns(code, filename),
|
|
65
|
+
...analyzeDeceptiveWithPatterns(code, filename),
|
|
66
|
+
...analyzePlaceholdersWithPatterns(code, filename),
|
|
67
|
+
...analyzeErrorsWithPatterns(code, filename),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
@@ -1,200 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
];
|
|
1
|
+
// Deceptive pattern analyzer - uses data-driven pattern system
|
|
2
|
+
// This file now delegates to the core analyzer with compiled patterns
|
|
3
|
+
import { analyzeDeceptiveWithPatterns } from './core.js';
|
|
4
|
+
// Main export - uses the data-driven pattern system
|
|
166
5
|
export function analyzeDeceptivePatterns(code, filename) {
|
|
167
|
-
|
|
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;
|
|
6
|
+
return analyzeDeceptiveWithPatterns(code, filename);
|
|
200
7
|
}
|