awesome-slash 2.4.2
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/.claude-plugin/marketplace.json +54 -0
- package/.claude-plugin/plugin.json +11 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +261 -0
- package/LICENSE +21 -0
- package/README.md +363 -0
- package/SECURITY.md +101 -0
- package/adapters/README.md +256 -0
- package/adapters/codex/README.md +272 -0
- package/adapters/codex/install.sh +179 -0
- package/adapters/opencode/README.md +301 -0
- package/adapters/opencode/install.sh +223 -0
- package/lib/patterns/review-patterns.js +511 -0
- package/lib/patterns/slop-patterns.js +647 -0
- package/lib/platform/detect-platform.js +535 -0
- package/lib/platform/verify-tools.js +235 -0
- package/lib/state/workflow-state.js +635 -0
- package/lib/state/workflow-state.schema.json +282 -0
- package/lib/utils/context-optimizer.js +227 -0
- package/mcp-server/index.js +303 -0
- package/mcp-server/package.json +23 -0
- package/package.json +63 -0
- package/plugins/deslop-around/.claude-plugin/plugin.json +20 -0
- package/plugins/deslop-around/commands/deslop-around.md +220 -0
- package/plugins/deslop-around/lib/patterns/review-patterns.js +511 -0
- package/plugins/deslop-around/lib/patterns/slop-patterns.js +641 -0
- package/plugins/deslop-around/lib/platform/detect-platform.js +514 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +235 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +635 -0
- package/plugins/deslop-around/lib/state/workflow-state.schema.json +282 -0
- package/plugins/deslop-around/lib/utils/context-optimizer.js +222 -0
- package/plugins/next-task/.claude-plugin/plugin.json +24 -0
- package/plugins/next-task/agents/ci-fixer.md +236 -0
- package/plugins/next-task/agents/ci-monitor.md +291 -0
- package/plugins/next-task/agents/delivery-validator.md +451 -0
- package/plugins/next-task/agents/deslop-work.md +272 -0
- package/plugins/next-task/agents/docs-updater.md +506 -0
- package/plugins/next-task/agents/exploration-agent.md +277 -0
- package/plugins/next-task/agents/implementation-agent.md +427 -0
- package/plugins/next-task/agents/planning-agent.md +236 -0
- package/plugins/next-task/agents/policy-selector.md +248 -0
- package/plugins/next-task/agents/review-orchestrator.md +521 -0
- package/plugins/next-task/agents/simple-fixer.md +136 -0
- package/plugins/next-task/agents/task-discoverer.md +357 -0
- package/plugins/next-task/agents/test-coverage-checker.md +447 -0
- package/plugins/next-task/agents/worktree-manager.md +419 -0
- package/plugins/next-task/commands/delivery-approval.md +331 -0
- package/plugins/next-task/commands/next-task.md +627 -0
- package/plugins/next-task/commands/update-docs-around.md +418 -0
- package/plugins/next-task/hooks/hooks.json +14 -0
- package/plugins/next-task/lib/patterns/review-patterns.js +511 -0
- package/plugins/next-task/lib/patterns/slop-patterns.js +641 -0
- package/plugins/next-task/lib/platform/detect-platform.js +514 -0
- package/plugins/next-task/lib/platform/verify-tools.js +235 -0
- package/plugins/next-task/lib/state/tasks-registry.schema.json +85 -0
- package/plugins/next-task/lib/state/workflow-state.js +635 -0
- package/plugins/next-task/lib/state/workflow-state.schema.json +282 -0
- package/plugins/next-task/lib/state/worktree-status.schema.json +219 -0
- package/plugins/next-task/lib/utils/context-optimizer.js +222 -0
- package/plugins/project-review/.claude-plugin/plugin.json +20 -0
- package/plugins/project-review/commands/project-review-agents.md +286 -0
- package/plugins/project-review/commands/project-review-github.md +142 -0
- package/plugins/project-review/commands/project-review.md +273 -0
- package/plugins/project-review/lib/patterns/review-patterns.js +511 -0
- package/plugins/project-review/lib/patterns/slop-patterns.js +641 -0
- package/plugins/project-review/lib/platform/detect-platform.js +514 -0
- package/plugins/project-review/lib/platform/verify-tools.js +235 -0
- package/plugins/project-review/lib/state/workflow-state.js +635 -0
- package/plugins/project-review/lib/state/workflow-state.schema.json +282 -0
- package/plugins/project-review/lib/utils/context-optimizer.js +222 -0
- package/plugins/reality-check/.claude-plugin/plugin.json +23 -0
- package/plugins/reality-check/README.md +156 -0
- package/plugins/reality-check/agents/code-explorer.md +353 -0
- package/plugins/reality-check/agents/doc-analyzer.md +337 -0
- package/plugins/reality-check/agents/issue-scanner.md +231 -0
- package/plugins/reality-check/agents/plan-synthesizer.md +479 -0
- package/plugins/reality-check/commands/scan.md +242 -0
- package/plugins/reality-check/commands/set.md +203 -0
- package/plugins/reality-check/lib/state/reality-check-state.js +509 -0
- package/plugins/reality-check/skills/reality-analysis/SKILL.md +317 -0
- package/plugins/ship/.claude-plugin/plugin.json +21 -0
- package/plugins/ship/commands/ship-ci-review-loop.md +443 -0
- package/plugins/ship/commands/ship-deployment.md +330 -0
- package/plugins/ship/commands/ship-error-handling.md +254 -0
- package/plugins/ship/commands/ship.md +370 -0
- package/plugins/ship/lib/patterns/review-patterns.js +511 -0
- package/plugins/ship/lib/patterns/slop-patterns.js +641 -0
- package/plugins/ship/lib/platform/detect-platform.js +514 -0
- package/plugins/ship/lib/platform/verify-tools.js +235 -0
- package/plugins/ship/lib/state/workflow-state.js +635 -0
- package/plugins/ship/lib/state/workflow-state.schema.json +282 -0
- package/plugins/ship/lib/utils/context-optimizer.js +222 -0
- package/scripts/install/claude.sh +50 -0
- package/scripts/install/codex.sh +181 -0
- package/scripts/install/opencode.sh +211 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slop Detection Patterns
|
|
3
|
+
* Pattern library for detecting and removing AI-generated code slop
|
|
4
|
+
*
|
|
5
|
+
* @author Avi Fenesh
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Deep freeze an object for V8 optimization and immutability
|
|
11
|
+
* @param {Object} obj - Object to freeze
|
|
12
|
+
* @returns {Object} Frozen object
|
|
13
|
+
*/
|
|
14
|
+
function deepFreeze(obj) {
|
|
15
|
+
Object.keys(obj).forEach(key => {
|
|
16
|
+
if (typeof obj[key] === 'object' && obj[key] !== null && !(obj[key] instanceof RegExp)) {
|
|
17
|
+
deepFreeze(obj[key]);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return Object.freeze(obj);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Pre-compiled regex cache for performance
|
|
24
|
+
const _compiledExcludePatterns = new Map();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get a compiled regex for an exclude pattern (cached)
|
|
28
|
+
* @param {string} pattern - Glob pattern to compile
|
|
29
|
+
* @returns {RegExp} Compiled regex
|
|
30
|
+
*/
|
|
31
|
+
function getCompiledPattern(pattern) {
|
|
32
|
+
if (!_compiledExcludePatterns.has(pattern)) {
|
|
33
|
+
// Escape all regex metacharacters except *, then replace * with .*
|
|
34
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
35
|
+
const regexStr = '^' + escaped.replace(/\*/g, '.*') + '$';
|
|
36
|
+
_compiledExcludePatterns.set(pattern, new RegExp(regexStr));
|
|
37
|
+
}
|
|
38
|
+
return _compiledExcludePatterns.get(pattern);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Auto-fix strategies:
|
|
43
|
+
* - remove: Delete the matching line(s)
|
|
44
|
+
* - replace: Replace with suggested fix
|
|
45
|
+
* - add_logging: Add proper error logging
|
|
46
|
+
* - flag: Mark for manual review
|
|
47
|
+
* - none: Report only, no auto-fix
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
const slopPatterns = {
|
|
51
|
+
/**
|
|
52
|
+
* Console debugging in JavaScript/TypeScript
|
|
53
|
+
*/
|
|
54
|
+
console_debugging: {
|
|
55
|
+
pattern: /console\.(log|debug|info|warn)\(/,
|
|
56
|
+
exclude: ['*.test.*', '*.spec.*', '*.config.*'],
|
|
57
|
+
severity: 'medium',
|
|
58
|
+
autoFix: 'remove',
|
|
59
|
+
language: 'javascript',
|
|
60
|
+
description: 'Console.log statements left in production code'
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Python debugging statements
|
|
65
|
+
*/
|
|
66
|
+
python_debugging: {
|
|
67
|
+
pattern: /(print\(|import pdb|breakpoint\(\)|import ipdb)/,
|
|
68
|
+
exclude: ['test_*.py', '*_test.py', 'conftest.py'],
|
|
69
|
+
severity: 'medium',
|
|
70
|
+
autoFix: 'remove',
|
|
71
|
+
language: 'python',
|
|
72
|
+
description: 'Debug print/breakpoint statements in production'
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Rust debugging macros
|
|
77
|
+
*/
|
|
78
|
+
rust_debugging: {
|
|
79
|
+
pattern: /(println!|dbg!|eprintln!)\(/,
|
|
80
|
+
exclude: ['*_test.rs', '*_tests.rs'],
|
|
81
|
+
severity: 'medium',
|
|
82
|
+
autoFix: 'remove',
|
|
83
|
+
language: 'rust',
|
|
84
|
+
description: 'Debug print macros in production code'
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Old TODO comments (>90 days)
|
|
89
|
+
*/
|
|
90
|
+
old_todos: {
|
|
91
|
+
pattern: /(TODO|FIXME|HACK|XXX):/,
|
|
92
|
+
exclude: [],
|
|
93
|
+
severity: 'low',
|
|
94
|
+
autoFix: 'flag',
|
|
95
|
+
language: null, // All languages
|
|
96
|
+
description: 'TODO/FIXME comments older than 90 days',
|
|
97
|
+
requiresAgeCheck: true,
|
|
98
|
+
ageThreshold: 90 // days
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Commented out code blocks
|
|
103
|
+
*/
|
|
104
|
+
commented_code: {
|
|
105
|
+
pattern: /^\s*(\/\/|#)\s*\w{5,}/,
|
|
106
|
+
exclude: [],
|
|
107
|
+
severity: 'medium',
|
|
108
|
+
autoFix: 'remove',
|
|
109
|
+
language: null,
|
|
110
|
+
description: 'Large blocks of commented-out code',
|
|
111
|
+
minConsecutiveLines: 5
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Placeholder text
|
|
116
|
+
*/
|
|
117
|
+
placeholder_text: {
|
|
118
|
+
pattern: /(lorem ipsum|test test test|asdf|foo bar baz|placeholder|replace this|todo: implement)/i,
|
|
119
|
+
exclude: ['*.test.*', '*.spec.*', 'README.*', '*.md'],
|
|
120
|
+
severity: 'high',
|
|
121
|
+
autoFix: 'flag',
|
|
122
|
+
language: null,
|
|
123
|
+
description: 'Placeholder text that should be replaced'
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Empty catch blocks (JavaScript/TypeScript)
|
|
128
|
+
*/
|
|
129
|
+
empty_catch_js: {
|
|
130
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*\}/,
|
|
131
|
+
exclude: [],
|
|
132
|
+
severity: 'high',
|
|
133
|
+
autoFix: 'add_logging',
|
|
134
|
+
language: 'javascript',
|
|
135
|
+
description: 'Empty catch blocks without error handling'
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Empty except blocks (Python)
|
|
140
|
+
*/
|
|
141
|
+
empty_except_py: {
|
|
142
|
+
pattern: /except\s*[^:]*:\s*pass\s*$/,
|
|
143
|
+
exclude: [],
|
|
144
|
+
severity: 'high',
|
|
145
|
+
autoFix: 'add_logging',
|
|
146
|
+
language: 'python',
|
|
147
|
+
description: 'Empty except blocks with just pass'
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Magic numbers (large hardcoded numbers)
|
|
152
|
+
*/
|
|
153
|
+
magic_numbers: {
|
|
154
|
+
pattern: /(?<![a-zA-Z_\d])[0-9]{4,}(?![a-zA-Z_\d])/,
|
|
155
|
+
exclude: ['*.test.*', '*.spec.*', '*.config.*', 'package.json', 'package-lock.json'],
|
|
156
|
+
severity: 'low',
|
|
157
|
+
autoFix: 'flag',
|
|
158
|
+
language: null,
|
|
159
|
+
description: 'Magic numbers that should be constants'
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Disabled linter rules
|
|
164
|
+
*/
|
|
165
|
+
disabled_linter: {
|
|
166
|
+
pattern: /(eslint-disable|pylint: disable|#\s*noqa|@SuppressWarnings|#\[allow\()/,
|
|
167
|
+
exclude: [],
|
|
168
|
+
severity: 'medium',
|
|
169
|
+
autoFix: 'flag',
|
|
170
|
+
language: null,
|
|
171
|
+
description: 'Disabled linter rules that may hide issues'
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Unused imports (basic pattern, language-specific tools better)
|
|
176
|
+
*/
|
|
177
|
+
unused_imports_hint: {
|
|
178
|
+
pattern: /^import .* from .* \/\/ unused$/,
|
|
179
|
+
exclude: [],
|
|
180
|
+
severity: 'low',
|
|
181
|
+
autoFix: 'remove',
|
|
182
|
+
language: null,
|
|
183
|
+
description: 'Imports marked as unused'
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Duplicate string literals (same string >5 times)
|
|
188
|
+
*/
|
|
189
|
+
duplicate_strings: {
|
|
190
|
+
pattern: null, // Requires multi-pass analysis
|
|
191
|
+
exclude: ['*.test.*', '*.spec.*'],
|
|
192
|
+
severity: 'low',
|
|
193
|
+
autoFix: 'flag',
|
|
194
|
+
language: null,
|
|
195
|
+
description: 'Duplicate string literals that should be constants',
|
|
196
|
+
requiresMultiPass: true
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Inconsistent indentation markers
|
|
201
|
+
*/
|
|
202
|
+
mixed_indentation: {
|
|
203
|
+
pattern: /^\t+ +|^ +\t+/,
|
|
204
|
+
exclude: ['Makefile', '*.mk'],
|
|
205
|
+
severity: 'low',
|
|
206
|
+
autoFix: 'replace',
|
|
207
|
+
language: null,
|
|
208
|
+
description: 'Mixed tabs and spaces'
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Trailing whitespace
|
|
213
|
+
*/
|
|
214
|
+
trailing_whitespace: {
|
|
215
|
+
pattern: /\s+$/,
|
|
216
|
+
exclude: ['*.md'], // Markdown uses trailing spaces for line breaks
|
|
217
|
+
severity: 'low',
|
|
218
|
+
autoFix: 'remove',
|
|
219
|
+
language: null,
|
|
220
|
+
description: 'Trailing whitespace at end of lines'
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Multiple consecutive blank lines
|
|
225
|
+
*/
|
|
226
|
+
multiple_blank_lines: {
|
|
227
|
+
pattern: /^\s*\n\s*\n\s*\n/,
|
|
228
|
+
exclude: [],
|
|
229
|
+
severity: 'low',
|
|
230
|
+
autoFix: 'replace',
|
|
231
|
+
language: null,
|
|
232
|
+
description: 'More than 2 consecutive blank lines'
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Hardcoded credentials patterns (expanded for comprehensive detection)
|
|
237
|
+
*/
|
|
238
|
+
hardcoded_secrets: {
|
|
239
|
+
pattern: /(password|secret|api[_-]?key|token|credential|auth)[_-]?(key|token|secret|pass)?\s*[:=]\s*["'`][^"'`\s]{8,}["'`]/i,
|
|
240
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*', '*.sample.*', 'README.*', '*.md'],
|
|
241
|
+
severity: 'critical',
|
|
242
|
+
autoFix: 'flag',
|
|
243
|
+
language: null,
|
|
244
|
+
description: 'Potential hardcoded credentials'
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* JWT tokens (eyJ prefix indicates base64 JSON header)
|
|
249
|
+
*/
|
|
250
|
+
jwt_tokens: {
|
|
251
|
+
pattern: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/,
|
|
252
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
253
|
+
severity: 'critical',
|
|
254
|
+
autoFix: 'flag',
|
|
255
|
+
language: null,
|
|
256
|
+
description: 'Hardcoded JWT token'
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* OpenAI API keys (sk-... format)
|
|
261
|
+
*/
|
|
262
|
+
openai_api_key: {
|
|
263
|
+
pattern: /sk-[a-zA-Z0-9]{32,}/,
|
|
264
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
265
|
+
severity: 'critical',
|
|
266
|
+
autoFix: 'flag',
|
|
267
|
+
language: null,
|
|
268
|
+
description: 'Hardcoded OpenAI API key'
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* GitHub tokens (personal access tokens, fine-grained tokens, OAuth)
|
|
273
|
+
*/
|
|
274
|
+
github_token: {
|
|
275
|
+
pattern: /(ghp_[a-zA-Z0-9]{36}|gho_[a-zA-Z0-9]{36}|ghu_[a-zA-Z0-9]{36}|ghs_[a-zA-Z0-9]{36}|ghr_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})/,
|
|
276
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
277
|
+
severity: 'critical',
|
|
278
|
+
autoFix: 'flag',
|
|
279
|
+
language: null,
|
|
280
|
+
description: 'Hardcoded GitHub token'
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* AWS credentials (access key IDs and secret keys)
|
|
285
|
+
*/
|
|
286
|
+
aws_credentials: {
|
|
287
|
+
pattern: /(AKIA[0-9A-Z]{16}|aws_secret_access_key\s*[:=]\s*["'`][A-Za-z0-9/+=]{40}["'`])/i,
|
|
288
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
289
|
+
severity: 'critical',
|
|
290
|
+
autoFix: 'flag',
|
|
291
|
+
language: null,
|
|
292
|
+
description: 'Hardcoded AWS credentials'
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Google Cloud / Firebase API keys and service accounts
|
|
297
|
+
*/
|
|
298
|
+
google_api_key: {
|
|
299
|
+
pattern: /(AIza[0-9A-Za-z_-]{35}|[0-9]+-[a-z0-9_]{32}\.apps\.googleusercontent\.com)/,
|
|
300
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
301
|
+
severity: 'critical',
|
|
302
|
+
autoFix: 'flag',
|
|
303
|
+
language: null,
|
|
304
|
+
description: 'Hardcoded Google/Firebase API key'
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Stripe API keys (live and test)
|
|
309
|
+
*/
|
|
310
|
+
stripe_api_key: {
|
|
311
|
+
pattern: /(sk_live_[a-zA-Z0-9]{24,}|sk_test_[a-zA-Z0-9]{24,}|rk_live_[a-zA-Z0-9]{24,}|rk_test_[a-zA-Z0-9]{24,})/,
|
|
312
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
313
|
+
severity: 'critical',
|
|
314
|
+
autoFix: 'flag',
|
|
315
|
+
language: null,
|
|
316
|
+
description: 'Hardcoded Stripe API key'
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Slack tokens (bot, user, webhook)
|
|
321
|
+
*/
|
|
322
|
+
slack_token: {
|
|
323
|
+
pattern: /(xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24}|xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24}|xoxa-[0-9]{10,}-[a-zA-Z0-9]{24}|https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]{8}\/B[A-Z0-9]{8,}\/[a-zA-Z0-9]{24})/,
|
|
324
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
325
|
+
severity: 'critical',
|
|
326
|
+
autoFix: 'flag',
|
|
327
|
+
language: null,
|
|
328
|
+
description: 'Hardcoded Slack token or webhook URL'
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Discord tokens and webhook URLs
|
|
333
|
+
*/
|
|
334
|
+
discord_token: {
|
|
335
|
+
pattern: /(discord.*["'`][A-Za-z0-9_-]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27}["'`]|https:\/\/discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[A-Za-z0-9_-]+)/i,
|
|
336
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
337
|
+
severity: 'critical',
|
|
338
|
+
autoFix: 'flag',
|
|
339
|
+
language: null,
|
|
340
|
+
description: 'Hardcoded Discord token or webhook'
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* SendGrid API key
|
|
345
|
+
*/
|
|
346
|
+
sendgrid_api_key: {
|
|
347
|
+
pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/,
|
|
348
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
349
|
+
severity: 'critical',
|
|
350
|
+
autoFix: 'flag',
|
|
351
|
+
language: null,
|
|
352
|
+
description: 'Hardcoded SendGrid API key'
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Twilio credentials
|
|
357
|
+
*/
|
|
358
|
+
twilio_credentials: {
|
|
359
|
+
pattern: /(AC[a-f0-9]{32}|SK[a-f0-9]{32})/,
|
|
360
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
361
|
+
severity: 'critical',
|
|
362
|
+
autoFix: 'flag',
|
|
363
|
+
language: null,
|
|
364
|
+
description: 'Hardcoded Twilio credentials'
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* NPM tokens
|
|
369
|
+
*/
|
|
370
|
+
npm_token: {
|
|
371
|
+
pattern: /npm_[a-zA-Z0-9]{36}/,
|
|
372
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*'],
|
|
373
|
+
severity: 'critical',
|
|
374
|
+
autoFix: 'flag',
|
|
375
|
+
language: null,
|
|
376
|
+
description: 'Hardcoded NPM token'
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Private keys (RSA, DSA, EC, PGP)
|
|
381
|
+
*/
|
|
382
|
+
private_key: {
|
|
383
|
+
pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/,
|
|
384
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*', '*.pem.example'],
|
|
385
|
+
severity: 'critical',
|
|
386
|
+
autoFix: 'flag',
|
|
387
|
+
language: null,
|
|
388
|
+
description: 'Private key in source code'
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Generic high-entropy strings (potential secrets)
|
|
393
|
+
*/
|
|
394
|
+
high_entropy_string: {
|
|
395
|
+
pattern: /["'`][A-Za-z0-9+/=_-]{40,}["'`]/,
|
|
396
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*', '*.lock', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'],
|
|
397
|
+
severity: 'medium',
|
|
398
|
+
autoFix: 'flag',
|
|
399
|
+
language: null,
|
|
400
|
+
description: 'High-entropy string that may be a secret',
|
|
401
|
+
requiresEntropyCheck: true,
|
|
402
|
+
entropyThreshold: 4.5
|
|
403
|
+
},
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Process.exit in libraries
|
|
407
|
+
*/
|
|
408
|
+
process_exit: {
|
|
409
|
+
pattern: /process\.exit\(/,
|
|
410
|
+
exclude: ['*.test.*', 'cli.js', 'index.js', 'bin/*'],
|
|
411
|
+
severity: 'high',
|
|
412
|
+
autoFix: 'flag',
|
|
413
|
+
language: 'javascript',
|
|
414
|
+
description: 'process.exit() should not be in library code'
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Bare URLs in code (should use constants)
|
|
419
|
+
*/
|
|
420
|
+
bare_urls: {
|
|
421
|
+
pattern: /https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/,
|
|
422
|
+
exclude: ['*.test.*', '*.md', 'package.json', 'README.*'],
|
|
423
|
+
severity: 'low',
|
|
424
|
+
autoFix: 'flag',
|
|
425
|
+
language: null,
|
|
426
|
+
description: 'Hardcoded URLs that should be configuration'
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// Freeze the patterns object for V8 optimization
|
|
431
|
+
deepFreeze(slopPatterns);
|
|
432
|
+
|
|
433
|
+
// ============================================================================
|
|
434
|
+
// Pre-indexed Maps for O(1) lookup performance (#18)
|
|
435
|
+
// Built once at module load time, avoiding iteration on every lookup
|
|
436
|
+
// ============================================================================
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Pre-indexed patterns by language
|
|
440
|
+
* Key: language name (or 'universal' for language-agnostic patterns)
|
|
441
|
+
* Value: Object of pattern name -> pattern definition
|
|
442
|
+
*/
|
|
443
|
+
const _patternsByLanguage = new Map();
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Pre-indexed patterns by severity
|
|
447
|
+
* Key: severity level ('critical', 'high', 'medium', 'low')
|
|
448
|
+
* Value: Object of pattern name -> pattern definition
|
|
449
|
+
*/
|
|
450
|
+
const _patternsBySeverity = new Map();
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Pre-indexed patterns by autoFix strategy
|
|
454
|
+
* Key: autoFix type ('remove', 'replace', 'add_logging', 'flag', 'none')
|
|
455
|
+
* Value: Object of pattern name -> pattern definition
|
|
456
|
+
*/
|
|
457
|
+
const _patternsByAutoFix = new Map();
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Set of all available languages for O(1) existence check
|
|
461
|
+
*/
|
|
462
|
+
const _availableLanguages = new Set();
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Set of all available severity levels for O(1) existence check
|
|
466
|
+
*/
|
|
467
|
+
const _availableSeverities = new Set();
|
|
468
|
+
|
|
469
|
+
// Build indexes at module load time
|
|
470
|
+
(function buildIndexes() {
|
|
471
|
+
for (const [name, pattern] of Object.entries(slopPatterns)) {
|
|
472
|
+
// Index by language
|
|
473
|
+
const lang = pattern.language || 'universal';
|
|
474
|
+
if (!_patternsByLanguage.has(lang)) {
|
|
475
|
+
_patternsByLanguage.set(lang, {});
|
|
476
|
+
}
|
|
477
|
+
_patternsByLanguage.get(lang)[name] = pattern;
|
|
478
|
+
_availableLanguages.add(lang);
|
|
479
|
+
|
|
480
|
+
// Index by severity
|
|
481
|
+
const severity = pattern.severity;
|
|
482
|
+
if (!_patternsBySeverity.has(severity)) {
|
|
483
|
+
_patternsBySeverity.set(severity, {});
|
|
484
|
+
}
|
|
485
|
+
_patternsBySeverity.get(severity)[name] = pattern;
|
|
486
|
+
_availableSeverities.add(severity);
|
|
487
|
+
|
|
488
|
+
// Index by autoFix strategy
|
|
489
|
+
const autoFix = pattern.autoFix || 'none';
|
|
490
|
+
if (!_patternsByAutoFix.has(autoFix)) {
|
|
491
|
+
_patternsByAutoFix.set(autoFix, {});
|
|
492
|
+
}
|
|
493
|
+
_patternsByAutoFix.get(autoFix)[name] = pattern;
|
|
494
|
+
}
|
|
495
|
+
})();
|
|
496
|
+
|
|
497
|
+
// Freeze the index Sets
|
|
498
|
+
Object.freeze(_availableLanguages);
|
|
499
|
+
Object.freeze(_availableSeverities);
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Get patterns for a specific language (O(1) lookup via pre-indexed Map)
|
|
503
|
+
* Includes universal patterns that apply to all languages
|
|
504
|
+
* @param {string} language - Language identifier ('javascript', 'python', 'rust', etc.)
|
|
505
|
+
* @returns {Object} Filtered patterns (language-specific + universal)
|
|
506
|
+
*/
|
|
507
|
+
function getPatternsForLanguage(language) {
|
|
508
|
+
const langPatterns = _patternsByLanguage.get(language) || {};
|
|
509
|
+
const universalPatterns = _patternsByLanguage.get('universal') || {};
|
|
510
|
+
|
|
511
|
+
// Merge language-specific with universal patterns
|
|
512
|
+
return { ...universalPatterns, ...langPatterns };
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Get patterns for a specific language only (excludes universal patterns)
|
|
517
|
+
* @param {string} language - Language identifier
|
|
518
|
+
* @returns {Object} Language-specific patterns only
|
|
519
|
+
*/
|
|
520
|
+
function getPatternsForLanguageOnly(language) {
|
|
521
|
+
return _patternsByLanguage.get(language) || {};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Get universal patterns (apply to all languages)
|
|
526
|
+
* @returns {Object} Universal patterns
|
|
527
|
+
*/
|
|
528
|
+
function getUniversalPatterns() {
|
|
529
|
+
return _patternsByLanguage.get('universal') || {};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Get patterns by severity (O(1) lookup via pre-indexed Map)
|
|
534
|
+
* @param {string} severity - Severity level ('critical', 'high', 'medium', 'low')
|
|
535
|
+
* @returns {Object} Filtered patterns
|
|
536
|
+
*/
|
|
537
|
+
function getPatternsBySeverity(severity) {
|
|
538
|
+
return _patternsBySeverity.get(severity) || {};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Get patterns by autoFix strategy (O(1) lookup via pre-indexed Map)
|
|
543
|
+
* @param {string} autoFix - AutoFix strategy ('remove', 'replace', 'add_logging', 'flag', 'none')
|
|
544
|
+
* @returns {Object} Filtered patterns
|
|
545
|
+
*/
|
|
546
|
+
function getPatternsByAutoFix(autoFix) {
|
|
547
|
+
return _patternsByAutoFix.get(autoFix) || {};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Get patterns matching multiple criteria (language AND severity)
|
|
552
|
+
* @param {Object} criteria - Filter criteria
|
|
553
|
+
* @param {string} [criteria.language] - Language filter
|
|
554
|
+
* @param {string} [criteria.severity] - Severity filter
|
|
555
|
+
* @param {string} [criteria.autoFix] - AutoFix strategy filter
|
|
556
|
+
* @returns {Object} Patterns matching all criteria
|
|
557
|
+
*/
|
|
558
|
+
function getPatternsByCriteria(criteria = {}) {
|
|
559
|
+
let result = { ...slopPatterns };
|
|
560
|
+
|
|
561
|
+
if (criteria.language) {
|
|
562
|
+
const langPatterns = getPatternsForLanguage(criteria.language);
|
|
563
|
+
result = Object.fromEntries(
|
|
564
|
+
Object.entries(result).filter(([name]) => name in langPatterns)
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (criteria.severity) {
|
|
569
|
+
const severityPatterns = getPatternsBySeverity(criteria.severity);
|
|
570
|
+
result = Object.fromEntries(
|
|
571
|
+
Object.entries(result).filter(([name]) => name in severityPatterns)
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (criteria.autoFix) {
|
|
576
|
+
const autoFixPatterns = getPatternsByAutoFix(criteria.autoFix);
|
|
577
|
+
result = Object.fromEntries(
|
|
578
|
+
Object.entries(result).filter(([name]) => name in autoFixPatterns)
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Get all available languages
|
|
587
|
+
* @returns {Array<string>} List of language identifiers
|
|
588
|
+
*/
|
|
589
|
+
function getAvailableLanguages() {
|
|
590
|
+
return Array.from(_availableLanguages);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Get all available severity levels
|
|
595
|
+
* @returns {Array<string>} List of severity levels
|
|
596
|
+
*/
|
|
597
|
+
function getAvailableSeverities() {
|
|
598
|
+
return Array.from(_availableSeverities);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Check if patterns exist for a language
|
|
603
|
+
* @param {string} language - Language identifier
|
|
604
|
+
* @returns {boolean} True if patterns exist
|
|
605
|
+
*/
|
|
606
|
+
function hasLanguage(language) {
|
|
607
|
+
return _patternsByLanguage.has(language);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Check if a file should be excluded based on pattern rules
|
|
612
|
+
* Uses pre-compiled regex cache for performance
|
|
613
|
+
* @param {string} filePath - File path to check
|
|
614
|
+
* @param {Array<string>} excludePatterns - Exclude patterns
|
|
615
|
+
* @returns {boolean} True if file should be excluded
|
|
616
|
+
*/
|
|
617
|
+
function isFileExcluded(filePath, excludePatterns) {
|
|
618
|
+
if (!excludePatterns || excludePatterns.length === 0) return false;
|
|
619
|
+
|
|
620
|
+
return excludePatterns.some(pattern => {
|
|
621
|
+
const regex = getCompiledPattern(pattern);
|
|
622
|
+
return regex.test(filePath);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
module.exports = {
|
|
627
|
+
slopPatterns,
|
|
628
|
+
// Pre-indexed lookup functions (O(1) performance)
|
|
629
|
+
getPatternsForLanguage,
|
|
630
|
+
getPatternsForLanguageOnly,
|
|
631
|
+
getUniversalPatterns,
|
|
632
|
+
getPatternsBySeverity,
|
|
633
|
+
getPatternsByAutoFix,
|
|
634
|
+
getPatternsByCriteria,
|
|
635
|
+
// Metadata functions
|
|
636
|
+
getAvailableLanguages,
|
|
637
|
+
getAvailableSeverities,
|
|
638
|
+
hasLanguage,
|
|
639
|
+
// File exclusion
|
|
640
|
+
isFileExcluded
|
|
641
|
+
};
|