awesome-slash 2.4.4 → 2.5.1

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.
Files changed (151) hide show
  1. package/.claude-plugin/marketplace.json +6 -6
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +123 -1
  4. package/README.md +186 -159
  5. package/SECURITY.md +25 -81
  6. package/adapters/codex/install.sh +58 -16
  7. package/adapters/opencode/install.sh +92 -23
  8. package/lib/index.js +47 -4
  9. package/lib/patterns/review-patterns.js +58 -11
  10. package/lib/patterns/slop-patterns.js +154 -147
  11. package/lib/platform/detect-platform.js +99 -350
  12. package/lib/platform/detection-configs.js +93 -0
  13. package/lib/platform/state-dir.js +122 -0
  14. package/lib/platform/verify-tools.js +10 -78
  15. package/lib/schemas/README.md +195 -0
  16. package/lib/schemas/validator.js +247 -0
  17. package/lib/sources/custom-handler.js +199 -0
  18. package/lib/sources/policy-questions.js +239 -0
  19. package/lib/sources/source-cache.js +164 -0
  20. package/lib/state/workflow-state.js +368 -665
  21. package/lib/types/README.md +292 -0
  22. package/lib/types/agent-frontmatter.d.ts +134 -0
  23. package/lib/types/command-frontmatter.d.ts +107 -0
  24. package/lib/types/hook-frontmatter.d.ts +115 -0
  25. package/lib/types/index.d.ts +84 -0
  26. package/lib/types/plugin-manifest.d.ts +102 -0
  27. package/lib/types/skill-frontmatter.d.ts +89 -0
  28. package/lib/utils/cache-manager.js +154 -0
  29. package/lib/utils/context-optimizer.js +5 -36
  30. package/lib/utils/deprecation.js +37 -0
  31. package/lib/utils/shell-escape.js +88 -0
  32. package/mcp-server/index.js +513 -22
  33. package/package.json +6 -2
  34. package/plugins/deslop-around/.claude-plugin/plugin.json +1 -1
  35. package/plugins/deslop-around/lib/index.js +170 -0
  36. package/plugins/deslop-around/lib/patterns/review-patterns.js +58 -11
  37. package/plugins/deslop-around/lib/patterns/slop-patterns.js +169 -129
  38. package/plugins/deslop-around/lib/platform/detect-platform.js +162 -316
  39. package/plugins/deslop-around/lib/platform/detection-configs.js +93 -0
  40. package/plugins/deslop-around/lib/platform/state-dir.js +122 -0
  41. package/plugins/deslop-around/lib/platform/verify-tools.js +10 -78
  42. package/plugins/deslop-around/lib/schemas/README.md +195 -0
  43. package/plugins/deslop-around/lib/schemas/validator.js +247 -0
  44. package/plugins/deslop-around/lib/sources/custom-handler.js +199 -0
  45. package/plugins/deslop-around/lib/sources/policy-questions.js +239 -0
  46. package/plugins/deslop-around/lib/sources/source-cache.js +164 -0
  47. package/plugins/deslop-around/lib/state/workflow-state.js +387 -484
  48. package/plugins/deslop-around/lib/types/README.md +292 -0
  49. package/plugins/deslop-around/lib/types/agent-frontmatter.d.ts +134 -0
  50. package/plugins/deslop-around/lib/types/command-frontmatter.d.ts +107 -0
  51. package/plugins/deslop-around/lib/types/hook-frontmatter.d.ts +115 -0
  52. package/plugins/deslop-around/lib/types/index.d.ts +84 -0
  53. package/plugins/deslop-around/lib/types/plugin-manifest.d.ts +102 -0
  54. package/plugins/deslop-around/lib/types/skill-frontmatter.d.ts +89 -0
  55. package/plugins/deslop-around/lib/utils/cache-manager.js +154 -0
  56. package/plugins/deslop-around/lib/utils/context-optimizer.js +115 -37
  57. package/plugins/deslop-around/lib/utils/deprecation.js +37 -0
  58. package/plugins/deslop-around/lib/utils/shell-escape.js +88 -0
  59. package/plugins/next-task/.claude-plugin/plugin.json +1 -1
  60. package/plugins/next-task/agents/delivery-validator.md +2 -2
  61. package/plugins/next-task/agents/implementation-agent.md +3 -4
  62. package/plugins/next-task/agents/planning-agent.md +77 -19
  63. package/plugins/next-task/agents/review-orchestrator.md +21 -122
  64. package/plugins/next-task/agents/task-discoverer.md +164 -23
  65. package/plugins/next-task/commands/next-task.md +180 -14
  66. package/plugins/next-task/lib/index.js +170 -0
  67. package/plugins/next-task/lib/patterns/review-patterns.js +58 -11
  68. package/plugins/next-task/lib/patterns/slop-patterns.js +169 -129
  69. package/plugins/next-task/lib/platform/detect-platform.js +162 -316
  70. package/plugins/next-task/lib/platform/detection-configs.js +93 -0
  71. package/plugins/next-task/lib/platform/state-dir.js +122 -0
  72. package/plugins/next-task/lib/platform/verify-tools.js +10 -78
  73. package/plugins/next-task/lib/schemas/README.md +195 -0
  74. package/plugins/next-task/lib/schemas/validator.js +247 -0
  75. package/plugins/next-task/lib/sources/custom-handler.js +199 -0
  76. package/plugins/next-task/lib/sources/policy-questions.js +239 -0
  77. package/plugins/next-task/lib/sources/source-cache.js +164 -0
  78. package/plugins/next-task/lib/state/workflow-state.js +387 -484
  79. package/plugins/next-task/lib/types/README.md +292 -0
  80. package/plugins/next-task/lib/types/agent-frontmatter.d.ts +134 -0
  81. package/plugins/next-task/lib/types/command-frontmatter.d.ts +107 -0
  82. package/plugins/next-task/lib/types/hook-frontmatter.d.ts +115 -0
  83. package/plugins/next-task/lib/types/index.d.ts +84 -0
  84. package/plugins/next-task/lib/types/plugin-manifest.d.ts +102 -0
  85. package/plugins/next-task/lib/types/skill-frontmatter.d.ts +89 -0
  86. package/plugins/next-task/lib/utils/cache-manager.js +154 -0
  87. package/plugins/next-task/lib/utils/context-optimizer.js +115 -37
  88. package/plugins/next-task/lib/utils/deprecation.js +37 -0
  89. package/plugins/next-task/lib/utils/shell-escape.js +88 -0
  90. package/plugins/project-review/.claude-plugin/plugin.json +1 -1
  91. package/plugins/project-review/lib/index.js +170 -0
  92. package/plugins/project-review/lib/patterns/review-patterns.js +58 -11
  93. package/plugins/project-review/lib/patterns/slop-patterns.js +169 -129
  94. package/plugins/project-review/lib/platform/detect-platform.js +162 -316
  95. package/plugins/project-review/lib/platform/detection-configs.js +93 -0
  96. package/plugins/project-review/lib/platform/state-dir.js +122 -0
  97. package/plugins/project-review/lib/platform/verify-tools.js +10 -78
  98. package/plugins/project-review/lib/schemas/README.md +195 -0
  99. package/plugins/project-review/lib/schemas/validator.js +247 -0
  100. package/plugins/project-review/lib/sources/custom-handler.js +199 -0
  101. package/plugins/project-review/lib/sources/policy-questions.js +239 -0
  102. package/plugins/project-review/lib/sources/source-cache.js +164 -0
  103. package/plugins/project-review/lib/state/workflow-state.js +387 -484
  104. package/plugins/project-review/lib/types/README.md +292 -0
  105. package/plugins/project-review/lib/types/agent-frontmatter.d.ts +134 -0
  106. package/plugins/project-review/lib/types/command-frontmatter.d.ts +107 -0
  107. package/plugins/project-review/lib/types/hook-frontmatter.d.ts +115 -0
  108. package/plugins/project-review/lib/types/index.d.ts +84 -0
  109. package/plugins/project-review/lib/types/plugin-manifest.d.ts +102 -0
  110. package/plugins/project-review/lib/types/skill-frontmatter.d.ts +89 -0
  111. package/plugins/project-review/lib/utils/cache-manager.js +154 -0
  112. package/plugins/project-review/lib/utils/context-optimizer.js +115 -37
  113. package/plugins/project-review/lib/utils/deprecation.js +37 -0
  114. package/plugins/project-review/lib/utils/shell-escape.js +88 -0
  115. package/plugins/reality-check/.claude-plugin/plugin.json +1 -1
  116. package/plugins/reality-check/agents/code-explorer.md +1 -1
  117. package/plugins/ship/.claude-plugin/plugin.json +1 -1
  118. package/plugins/ship/lib/index.js +170 -0
  119. package/plugins/ship/lib/patterns/review-patterns.js +58 -11
  120. package/plugins/ship/lib/patterns/slop-patterns.js +169 -129
  121. package/plugins/ship/lib/platform/detect-platform.js +162 -316
  122. package/plugins/ship/lib/platform/detection-configs.js +93 -0
  123. package/plugins/ship/lib/platform/state-dir.js +122 -0
  124. package/plugins/ship/lib/platform/verify-tools.js +10 -78
  125. package/plugins/ship/lib/schemas/README.md +195 -0
  126. package/plugins/ship/lib/schemas/validator.js +247 -0
  127. package/plugins/ship/lib/sources/custom-handler.js +199 -0
  128. package/plugins/ship/lib/sources/policy-questions.js +239 -0
  129. package/plugins/ship/lib/sources/source-cache.js +164 -0
  130. package/plugins/ship/lib/state/workflow-state.js +387 -484
  131. package/plugins/ship/lib/types/README.md +292 -0
  132. package/plugins/ship/lib/types/agent-frontmatter.d.ts +134 -0
  133. package/plugins/ship/lib/types/command-frontmatter.d.ts +107 -0
  134. package/plugins/ship/lib/types/hook-frontmatter.d.ts +115 -0
  135. package/plugins/ship/lib/types/index.d.ts +84 -0
  136. package/plugins/ship/lib/types/plugin-manifest.d.ts +102 -0
  137. package/plugins/ship/lib/types/skill-frontmatter.d.ts +89 -0
  138. package/plugins/ship/lib/utils/cache-manager.js +154 -0
  139. package/plugins/ship/lib/utils/context-optimizer.js +115 -37
  140. package/plugins/ship/lib/utils/deprecation.js +37 -0
  141. package/plugins/ship/lib/utils/shell-escape.js +88 -0
  142. package/scripts/install/codex.sh +216 -72
  143. package/scripts/install/opencode.sh +197 -21
  144. package/lib/state/workflow-state.schema.json +0 -282
  145. package/plugins/deslop-around/lib/state/workflow-state.schema.json +0 -282
  146. package/plugins/next-task/agents/policy-selector.md +0 -248
  147. package/plugins/next-task/lib/state/tasks-registry.schema.json +0 -85
  148. package/plugins/next-task/lib/state/workflow-state.schema.json +0 -282
  149. package/plugins/next-task/lib/state/worktree-status.schema.json +0 -219
  150. package/plugins/project-review/lib/state/workflow-state.schema.json +0 -282
  151. 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
- Object.keys(obj).forEach(key => {
16
- if (typeof obj[key] === 'object' && obj[key] !== null && !(obj[key] instanceof RegExp)) {
17
- deepFreeze(obj[key]);
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
- return Object.freeze(obj);
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
- 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));
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
- return _compiledExcludePatterns.get(pattern);
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- },
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
- 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
- );
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
- if (criteria.severity) {
569
- const severityPatterns = getPatternsBySeverity(criteria.severity);
570
- result = Object.fromEntries(
571
- Object.entries(result).filter(([name]) => name in severityPatterns)
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;
593
+
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;
574
601
 
575
- if (criteria.autoFix) {
576
- const autoFixPatterns = getPatternsByAutoFix(criteria.autoFix);
577
- result = Object.fromEntries(
578
- Object.entries(result).filter(([name]) => name in autoFixPatterns)
579
- );
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,27 @@ function hasLanguage(language) {
617
640
  function isFileExcluded(filePath, excludePatterns) {
618
641
  if (!excludePatterns || excludePatterns.length === 0) return false;
619
642
 
620
- return excludePatterns.some(pattern => {
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
+ const cached = _excludeResultCache.get(cacheKey);
648
+ if (cached !== undefined) return cached;
649
+
650
+ // Compute result
651
+ const result = excludePatterns.some(pattern => {
621
652
  const regex = getCompiledPattern(pattern);
622
653
  return regex.test(filePath);
623
654
  });
655
+
656
+ // Store in cache with FIFO eviction
657
+ if (_excludeResultCache.size >= MAX_EXCLUDE_RESULT_CACHE_SIZE) {
658
+ const firstKey = _excludeResultCache.keys().next().value;
659
+ _excludeResultCache.delete(firstKey);
660
+ }
661
+ _excludeResultCache.set(cacheKey, result);
662
+
663
+ return result;
624
664
  }
625
665
 
626
666
  module.exports = {