awesome-slash 2.4.4 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +6 -6
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +88 -1
- package/README.md +173 -161
- package/SECURITY.md +25 -81
- package/adapters/codex/install.sh +58 -16
- package/adapters/opencode/install.sh +92 -23
- package/lib/index.js +47 -4
- package/lib/patterns/review-patterns.js +58 -11
- package/lib/patterns/slop-patterns.js +154 -147
- package/lib/platform/detect-platform.js +99 -350
- package/lib/platform/detection-configs.js +93 -0
- package/lib/platform/verify-tools.js +10 -78
- package/lib/schemas/README.md +195 -0
- package/lib/schemas/validator.js +247 -0
- package/lib/sources/custom-handler.js +199 -0
- package/lib/sources/policy-questions.js +239 -0
- package/lib/sources/source-cache.js +149 -0
- package/lib/state/workflow-state.js +363 -665
- package/lib/types/README.md +292 -0
- package/lib/types/agent-frontmatter.d.ts +134 -0
- package/lib/types/command-frontmatter.d.ts +107 -0
- package/lib/types/hook-frontmatter.d.ts +115 -0
- package/lib/types/index.d.ts +84 -0
- package/lib/types/plugin-manifest.d.ts +102 -0
- package/lib/types/skill-frontmatter.d.ts +89 -0
- package/lib/utils/cache-manager.js +154 -0
- package/lib/utils/context-optimizer.js +5 -36
- package/lib/utils/deprecation.js +37 -0
- package/lib/utils/shell-escape.js +88 -0
- package/mcp-server/index.js +513 -18
- package/package.json +6 -2
- package/plugins/deslop-around/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop-around/lib/index.js +170 -0
- package/plugins/deslop-around/lib/patterns/review-patterns.js +58 -11
- package/plugins/deslop-around/lib/patterns/slop-patterns.js +170 -129
- package/plugins/deslop-around/lib/platform/detect-platform.js +212 -123
- package/plugins/deslop-around/lib/platform/detection-configs.js +93 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +10 -1
- package/plugins/deslop-around/lib/schemas/README.md +195 -0
- package/plugins/deslop-around/lib/schemas/validator.js +205 -0
- package/plugins/deslop-around/lib/sources/custom-handler.js +199 -0
- package/plugins/deslop-around/lib/sources/policy-questions.js +239 -0
- package/plugins/deslop-around/lib/sources/source-cache.js +149 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +382 -484
- package/plugins/deslop-around/lib/types/README.md +292 -0
- package/plugins/deslop-around/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/deslop-around/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/deslop-around/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/deslop-around/lib/types/index.d.ts +84 -0
- package/plugins/deslop-around/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/deslop-around/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/deslop-around/lib/utils/cache-manager.js +154 -0
- package/plugins/deslop-around/lib/utils/context-optimizer.js +115 -37
- package/plugins/deslop-around/lib/utils/deprecation.js +37 -0
- package/plugins/deslop-around/lib/utils/shell-escape.js +88 -0
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/delivery-validator.md +2 -2
- package/plugins/next-task/agents/implementation-agent.md +3 -4
- package/plugins/next-task/agents/planning-agent.md +77 -19
- package/plugins/next-task/agents/review-orchestrator.md +21 -122
- package/plugins/next-task/agents/task-discoverer.md +164 -23
- package/plugins/next-task/commands/next-task.md +180 -14
- package/plugins/next-task/lib/index.js +170 -0
- package/plugins/next-task/lib/patterns/review-patterns.js +58 -11
- package/plugins/next-task/lib/patterns/slop-patterns.js +170 -129
- package/plugins/next-task/lib/platform/detect-platform.js +212 -123
- package/plugins/next-task/lib/platform/detection-configs.js +93 -0
- package/plugins/next-task/lib/platform/verify-tools.js +10 -1
- package/plugins/next-task/lib/schemas/README.md +195 -0
- package/plugins/next-task/lib/schemas/validator.js +205 -0
- package/plugins/next-task/lib/sources/custom-handler.js +199 -0
- package/plugins/next-task/lib/sources/policy-questions.js +239 -0
- package/plugins/next-task/lib/sources/source-cache.js +149 -0
- package/plugins/next-task/lib/state/workflow-state.js +382 -484
- package/plugins/next-task/lib/types/README.md +292 -0
- package/plugins/next-task/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/next-task/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/next-task/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/next-task/lib/types/index.d.ts +84 -0
- package/plugins/next-task/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/next-task/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/next-task/lib/utils/cache-manager.js +154 -0
- package/plugins/next-task/lib/utils/context-optimizer.js +115 -37
- package/plugins/next-task/lib/utils/deprecation.js +37 -0
- package/plugins/next-task/lib/utils/shell-escape.js +88 -0
- package/plugins/project-review/.claude-plugin/plugin.json +1 -1
- package/plugins/project-review/lib/index.js +170 -0
- package/plugins/project-review/lib/patterns/review-patterns.js +58 -11
- package/plugins/project-review/lib/patterns/slop-patterns.js +170 -129
- package/plugins/project-review/lib/platform/detect-platform.js +212 -123
- package/plugins/project-review/lib/platform/detection-configs.js +93 -0
- package/plugins/project-review/lib/platform/verify-tools.js +10 -1
- package/plugins/project-review/lib/schemas/README.md +195 -0
- package/plugins/project-review/lib/schemas/validator.js +205 -0
- package/plugins/project-review/lib/sources/custom-handler.js +199 -0
- package/plugins/project-review/lib/sources/policy-questions.js +239 -0
- package/plugins/project-review/lib/sources/source-cache.js +149 -0
- package/plugins/project-review/lib/state/workflow-state.js +382 -484
- package/plugins/project-review/lib/types/README.md +292 -0
- package/plugins/project-review/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/project-review/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/project-review/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/project-review/lib/types/index.d.ts +84 -0
- package/plugins/project-review/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/project-review/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/project-review/lib/utils/cache-manager.js +154 -0
- package/plugins/project-review/lib/utils/context-optimizer.js +115 -37
- package/plugins/project-review/lib/utils/deprecation.js +37 -0
- package/plugins/project-review/lib/utils/shell-escape.js +88 -0
- package/plugins/reality-check/.claude-plugin/plugin.json +1 -1
- package/plugins/reality-check/agents/code-explorer.md +1 -1
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/index.js +170 -0
- package/plugins/ship/lib/patterns/review-patterns.js +58 -11
- package/plugins/ship/lib/patterns/slop-patterns.js +170 -129
- package/plugins/ship/lib/platform/detect-platform.js +212 -123
- package/plugins/ship/lib/platform/detection-configs.js +93 -0
- package/plugins/ship/lib/platform/verify-tools.js +10 -1
- package/plugins/ship/lib/schemas/README.md +195 -0
- package/plugins/ship/lib/schemas/validator.js +205 -0
- package/plugins/ship/lib/sources/custom-handler.js +199 -0
- package/plugins/ship/lib/sources/policy-questions.js +239 -0
- package/plugins/ship/lib/sources/source-cache.js +149 -0
- package/plugins/ship/lib/state/workflow-state.js +382 -484
- package/plugins/ship/lib/types/README.md +292 -0
- package/plugins/ship/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/ship/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/ship/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/ship/lib/types/index.d.ts +84 -0
- package/plugins/ship/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/ship/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/ship/lib/utils/cache-manager.js +154 -0
- package/plugins/ship/lib/utils/context-optimizer.js +115 -37
- package/plugins/ship/lib/utils/deprecation.js +37 -0
- package/plugins/ship/lib/utils/shell-escape.js +88 -0
- package/lib/state/workflow-state.schema.json +0 -282
- package/plugins/deslop-around/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/agents/policy-selector.md +0 -248
- package/plugins/next-task/lib/state/tasks-registry.schema.json +0 -85
- package/plugins/next-task/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/lib/state/worktree-status.schema.json +0 -219
- package/plugins/project-review/lib/state/workflow-state.schema.json +0 -282
- package/plugins/ship/lib/state/workflow-state.schema.json +0 -282
|
@@ -8,34 +8,102 @@
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Deep freeze an object for V8 optimization and immutability
|
|
11
|
+
* Optimized: uses for-of instead of forEach to avoid function call overhead
|
|
11
12
|
* @param {Object} obj - Object to freeze
|
|
12
13
|
* @returns {Object} Frozen object
|
|
13
14
|
*/
|
|
14
15
|
function deepFreeze(obj) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
// Freeze the object first (fast path)
|
|
17
|
+
Object.freeze(obj);
|
|
18
|
+
|
|
19
|
+
// Then recursively freeze nested objects (only if needed)
|
|
20
|
+
// Use Object.keys() for cleaner iteration over own properties
|
|
21
|
+
for (const key of Object.keys(obj)) {
|
|
22
|
+
const value = obj[key];
|
|
23
|
+
if (value && typeof value === 'object' && !(value instanceof RegExp)) {
|
|
24
|
+
deepFreeze(value);
|
|
18
25
|
}
|
|
19
|
-
}
|
|
20
|
-
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return obj;
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
// Pre-compiled regex cache for performance
|
|
31
|
+
// Pre-compiled regex cache for performance (limited to prevent memory growth)
|
|
32
|
+
const MAX_PATTERN_CACHE_SIZE = 50;
|
|
24
33
|
const _compiledExcludePatterns = new Map();
|
|
25
34
|
|
|
35
|
+
// Exclude result cache for directory-level caching (limited to prevent memory growth)
|
|
36
|
+
const MAX_EXCLUDE_RESULT_CACHE_SIZE = 200;
|
|
37
|
+
const _excludeResultCache = new Map();
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Maximum allowed wildcards in a glob pattern to prevent ReDoS
|
|
41
|
+
*/
|
|
42
|
+
const MAX_GLOB_WILDCARDS = 10;
|
|
43
|
+
|
|
26
44
|
/**
|
|
27
45
|
* Get a compiled regex for an exclude pattern (cached)
|
|
46
|
+
* Uses safe regex construction to prevent catastrophic backtracking
|
|
47
|
+
* Optimized: uses Map.get() instead of has() + get() (eliminates redundant lookup)
|
|
28
48
|
* @param {string} pattern - Glob pattern to compile
|
|
29
49
|
* @returns {RegExp} Compiled regex
|
|
30
50
|
*/
|
|
31
51
|
function getCompiledPattern(pattern) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
// Try to get cached pattern (O(1) lookup)
|
|
53
|
+
let cached = _compiledExcludePatterns.get(pattern);
|
|
54
|
+
if (cached) {
|
|
55
|
+
return cached;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Enforce cache size limit using FIFO eviction
|
|
59
|
+
if (_compiledExcludePatterns.size >= MAX_PATTERN_CACHE_SIZE) {
|
|
60
|
+
const firstKey = _compiledExcludePatterns.keys().next().value;
|
|
61
|
+
_compiledExcludePatterns.delete(firstKey);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Count wildcards to prevent overly complex patterns
|
|
65
|
+
const wildcardCount = (pattern.match(/\*/g) || []).length;
|
|
66
|
+
if (wildcardCount > MAX_GLOB_WILDCARDS) {
|
|
67
|
+
// Too many wildcards - use a safe fallback that matches nothing dangerous
|
|
68
|
+
const safeRegex = /^$/;
|
|
69
|
+
_compiledExcludePatterns.set(pattern, safeRegex);
|
|
70
|
+
return safeRegex;
|
|
37
71
|
}
|
|
38
|
-
|
|
72
|
+
|
|
73
|
+
// Escape all regex metacharacters except *
|
|
74
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
75
|
+
|
|
76
|
+
// Convert glob patterns to regex:
|
|
77
|
+
// - Both * and ** use .* for backward compatibility (patterns match anywhere in path)
|
|
78
|
+
// - ReDoS protection is provided by MAX_GLOB_WILDCARDS limit above
|
|
79
|
+
let regexStr = escaped
|
|
80
|
+
.replace(/\*\*/g, '\0GLOBSTAR\0') // Temporarily mark globstar
|
|
81
|
+
.replace(/\*/g, '.*') // Single star: match anything (backward compatible)
|
|
82
|
+
.replace(/\0GLOBSTAR\0/g, '.*'); // Globstar: match anything
|
|
83
|
+
|
|
84
|
+
regexStr = '^' + regexStr + '$';
|
|
85
|
+
const compiledRegex = new RegExp(regexStr);
|
|
86
|
+
_compiledExcludePatterns.set(pattern, compiledRegex);
|
|
87
|
+
return compiledRegex;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Helper to create secret detection pattern with common metadata
|
|
92
|
+
* Reduces duplication across similar secret patterns (all have same severity/autoFix/language)
|
|
93
|
+
* @param {RegExp} pattern - Detection regex pattern
|
|
94
|
+
* @param {string} description - Human-readable description
|
|
95
|
+
* @param {Array<string>} [additionalExcludes=[]] - Extra files to exclude beyond standard test files
|
|
96
|
+
* @returns {Object} Complete pattern object
|
|
97
|
+
*/
|
|
98
|
+
function createSecretPattern(pattern, description, additionalExcludes = []) {
|
|
99
|
+
return {
|
|
100
|
+
pattern,
|
|
101
|
+
exclude: ['*.test.*', '*.spec.*', '*.example.*', ...additionalExcludes],
|
|
102
|
+
severity: 'critical',
|
|
103
|
+
autoFix: 'flag',
|
|
104
|
+
language: null,
|
|
105
|
+
description
|
|
106
|
+
};
|
|
39
107
|
}
|
|
40
108
|
|
|
41
109
|
/**
|
|
@@ -234,9 +302,12 @@ const slopPatterns = {
|
|
|
234
302
|
|
|
235
303
|
/**
|
|
236
304
|
* Hardcoded credentials patterns (expanded for comprehensive detection)
|
|
305
|
+
* Excludes common false positives:
|
|
306
|
+
* - Template placeholders: ${VAR}, {{VAR}}, <VAR>
|
|
307
|
+
* - Masked/example values: xxxxxxxx, ********
|
|
237
308
|
*/
|
|
238
309
|
hardcoded_secrets: {
|
|
239
|
-
pattern: /(password|secret|api[_-]?key|token|credential|auth)[_-]?(key|token|secret|pass)?\s*[:=]\s*["'`][^"'`\s]{8,}["'`]/i,
|
|
310
|
+
pattern: /(password|secret|api[_-]?key|token|credential|auth)[_-]?(key|token|secret|pass)?\s*[:=]\s*["'`](?!\$\{)(?!\{\{)(?!<[A-Z_])(?![x*#]{8,})(?![X*#]{8,})[^"'`\s]{8,}["'`]/i,
|
|
240
311
|
exclude: ['*.test.*', '*.spec.*', '*.example.*', '*.sample.*', 'README.*', '*.md'],
|
|
241
312
|
severity: 'critical',
|
|
242
313
|
autoFix: 'flag',
|
|
@@ -247,146 +318,99 @@ const slopPatterns = {
|
|
|
247
318
|
/**
|
|
248
319
|
* JWT tokens (eyJ prefix indicates base64 JSON header)
|
|
249
320
|
*/
|
|
250
|
-
jwt_tokens:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
autoFix: 'flag',
|
|
255
|
-
language: null,
|
|
256
|
-
description: 'Hardcoded JWT token'
|
|
257
|
-
},
|
|
321
|
+
jwt_tokens: createSecretPattern(
|
|
322
|
+
/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/,
|
|
323
|
+
'Hardcoded JWT token'
|
|
324
|
+
),
|
|
258
325
|
|
|
259
326
|
/**
|
|
260
327
|
* OpenAI API keys (sk-... format)
|
|
261
328
|
*/
|
|
262
|
-
openai_api_key:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
autoFix: 'flag',
|
|
267
|
-
language: null,
|
|
268
|
-
description: 'Hardcoded OpenAI API key'
|
|
269
|
-
},
|
|
329
|
+
openai_api_key: createSecretPattern(
|
|
330
|
+
/sk-[a-zA-Z0-9]{32,}/,
|
|
331
|
+
'Hardcoded OpenAI API key'
|
|
332
|
+
),
|
|
270
333
|
|
|
271
334
|
/**
|
|
272
335
|
* GitHub tokens (personal access tokens, fine-grained tokens, OAuth)
|
|
273
336
|
*/
|
|
274
|
-
github_token:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
autoFix: 'flag',
|
|
279
|
-
language: null,
|
|
280
|
-
description: 'Hardcoded GitHub token'
|
|
281
|
-
},
|
|
337
|
+
github_token: createSecretPattern(
|
|
338
|
+
/(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})/,
|
|
339
|
+
'Hardcoded GitHub token'
|
|
340
|
+
),
|
|
282
341
|
|
|
283
342
|
/**
|
|
284
343
|
* AWS credentials (access key IDs and secret keys)
|
|
285
344
|
*/
|
|
286
|
-
aws_credentials:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
autoFix: 'flag',
|
|
291
|
-
language: null,
|
|
292
|
-
description: 'Hardcoded AWS credentials'
|
|
293
|
-
},
|
|
345
|
+
aws_credentials: createSecretPattern(
|
|
346
|
+
/(AKIA[0-9A-Z]{16}|aws_secret_access_key\s*[:=]\s*["'`][A-Za-z0-9/+=]{40}["'`])/i,
|
|
347
|
+
'Hardcoded AWS credentials'
|
|
348
|
+
),
|
|
294
349
|
|
|
295
350
|
/**
|
|
296
351
|
* Google Cloud / Firebase API keys and service accounts
|
|
297
352
|
*/
|
|
298
|
-
google_api_key:
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
autoFix: 'flag',
|
|
303
|
-
language: null,
|
|
304
|
-
description: 'Hardcoded Google/Firebase API key'
|
|
305
|
-
},
|
|
353
|
+
google_api_key: createSecretPattern(
|
|
354
|
+
/(AIza[0-9A-Za-z_-]{35}|[0-9]+-[a-z0-9_]{32}\.apps\.googleusercontent\.com)/,
|
|
355
|
+
'Hardcoded Google/Firebase API key'
|
|
356
|
+
),
|
|
306
357
|
|
|
307
358
|
/**
|
|
308
359
|
* Stripe API keys (live and test)
|
|
309
360
|
*/
|
|
310
|
-
stripe_api_key:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
autoFix: 'flag',
|
|
315
|
-
language: null,
|
|
316
|
-
description: 'Hardcoded Stripe API key'
|
|
317
|
-
},
|
|
361
|
+
stripe_api_key: createSecretPattern(
|
|
362
|
+
/(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,})/,
|
|
363
|
+
'Hardcoded Stripe API key'
|
|
364
|
+
),
|
|
318
365
|
|
|
319
366
|
/**
|
|
320
367
|
* Slack tokens (bot, user, webhook)
|
|
321
368
|
*/
|
|
322
|
-
slack_token:
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
autoFix: 'flag',
|
|
327
|
-
language: null,
|
|
328
|
-
description: 'Hardcoded Slack token or webhook URL'
|
|
329
|
-
},
|
|
369
|
+
slack_token: createSecretPattern(
|
|
370
|
+
/(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})/,
|
|
371
|
+
'Hardcoded Slack token or webhook URL'
|
|
372
|
+
),
|
|
330
373
|
|
|
331
374
|
/**
|
|
332
375
|
* Discord tokens and webhook URLs
|
|
333
376
|
*/
|
|
334
|
-
discord_token:
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
autoFix: 'flag',
|
|
339
|
-
language: null,
|
|
340
|
-
description: 'Hardcoded Discord token or webhook'
|
|
341
|
-
},
|
|
377
|
+
discord_token: createSecretPattern(
|
|
378
|
+
/(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,
|
|
379
|
+
'Hardcoded Discord token or webhook'
|
|
380
|
+
),
|
|
342
381
|
|
|
343
382
|
/**
|
|
344
383
|
* SendGrid API key
|
|
345
384
|
*/
|
|
346
|
-
sendgrid_api_key:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
autoFix: 'flag',
|
|
351
|
-
language: null,
|
|
352
|
-
description: 'Hardcoded SendGrid API key'
|
|
353
|
-
},
|
|
385
|
+
sendgrid_api_key: createSecretPattern(
|
|
386
|
+
/SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/,
|
|
387
|
+
'Hardcoded SendGrid API key'
|
|
388
|
+
),
|
|
354
389
|
|
|
355
390
|
/**
|
|
356
391
|
* Twilio credentials
|
|
357
392
|
*/
|
|
358
|
-
twilio_credentials:
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
autoFix: 'flag',
|
|
363
|
-
language: null,
|
|
364
|
-
description: 'Hardcoded Twilio credentials'
|
|
365
|
-
},
|
|
393
|
+
twilio_credentials: createSecretPattern(
|
|
394
|
+
/(AC[a-f0-9]{32}|SK[a-f0-9]{32})/,
|
|
395
|
+
'Hardcoded Twilio credentials'
|
|
396
|
+
),
|
|
366
397
|
|
|
367
398
|
/**
|
|
368
399
|
* NPM tokens
|
|
369
400
|
*/
|
|
370
|
-
npm_token:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
autoFix: 'flag',
|
|
375
|
-
language: null,
|
|
376
|
-
description: 'Hardcoded NPM token'
|
|
377
|
-
},
|
|
401
|
+
npm_token: createSecretPattern(
|
|
402
|
+
/npm_[a-zA-Z0-9]{36}/,
|
|
403
|
+
'Hardcoded NPM token'
|
|
404
|
+
),
|
|
378
405
|
|
|
379
406
|
/**
|
|
380
407
|
* Private keys (RSA, DSA, EC, PGP)
|
|
381
408
|
*/
|
|
382
|
-
private_key:
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
language: null,
|
|
388
|
-
description: 'Private key in source code'
|
|
389
|
-
},
|
|
409
|
+
private_key: createSecretPattern(
|
|
410
|
+
/-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/,
|
|
411
|
+
'Private key in source code',
|
|
412
|
+
['*.pem.example'] // Additional excludes beyond standard test files
|
|
413
|
+
),
|
|
390
414
|
|
|
391
415
|
/**
|
|
392
416
|
* Generic high-entropy strings (potential secrets)
|
|
@@ -549,6 +573,7 @@ function getPatternsByAutoFix(autoFix) {
|
|
|
549
573
|
|
|
550
574
|
/**
|
|
551
575
|
* Get patterns matching multiple criteria (language AND severity)
|
|
576
|
+
* Optimized: single-pass filtering instead of chained Object.entries
|
|
552
577
|
* @param {Object} criteria - Filter criteria
|
|
553
578
|
* @param {string} [criteria.language] - Language filter
|
|
554
579
|
* @param {string} [criteria.severity] - Severity filter
|
|
@@ -556,27 +581,25 @@ function getPatternsByAutoFix(autoFix) {
|
|
|
556
581
|
* @returns {Object} Patterns matching all criteria
|
|
557
582
|
*/
|
|
558
583
|
function getPatternsByCriteria(criteria = {}) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const langPatterns = getPatternsForLanguage(criteria.language);
|
|
563
|
-
result = Object.fromEntries(
|
|
564
|
-
Object.entries(result).filter(([name]) => name in langPatterns)
|
|
565
|
-
);
|
|
584
|
+
// Fast path: no criteria means return all patterns
|
|
585
|
+
if (!criteria.language && !criteria.severity && !criteria.autoFix) {
|
|
586
|
+
return { ...slopPatterns };
|
|
566
587
|
}
|
|
567
588
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
);
|
|
573
|
-
}
|
|
589
|
+
// Pre-fetch filter sets (O(1) Map lookups)
|
|
590
|
+
const langPatterns = criteria.language ? getPatternsForLanguage(criteria.language) : null;
|
|
591
|
+
const severityPatterns = criteria.severity ? getPatternsBySeverity(criteria.severity) : null;
|
|
592
|
+
const autoFixPatterns = criteria.autoFix ? getPatternsByAutoFix(criteria.autoFix) : null;
|
|
574
593
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
);
|
|
594
|
+
// Single-pass filter: check all criteria at once
|
|
595
|
+
const result = {};
|
|
596
|
+
for (const [name, pattern] of Object.entries(slopPatterns)) {
|
|
597
|
+
// Check all criteria in one pass (short-circuit on first failure)
|
|
598
|
+
if (langPatterns && !(name in langPatterns)) continue;
|
|
599
|
+
if (severityPatterns && !(name in severityPatterns)) continue;
|
|
600
|
+
if (autoFixPatterns && !(name in autoFixPatterns)) continue;
|
|
601
|
+
|
|
602
|
+
result[name] = pattern;
|
|
580
603
|
}
|
|
581
604
|
|
|
582
605
|
return result;
|
|
@@ -609,7 +632,7 @@ function hasLanguage(language) {
|
|
|
609
632
|
|
|
610
633
|
/**
|
|
611
634
|
* Check if a file should be excluded based on pattern rules
|
|
612
|
-
* Uses pre-compiled regex cache for performance
|
|
635
|
+
* Uses pre-compiled regex cache and result cache for performance
|
|
613
636
|
* @param {string} filePath - File path to check
|
|
614
637
|
* @param {Array<string>} excludePatterns - Exclude patterns
|
|
615
638
|
* @returns {boolean} True if file should be excluded
|
|
@@ -617,10 +640,28 @@ function hasLanguage(language) {
|
|
|
617
640
|
function isFileExcluded(filePath, excludePatterns) {
|
|
618
641
|
if (!excludePatterns || excludePatterns.length === 0) return false;
|
|
619
642
|
|
|
620
|
-
|
|
643
|
+
// Create cache key using JSON.stringify for collision-resistant format
|
|
644
|
+
const cacheKey = JSON.stringify([filePath, excludePatterns]);
|
|
645
|
+
|
|
646
|
+
// Check cache first (O(1) lookup)
|
|
647
|
+
if (_excludeResultCache.has(cacheKey)) {
|
|
648
|
+
return _excludeResultCache.get(cacheKey);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Compute result
|
|
652
|
+
const result = excludePatterns.some(pattern => {
|
|
621
653
|
const regex = getCompiledPattern(pattern);
|
|
622
654
|
return regex.test(filePath);
|
|
623
655
|
});
|
|
656
|
+
|
|
657
|
+
// Store in cache with FIFO eviction
|
|
658
|
+
if (_excludeResultCache.size >= MAX_EXCLUDE_RESULT_CACHE_SIZE) {
|
|
659
|
+
const firstKey = _excludeResultCache.keys().next().value;
|
|
660
|
+
_excludeResultCache.delete(firstKey);
|
|
661
|
+
}
|
|
662
|
+
_excludeResultCache.set(cacheKey, result);
|
|
663
|
+
|
|
664
|
+
return result;
|
|
624
665
|
}
|
|
625
666
|
|
|
626
667
|
module.exports = {
|