getdoorman 1.0.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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +181 -0
  3. package/bin/doorman.js +444 -0
  4. package/package.json +74 -0
  5. package/src/ai-fixer.js +559 -0
  6. package/src/ast-scanner.js +434 -0
  7. package/src/auth.js +149 -0
  8. package/src/baseline.js +48 -0
  9. package/src/compliance.js +539 -0
  10. package/src/config.js +466 -0
  11. package/src/custom-rules.js +32 -0
  12. package/src/dashboard.js +202 -0
  13. package/src/detector.js +142 -0
  14. package/src/fix-engine.js +48 -0
  15. package/src/fix-registry-extra.js +95 -0
  16. package/src/fix-registry-go-rust.js +77 -0
  17. package/src/fix-registry-java-csharp.js +77 -0
  18. package/src/fix-registry-js.js +99 -0
  19. package/src/fix-registry-mcp-ai.js +57 -0
  20. package/src/fix-registry-python.js +87 -0
  21. package/src/fixer-ruby-php.js +608 -0
  22. package/src/fixer.js +2113 -0
  23. package/src/hooks.js +115 -0
  24. package/src/ignore.js +176 -0
  25. package/src/index.js +384 -0
  26. package/src/metrics.js +126 -0
  27. package/src/monorepo.js +65 -0
  28. package/src/presets.js +54 -0
  29. package/src/reporter.js +975 -0
  30. package/src/rule-worker.js +36 -0
  31. package/src/rules/ast-rules.js +756 -0
  32. package/src/rules/bugs/accessibility.js +235 -0
  33. package/src/rules/bugs/ai-codegen-fixable.js +172 -0
  34. package/src/rules/bugs/ai-codegen.js +365 -0
  35. package/src/rules/bugs/code-smell-bugs.js +247 -0
  36. package/src/rules/bugs/crypto-bugs.js +195 -0
  37. package/src/rules/bugs/docker-bugs.js +158 -0
  38. package/src/rules/bugs/general.js +361 -0
  39. package/src/rules/bugs/go-bugs.js +279 -0
  40. package/src/rules/bugs/index.js +73 -0
  41. package/src/rules/bugs/js-api.js +257 -0
  42. package/src/rules/bugs/js-array-object.js +210 -0
  43. package/src/rules/bugs/js-async-fixable.js +223 -0
  44. package/src/rules/bugs/js-async.js +211 -0
  45. package/src/rules/bugs/js-closure-scope.js +182 -0
  46. package/src/rules/bugs/js-database.js +203 -0
  47. package/src/rules/bugs/js-error-handling.js +148 -0
  48. package/src/rules/bugs/js-logic.js +261 -0
  49. package/src/rules/bugs/js-memory.js +214 -0
  50. package/src/rules/bugs/js-node.js +361 -0
  51. package/src/rules/bugs/js-react.js +373 -0
  52. package/src/rules/bugs/js-regex.js +200 -0
  53. package/src/rules/bugs/js-state.js +272 -0
  54. package/src/rules/bugs/js-type-coercion.js +318 -0
  55. package/src/rules/bugs/nextjs-bugs.js +242 -0
  56. package/src/rules/bugs/nextjs-fixable.js +120 -0
  57. package/src/rules/bugs/node-fixable.js +178 -0
  58. package/src/rules/bugs/python-advanced.js +245 -0
  59. package/src/rules/bugs/python-fixable.js +98 -0
  60. package/src/rules/bugs/python.js +284 -0
  61. package/src/rules/bugs/react-fixable.js +207 -0
  62. package/src/rules/bugs/ruby-bugs.js +182 -0
  63. package/src/rules/bugs/shell-bugs.js +181 -0
  64. package/src/rules/bugs/silent-failures.js +261 -0
  65. package/src/rules/bugs/ts-bugs.js +235 -0
  66. package/src/rules/bugs/unused-vars.js +65 -0
  67. package/src/rules/compliance/accessibility-ext.js +468 -0
  68. package/src/rules/compliance/education.js +322 -0
  69. package/src/rules/compliance/financial.js +421 -0
  70. package/src/rules/compliance/frameworks.js +507 -0
  71. package/src/rules/compliance/healthcare.js +520 -0
  72. package/src/rules/compliance/index.js +2714 -0
  73. package/src/rules/compliance/regional-eu.js +480 -0
  74. package/src/rules/compliance/regional-international.js +903 -0
  75. package/src/rules/cost/index.js +1993 -0
  76. package/src/rules/data/index.js +2503 -0
  77. package/src/rules/dependencies/index.js +1684 -0
  78. package/src/rules/deployment/index.js +2050 -0
  79. package/src/rules/index.js +71 -0
  80. package/src/rules/infrastructure/index.js +3048 -0
  81. package/src/rules/performance/index.js +3455 -0
  82. package/src/rules/quality/index.js +3175 -0
  83. package/src/rules/reliability/index.js +3040 -0
  84. package/src/rules/scope-rules.js +815 -0
  85. package/src/rules/security/ai-api.js +1177 -0
  86. package/src/rules/security/auth.js +1328 -0
  87. package/src/rules/security/cors.js +127 -0
  88. package/src/rules/security/crypto.js +527 -0
  89. package/src/rules/security/csharp.js +862 -0
  90. package/src/rules/security/csrf.js +193 -0
  91. package/src/rules/security/dart.js +835 -0
  92. package/src/rules/security/deserialization.js +291 -0
  93. package/src/rules/security/file-upload.js +187 -0
  94. package/src/rules/security/go.js +850 -0
  95. package/src/rules/security/headers.js +235 -0
  96. package/src/rules/security/index.js +65 -0
  97. package/src/rules/security/injection.js +1639 -0
  98. package/src/rules/security/mcp-server.js +71 -0
  99. package/src/rules/security/misconfiguration.js +660 -0
  100. package/src/rules/security/oauth-jwt.js +329 -0
  101. package/src/rules/security/path-traversal.js +295 -0
  102. package/src/rules/security/php.js +1054 -0
  103. package/src/rules/security/prototype-pollution.js +283 -0
  104. package/src/rules/security/rate-limiting.js +208 -0
  105. package/src/rules/security/ruby.js +1061 -0
  106. package/src/rules/security/rust.js +693 -0
  107. package/src/rules/security/secrets.js +747 -0
  108. package/src/rules/security/shell.js +647 -0
  109. package/src/rules/security/ssrf.js +298 -0
  110. package/src/rules/security/supply-chain-advanced.js +393 -0
  111. package/src/rules/security/supply-chain.js +734 -0
  112. package/src/rules/security/swift.js +835 -0
  113. package/src/rules/security/taint.js +27 -0
  114. package/src/rules/security/xss.js +520 -0
  115. package/src/scan-cache.js +71 -0
  116. package/src/scanner.js +710 -0
  117. package/src/scope-analyzer.js +685 -0
  118. package/src/share.js +88 -0
  119. package/src/taint.js +300 -0
  120. package/src/telemetry.js +183 -0
  121. package/src/tracer.js +190 -0
  122. package/src/upload.js +35 -0
  123. package/src/worker.js +31 -0
@@ -0,0 +1,1054 @@
1
+ /**
2
+ * PHP Security Rules (SEC-PHP-001 through SEC-PHP-060)
3
+ *
4
+ * Each rule scans PHP source files for security vulnerabilities.
5
+ */
6
+
7
+ const isPHP = (f) => f.endsWith('.php');
8
+
9
+ const SKIP_PATH = /[/\\](test|tests|__tests__|__mocks__|mocks|fixtures|__fixtures__|spec|__snapshots__|node_modules|vendor|dist|build)[/\\]/i;
10
+ const COMMENT_LINE = /^\s*(\/\/|#|\/\*|\*)/;
11
+
12
+ function scanLines(content, regex, file, rule) {
13
+ const findings = [];
14
+ const lines = content.split('\n');
15
+ for (let i = 0; i < lines.length; i++) {
16
+ const line = lines[i];
17
+ if (COMMENT_LINE.test(line)) continue;
18
+ if (regex.test(line)) {
19
+ findings.push({
20
+ ruleId: rule.id,
21
+ category: rule.category,
22
+ severity: rule.severity,
23
+ title: rule.title,
24
+ description: rule.description,
25
+ confidence: rule.confidence,
26
+ file,
27
+ line: i + 1,
28
+ fix: rule.fix || null,
29
+ });
30
+ }
31
+ }
32
+ return findings;
33
+ }
34
+
35
+ function checkPHP(rule, files, ...patterns) {
36
+ const findings = [];
37
+ for (const [path, content] of files) {
38
+ if (SKIP_PATH.test(path)) continue;
39
+ if (isPHP(path)) {
40
+ for (const p of patterns) {
41
+ findings.push(...scanLines(content, p, path, rule));
42
+ }
43
+ }
44
+ }
45
+ return findings;
46
+ }
47
+
48
+ const rules = [
49
+ // ===========================================================================
50
+ // SQL Injection (001–010)
51
+ // ===========================================================================
52
+
53
+ // SEC-PHP-001: SQL injection via string concatenation in mysql_query
54
+ {
55
+ id: 'SEC-PHP-001',
56
+ category: 'security',
57
+ severity: 'critical',
58
+ confidence: 'likely',
59
+ title: 'SQL Injection via mysql_query with Concatenation',
60
+ description: 'Concatenating user input into mysql_query() allows SQL injection.',
61
+ fix: { suggestion: 'Use PDO prepared statements with parameterized queries.' },
62
+ check({ files }) {
63
+ return checkPHP(this, files,
64
+ /mysql_query\s*\(\s*["'].*\.\s*\$/,
65
+ );
66
+ },
67
+ },
68
+
69
+ // SEC-PHP-002: SQL injection via mysqli_query with variables
70
+ {
71
+ id: 'SEC-PHP-002',
72
+ category: 'security',
73
+ severity: 'critical',
74
+ confidence: 'likely',
75
+ title: 'SQL Injection via mysqli_query with Variable Interpolation',
76
+ description: 'Using variable interpolation in mysqli_query() enables SQL injection.',
77
+ fix: { suggestion: 'Use mysqli_prepare() with bind_param() for parameterized queries.' },
78
+ check({ files }) {
79
+ return checkPHP(this, files,
80
+ /mysqli_query\s*\(.*["'].*\$_(?:GET|POST|REQUEST|COOKIE)/,
81
+ );
82
+ },
83
+ },
84
+
85
+ // SEC-PHP-003: SQL injection via PDO query with interpolation
86
+ {
87
+ id: 'SEC-PHP-003',
88
+ category: 'security',
89
+ severity: 'critical',
90
+ confidence: 'likely',
91
+ title: 'SQL Injection via PDO::query with Variable Interpolation',
92
+ description: 'Using string interpolation in PDO::query() bypasses prepared statement protection.',
93
+ fix: { suggestion: 'Use PDO::prepare() with bindParam() or bindValue().' },
94
+ check({ files }) {
95
+ return checkPHP(this, files,
96
+ /->query\s*\(\s*["'].*\$_(?:GET|POST|REQUEST)/,
97
+ );
98
+ },
99
+ },
100
+
101
+ // SEC-PHP-004: SQL injection via string concatenation with $_GET
102
+ {
103
+ id: 'SEC-PHP-004',
104
+ category: 'security',
105
+ severity: 'critical',
106
+ confidence: 'likely',
107
+ title: 'SQL Injection via $_GET in Query String',
108
+ description: 'Directly embedding $_GET values in SQL queries allows injection.',
109
+ fix: { suggestion: 'Use prepared statements with bound parameters.' },
110
+ check({ files }) {
111
+ return checkPHP(this, files,
112
+ /(?:SELECT|INSERT|UPDATE|DELETE).*\.\s*\$_GET/,
113
+ );
114
+ },
115
+ },
116
+
117
+ // SEC-PHP-005: SQL injection via $_POST in query
118
+ {
119
+ id: 'SEC-PHP-005',
120
+ category: 'security',
121
+ severity: 'critical',
122
+ confidence: 'likely',
123
+ title: 'SQL Injection via $_POST in Query String',
124
+ description: 'Directly embedding $_POST values in SQL queries allows injection.',
125
+ fix: { suggestion: 'Use prepared statements with bound parameters.' },
126
+ check({ files }) {
127
+ return checkPHP(this, files,
128
+ /(?:SELECT|INSERT|UPDATE|DELETE).*\.\s*\$_POST/,
129
+ );
130
+ },
131
+ },
132
+
133
+ // SEC-PHP-006: SQL injection via sprintf in query
134
+ {
135
+ id: 'SEC-PHP-006',
136
+ category: 'security',
137
+ severity: 'critical',
138
+ confidence: 'likely',
139
+ title: 'SQL Injection via sprintf()',
140
+ description: 'Using sprintf to build SQL queries with user input allows injection.',
141
+ fix: { suggestion: 'Use prepared statements instead of sprintf for SQL.' },
142
+ check({ files }) {
143
+ return checkPHP(this, files,
144
+ /sprintf\s*\(\s*["'](?:SELECT|INSERT|UPDATE|DELETE)/,
145
+ );
146
+ },
147
+ },
148
+
149
+ // SEC-PHP-007: SQL injection via $_REQUEST
150
+ {
151
+ id: 'SEC-PHP-007',
152
+ category: 'security',
153
+ severity: 'critical',
154
+ confidence: 'likely',
155
+ title: 'SQL Injection via $_REQUEST in Query',
156
+ description: 'Using $_REQUEST in SQL queries allows injection from any request method.',
157
+ fix: { suggestion: 'Use prepared statements with bound parameters.' },
158
+ check({ files }) {
159
+ return checkPHP(this, files,
160
+ /(?:SELECT|INSERT|UPDATE|DELETE).*\.\s*\$_REQUEST/,
161
+ );
162
+ },
163
+ },
164
+
165
+ // SEC-PHP-008: SQL injection via order by user input
166
+ {
167
+ id: 'SEC-PHP-008',
168
+ category: 'security',
169
+ severity: 'high',
170
+ confidence: 'likely',
171
+ title: 'SQL Injection via ORDER BY with User Input',
172
+ description: 'Passing user input to ORDER BY clause allows SQL injection.',
173
+ fix: { suggestion: 'Whitelist allowed column names for ORDER BY.' },
174
+ check({ files }) {
175
+ return checkPHP(this, files,
176
+ /ORDER\s+BY.*\.\s*\$_(?:GET|POST|REQUEST)/,
177
+ );
178
+ },
179
+ },
180
+
181
+ // SEC-PHP-009: SQL injection via LIKE with user input
182
+ {
183
+ id: 'SEC-PHP-009',
184
+ category: 'security',
185
+ severity: 'high',
186
+ confidence: 'likely',
187
+ title: 'SQL Injection via LIKE Clause',
188
+ description: 'Concatenating user input in LIKE clauses allows SQL injection.',
189
+ fix: { suggestion: 'Use prepared statements with bound parameters for LIKE.' },
190
+ check({ files }) {
191
+ return checkPHP(this, files,
192
+ /LIKE\s+['"]%.*\.\s*\$_(?:GET|POST|REQUEST)/,
193
+ );
194
+ },
195
+ },
196
+
197
+ // SEC-PHP-010: SQL injection via db->query without escaping
198
+ {
199
+ id: 'SEC-PHP-010',
200
+ category: 'security',
201
+ severity: 'critical',
202
+ confidence: 'likely',
203
+ title: 'SQL Injection via $db->query with Concatenation',
204
+ description: 'Using $db->query() with concatenated variables allows SQL injection.',
205
+ fix: { suggestion: 'Use $db->prepare() with parameter binding.' },
206
+ check({ files }) {
207
+ return checkPHP(this, files,
208
+ /\$\w+->query\s*\(\s*["'].*\.\s*\$/,
209
+ );
210
+ },
211
+ },
212
+
213
+ // ===========================================================================
214
+ // XSS (011–015)
215
+ // ===========================================================================
216
+
217
+ // SEC-PHP-011: XSS via echo with $_GET
218
+ {
219
+ id: 'SEC-PHP-011',
220
+ category: 'security',
221
+ severity: 'high',
222
+ confidence: 'likely',
223
+ title: 'XSS via echo with $_GET',
224
+ description: 'Echoing $_GET values without escaping enables reflected XSS.',
225
+ fix: { suggestion: 'Use htmlspecialchars($value, ENT_QUOTES, "UTF-8") before output.' },
226
+ check({ files }) {
227
+ return checkPHP(this, files,
228
+ /echo\s+.*\$_GET/,
229
+ );
230
+ },
231
+ },
232
+
233
+ // SEC-PHP-012: XSS via echo with $_POST
234
+ {
235
+ id: 'SEC-PHP-012',
236
+ category: 'security',
237
+ severity: 'high',
238
+ confidence: 'likely',
239
+ title: 'XSS via echo with $_POST',
240
+ description: 'Echoing $_POST values without escaping enables XSS.',
241
+ fix: { suggestion: 'Use htmlspecialchars() before outputting user input.' },
242
+ check({ files }) {
243
+ return checkPHP(this, files,
244
+ /echo\s+.*\$_POST/,
245
+ );
246
+ },
247
+ },
248
+
249
+ // SEC-PHP-013: XSS via print with user input
250
+ {
251
+ id: 'SEC-PHP-013',
252
+ category: 'security',
253
+ severity: 'high',
254
+ confidence: 'likely',
255
+ title: 'XSS via print with User Input',
256
+ description: 'Using print with unescaped user input enables XSS.',
257
+ fix: { suggestion: 'Use htmlspecialchars() before outputting user input.' },
258
+ check({ files }) {
259
+ return checkPHP(this, files,
260
+ /print\s+.*\$_(?:GET|POST|REQUEST)/,
261
+ );
262
+ },
263
+ },
264
+
265
+ // SEC-PHP-014: XSS via printf/sprintf output
266
+ {
267
+ id: 'SEC-PHP-014',
268
+ category: 'security',
269
+ severity: 'high',
270
+ confidence: 'likely',
271
+ title: 'XSS via printf with User Input',
272
+ description: 'Using printf with unescaped user input in HTML context enables XSS.',
273
+ fix: { suggestion: 'Use htmlspecialchars() on user input before passing to printf.' },
274
+ check({ files }) {
275
+ return checkPHP(this, files,
276
+ /printf\s*\(.*\$_(?:GET|POST|REQUEST)/,
277
+ );
278
+ },
279
+ },
280
+
281
+ // SEC-PHP-015: XSS via $_REQUEST echo
282
+ {
283
+ id: 'SEC-PHP-015',
284
+ category: 'security',
285
+ severity: 'high',
286
+ confidence: 'likely',
287
+ title: 'XSS via echo with $_REQUEST',
288
+ description: 'Echoing $_REQUEST without escaping enables XSS from any input source.',
289
+ fix: { suggestion: 'Use htmlspecialchars() before outputting user input.' },
290
+ check({ files }) {
291
+ return checkPHP(this, files,
292
+ /echo\s+.*\$_REQUEST/,
293
+ );
294
+ },
295
+ },
296
+
297
+ // ===========================================================================
298
+ // Command Injection (016–020)
299
+ // ===========================================================================
300
+
301
+ // SEC-PHP-016: Command injection via exec()
302
+ {
303
+ id: 'SEC-PHP-016',
304
+ category: 'security',
305
+ severity: 'critical',
306
+ confidence: 'likely',
307
+ title: 'Command Injection via exec()',
308
+ description: 'Passing user input to exec() allows arbitrary command execution.',
309
+ fix: { suggestion: 'Use escapeshellarg() and escapeshellcmd() on all user inputs.' },
310
+ check({ files }) {
311
+ return checkPHP(this, files,
312
+ /\bexec\s*\(.*\$_(?:GET|POST|REQUEST)/,
313
+ );
314
+ },
315
+ },
316
+
317
+ // SEC-PHP-017: Command injection via system()
318
+ {
319
+ id: 'SEC-PHP-017',
320
+ category: 'security',
321
+ severity: 'critical',
322
+ confidence: 'likely',
323
+ title: 'Command Injection via system()',
324
+ description: 'Passing user input to system() allows arbitrary command execution.',
325
+ fix: { suggestion: 'Use escapeshellarg() on all user-provided arguments.' },
326
+ check({ files }) {
327
+ return checkPHP(this, files,
328
+ /\bsystem\s*\(.*\$_(?:GET|POST|REQUEST)/,
329
+ );
330
+ },
331
+ },
332
+
333
+ // SEC-PHP-018: Command injection via shell_exec()
334
+ {
335
+ id: 'SEC-PHP-018',
336
+ category: 'security',
337
+ severity: 'critical',
338
+ confidence: 'likely',
339
+ title: 'Command Injection via shell_exec()',
340
+ description: 'Passing user input to shell_exec() allows arbitrary command execution.',
341
+ fix: { suggestion: 'Use escapeshellarg() and avoid shell_exec with user input.' },
342
+ check({ files }) {
343
+ return checkPHP(this, files,
344
+ /shell_exec\s*\(.*\$_(?:GET|POST|REQUEST)/,
345
+ );
346
+ },
347
+ },
348
+
349
+ // SEC-PHP-019: Command injection via passthru()
350
+ {
351
+ id: 'SEC-PHP-019',
352
+ category: 'security',
353
+ severity: 'critical',
354
+ confidence: 'likely',
355
+ title: 'Command Injection via passthru()',
356
+ description: 'Passing user input to passthru() allows arbitrary command execution.',
357
+ fix: { suggestion: 'Use escapeshellarg() on all user inputs passed to passthru().' },
358
+ check({ files }) {
359
+ return checkPHP(this, files,
360
+ /passthru\s*\(.*\$_(?:GET|POST|REQUEST)/,
361
+ );
362
+ },
363
+ },
364
+
365
+ // SEC-PHP-020: Command injection via backtick operator
366
+ {
367
+ id: 'SEC-PHP-020',
368
+ category: 'security',
369
+ severity: 'critical',
370
+ confidence: 'likely',
371
+ title: 'Command Injection via Backtick Operator',
372
+ description: 'Using backtick operator with user input allows arbitrary command execution.',
373
+ fix: { suggestion: 'Avoid backtick operator; use escapeshellarg() with exec() instead.' },
374
+ check({ files }) {
375
+ return checkPHP(this, files,
376
+ /`[^`]*\$_(?:GET|POST|REQUEST)/,
377
+ );
378
+ },
379
+ },
380
+
381
+ // ===========================================================================
382
+ // File Inclusion (021–025)
383
+ // ===========================================================================
384
+
385
+ // SEC-PHP-021: Local file inclusion via include
386
+ {
387
+ id: 'SEC-PHP-021',
388
+ category: 'security',
389
+ severity: 'critical',
390
+ confidence: 'likely',
391
+ title: 'Local File Inclusion via include()',
392
+ description: 'Using include() with user input allows including arbitrary files.',
393
+ fix: { suggestion: 'Whitelist allowed files and validate against a fixed set of paths.' },
394
+ check({ files }) {
395
+ return checkPHP(this, files,
396
+ /\binclude\s*\(?\s*\$_(?:GET|POST|REQUEST)/,
397
+ );
398
+ },
399
+ },
400
+
401
+ // SEC-PHP-022: Local file inclusion via require
402
+ {
403
+ id: 'SEC-PHP-022',
404
+ category: 'security',
405
+ severity: 'critical',
406
+ confidence: 'likely',
407
+ title: 'Local File Inclusion via require()',
408
+ description: 'Using require() with user input allows including arbitrary files.',
409
+ fix: { suggestion: 'Whitelist allowed files and use a map of safe paths.' },
410
+ check({ files }) {
411
+ return checkPHP(this, files,
412
+ /\brequire\s*\(?\s*\$_(?:GET|POST|REQUEST)/,
413
+ );
414
+ },
415
+ },
416
+
417
+ // SEC-PHP-023: File inclusion via include_once with variable
418
+ {
419
+ id: 'SEC-PHP-023',
420
+ category: 'security',
421
+ severity: 'critical',
422
+ confidence: 'likely',
423
+ title: 'File Inclusion via include_once with Variable',
424
+ description: 'Using include_once with user-controlled variable allows file inclusion.',
425
+ fix: { suggestion: 'Validate file paths against a whitelist before inclusion.' },
426
+ check({ files }) {
427
+ return checkPHP(this, files,
428
+ /include_once\s*\(?\s*\$/,
429
+ );
430
+ },
431
+ },
432
+
433
+ // SEC-PHP-024: Remote file inclusion via allow_url_include
434
+ {
435
+ id: 'SEC-PHP-024',
436
+ category: 'security',
437
+ severity: 'critical',
438
+ confidence: 'likely',
439
+ title: 'Remote File Inclusion Enabled',
440
+ description: 'Setting allow_url_include=On enables remote file inclusion attacks.',
441
+ fix: { suggestion: 'Set allow_url_include=Off in php.ini.' },
442
+ check({ files }) {
443
+ return checkPHP(this, files,
444
+ /allow_url_include\s*=\s*(?:On|1|true)/i,
445
+ );
446
+ },
447
+ },
448
+
449
+ // SEC-PHP-025: File inclusion via require_once with variable
450
+ {
451
+ id: 'SEC-PHP-025',
452
+ category: 'security',
453
+ severity: 'critical',
454
+ confidence: 'likely',
455
+ title: 'File Inclusion via require_once with Variable',
456
+ description: 'Using require_once with user-controlled variable allows file inclusion.',
457
+ fix: { suggestion: 'Validate file paths against a whitelist before inclusion.' },
458
+ check({ files }) {
459
+ return checkPHP(this, files,
460
+ /require_once\s*\(?\s*\$/,
461
+ );
462
+ },
463
+ },
464
+
465
+ // ===========================================================================
466
+ // Deserialization (026–030)
467
+ // ===========================================================================
468
+
469
+ // SEC-PHP-026: Unsafe unserialize
470
+ {
471
+ id: 'SEC-PHP-026',
472
+ category: 'security',
473
+ severity: 'critical',
474
+ confidence: 'likely',
475
+ title: 'Unsafe unserialize() with User Input',
476
+ description: 'Using unserialize() on user input allows object injection and RCE.',
477
+ fix: { suggestion: 'Use json_decode() instead or pass allowed_classes option.' },
478
+ check({ files }) {
479
+ return checkPHP(this, files,
480
+ /unserialize\s*\(\s*\$_(?:GET|POST|REQUEST|COOKIE)/,
481
+ );
482
+ },
483
+ },
484
+
485
+ // SEC-PHP-027: Unsafe unserialize with any variable
486
+ {
487
+ id: 'SEC-PHP-027',
488
+ category: 'security',
489
+ severity: 'high',
490
+ confidence: 'suggestion',
491
+ title: 'Potentially Unsafe unserialize()',
492
+ description: 'Using unserialize() without allowed_classes restriction may enable object injection.',
493
+ fix: { suggestion: 'Pass ["allowed_classes" => false] or use json_decode().' },
494
+ check({ files }) {
495
+ return checkPHP(this, files,
496
+ /unserialize\s*\(\s*\$(?!_(GET|POST|REQUEST|COOKIE))/,
497
+ );
498
+ },
499
+ },
500
+
501
+ // SEC-PHP-028: PHP object injection via __wakeup
502
+ {
503
+ id: 'SEC-PHP-028',
504
+ category: 'security',
505
+ severity: 'high',
506
+ confidence: 'suggestion',
507
+ title: 'PHP Object Injection via __wakeup Magic Method',
508
+ description: 'Classes with __wakeup that perform dangerous operations are gadgets for deserialization attacks.',
509
+ fix: { suggestion: 'Avoid dangerous operations in __wakeup; validate all properties.' },
510
+ check({ files }) {
511
+ return checkPHP(this, files,
512
+ /function\s+__wakeup\s*\(/,
513
+ );
514
+ },
515
+ },
516
+
517
+ // SEC-PHP-029: Unsafe phar deserialization
518
+ {
519
+ id: 'SEC-PHP-029',
520
+ category: 'security',
521
+ severity: 'critical',
522
+ confidence: 'likely',
523
+ title: 'Phar Deserialization Attack',
524
+ description: 'File operations on phar:// streams with user input trigger deserialization.',
525
+ fix: { suggestion: 'Never pass user input to file functions with phar:// protocol.' },
526
+ check({ files }) {
527
+ return checkPHP(this, files,
528
+ /phar:\/\/.*\$_(?:GET|POST|REQUEST)/,
529
+ );
530
+ },
531
+ },
532
+
533
+ // SEC-PHP-030: Unsafe object hydration via __destruct
534
+ {
535
+ id: 'SEC-PHP-030',
536
+ category: 'security',
537
+ severity: 'high',
538
+ confidence: 'suggestion',
539
+ title: 'Dangerous __destruct Magic Method',
540
+ description: 'Classes with __destruct that execute commands or delete files are deserialization gadgets.',
541
+ fix: { suggestion: 'Avoid file/command operations in __destruct; validate state first.' },
542
+ check({ files }) {
543
+ return checkPHP(this, files,
544
+ /function\s+__destruct\s*\(.*(?:exec|system|unlink|file_put_contents)/,
545
+ );
546
+ },
547
+ },
548
+
549
+ // ===========================================================================
550
+ // Eval / Code Execution (031–035)
551
+ // ===========================================================================
552
+
553
+ // SEC-PHP-031: eval() with user input
554
+ {
555
+ id: 'SEC-PHP-031',
556
+ category: 'security',
557
+ severity: 'critical',
558
+ confidence: 'likely',
559
+ title: 'Code Injection via eval()',
560
+ description: 'Using eval() with user-controlled input allows arbitrary code execution.',
561
+ fix: { suggestion: 'Avoid eval() entirely; use safe alternatives.' },
562
+ check({ files }) {
563
+ return checkPHP(this, files,
564
+ /\beval\s*\(.*\$_(?:GET|POST|REQUEST)/,
565
+ );
566
+ },
567
+ },
568
+
569
+ // SEC-PHP-032: eval with any variable
570
+ {
571
+ id: 'SEC-PHP-032',
572
+ category: 'security',
573
+ severity: 'high',
574
+ confidence: 'suggestion',
575
+ title: 'Potentially Unsafe eval() Usage',
576
+ description: 'Using eval() with variables that may contain user input allows RCE.',
577
+ fix: { suggestion: 'Avoid eval() entirely; use a safe interpreter or whitelist approach.' },
578
+ check({ files }) {
579
+ return checkPHP(this, files,
580
+ /\beval\s*\(\s*\$/,
581
+ );
582
+ },
583
+ },
584
+
585
+ // SEC-PHP-033: assert() used as eval
586
+ {
587
+ id: 'SEC-PHP-033',
588
+ category: 'security',
589
+ severity: 'critical',
590
+ confidence: 'likely',
591
+ title: 'Code Execution via assert()',
592
+ description: 'assert() with string arguments evaluates code like eval() in PHP < 8.',
593
+ fix: { suggestion: 'Never pass strings to assert(); use boolean expressions only.' },
594
+ check({ files }) {
595
+ return checkPHP(this, files,
596
+ /\bassert\s*\(\s*["']\$/,
597
+ );
598
+ },
599
+ },
600
+
601
+ // SEC-PHP-034: preg_replace with /e modifier
602
+ {
603
+ id: 'SEC-PHP-034',
604
+ category: 'security',
605
+ severity: 'critical',
606
+ confidence: 'likely',
607
+ title: 'Code Execution via preg_replace /e Modifier',
608
+ description: 'The /e modifier in preg_replace evaluates replacement as PHP code.',
609
+ fix: { suggestion: 'Use preg_replace_callback() instead of the /e modifier.' },
610
+ check({ files }) {
611
+ return checkPHP(this, files,
612
+ /preg_replace\s*\(\s*['"]\/.*\/e['"]/,
613
+ );
614
+ },
615
+ },
616
+
617
+ // SEC-PHP-035: create_function (deprecated eval wrapper)
618
+ {
619
+ id: 'SEC-PHP-035',
620
+ category: 'security',
621
+ severity: 'high',
622
+ confidence: 'likely',
623
+ title: 'Code Execution via create_function()',
624
+ description: 'create_function() uses eval internally and allows code injection.',
625
+ fix: { suggestion: 'Use anonymous functions (closures) instead of create_function().' },
626
+ check({ files }) {
627
+ return checkPHP(this, files,
628
+ /create_function\s*\(/,
629
+ );
630
+ },
631
+ },
632
+
633
+ // ===========================================================================
634
+ // Type Juggling (036–040)
635
+ // ===========================================================================
636
+
637
+ // SEC-PHP-036: Loose comparison with == for authentication
638
+ {
639
+ id: 'SEC-PHP-036',
640
+ category: 'security',
641
+ severity: 'high',
642
+ confidence: 'likely',
643
+ title: 'Type Juggling via Loose Comparison in Auth',
644
+ description: 'Using == for password or token comparison is vulnerable to type juggling.',
645
+ fix: { suggestion: 'Use === (strict comparison) or hash_equals() for security comparisons.' },
646
+ check({ files }) {
647
+ return checkPHP(this, files,
648
+ /(?:password|token|secret|hash)\s*==\s*(?!\=)/,
649
+ );
650
+ },
651
+ },
652
+
653
+ // SEC-PHP-037: Loose comparison with strcmp bypass
654
+ {
655
+ id: 'SEC-PHP-037',
656
+ category: 'security',
657
+ severity: 'high',
658
+ confidence: 'likely',
659
+ title: 'strcmp() Bypass via Type Juggling',
660
+ description: 'strcmp() returns NULL on array input, which loosely equals 0 (success).',
661
+ fix: { suggestion: 'Use hash_equals() or === with strict type checking.' },
662
+ check({ files }) {
663
+ return checkPHP(this, files,
664
+ /strcmp\s*\(.*\$_(?:GET|POST|REQUEST)/,
665
+ );
666
+ },
667
+ },
668
+
669
+ // SEC-PHP-038: Switch statement with loose comparison
670
+ {
671
+ id: 'SEC-PHP-038',
672
+ category: 'security',
673
+ severity: 'medium',
674
+ confidence: 'suggestion',
675
+ title: 'Type Juggling in switch Statement',
676
+ description: 'PHP switch uses loose comparison, which can be exploited via type juggling.',
677
+ fix: { suggestion: 'Use if/elseif with === (strict comparison) instead of switch.' },
678
+ check({ files }) {
679
+ return checkPHP(this, files,
680
+ /switch\s*\(\s*\$_(?:GET|POST|REQUEST)/,
681
+ );
682
+ },
683
+ },
684
+
685
+ // SEC-PHP-039: in_array without strict mode
686
+ {
687
+ id: 'SEC-PHP-039',
688
+ category: 'security',
689
+ severity: 'medium',
690
+ confidence: 'suggestion',
691
+ title: 'Type Juggling in in_array()',
692
+ description: 'in_array() without strict=true uses loose comparison and may bypass checks.',
693
+ fix: { suggestion: 'Pass true as the third argument: in_array($val, $arr, true).' },
694
+ check({ files }) {
695
+ return checkPHP(this, files,
696
+ /in_array\s*\([^)]*\$_(?:GET|POST|REQUEST)[^)]*\)\s*(?!.*true)/,
697
+ );
698
+ },
699
+ },
700
+
701
+ // SEC-PHP-040: array_search without strict mode
702
+ {
703
+ id: 'SEC-PHP-040',
704
+ category: 'security',
705
+ severity: 'medium',
706
+ confidence: 'suggestion',
707
+ title: 'Type Juggling in array_search()',
708
+ description: 'array_search() without strict=true uses loose comparison.',
709
+ fix: { suggestion: 'Pass true as the third argument: array_search($val, $arr, true).' },
710
+ check({ files }) {
711
+ return checkPHP(this, files,
712
+ /array_search\s*\([^)]*\$_(?:GET|POST|REQUEST)/,
713
+ );
714
+ },
715
+ },
716
+
717
+ // ===========================================================================
718
+ // Laravel (041–045)
719
+ // ===========================================================================
720
+
721
+ // SEC-PHP-041: Laravel raw DB query with user input
722
+ {
723
+ id: 'SEC-PHP-041',
724
+ category: 'security',
725
+ severity: 'critical',
726
+ confidence: 'likely',
727
+ title: 'SQL Injection via Laravel DB::raw()',
728
+ description: 'Using DB::raw() with user input bypasses query builder protections.',
729
+ fix: { suggestion: 'Use query builder bindings: DB::raw("... ?", [$value]).' },
730
+ check({ files }) {
731
+ return checkPHP(this, files,
732
+ /DB::raw\s*\(.*\$_(?:GET|POST|REQUEST)/,
733
+ );
734
+ },
735
+ },
736
+
737
+ // SEC-PHP-042: Laravel mass assignment without $fillable
738
+ {
739
+ id: 'SEC-PHP-042',
740
+ category: 'security',
741
+ severity: 'high',
742
+ confidence: 'likely',
743
+ title: 'Laravel Mass Assignment via $guarded = []',
744
+ description: 'Setting $guarded to empty array allows all attributes to be mass-assigned.',
745
+ fix: { suggestion: 'Define $fillable with only allowed attributes instead of empty $guarded.' },
746
+ check({ files }) {
747
+ return checkPHP(this, files,
748
+ /\$guarded\s*=\s*\[\s*\]/,
749
+ );
750
+ },
751
+ },
752
+
753
+ // SEC-PHP-043: Laravel blade {!! !!} unescaped output
754
+ {
755
+ id: 'SEC-PHP-043',
756
+ category: 'security',
757
+ severity: 'high',
758
+ confidence: 'likely',
759
+ title: 'XSS via Laravel Blade Unescaped Output',
760
+ description: 'Using {!! !!} outputs raw HTML without escaping, enabling XSS.',
761
+ fix: { suggestion: 'Use {{ }} for auto-escaped output; sanitize when {!! !!} is needed.' },
762
+ check({ files }) {
763
+ return checkPHP(this, files,
764
+ /\{!!\s*\$(?:request|_GET|_POST)/,
765
+ );
766
+ },
767
+ },
768
+
769
+ // SEC-PHP-044: Laravel debug mode in production
770
+ {
771
+ id: 'SEC-PHP-044',
772
+ category: 'security',
773
+ severity: 'high',
774
+ confidence: 'likely',
775
+ title: 'Laravel Debug Mode Enabled',
776
+ description: 'APP_DEBUG=true in production exposes sensitive information in error pages.',
777
+ fix: { suggestion: 'Set APP_DEBUG=false in production .env files.' },
778
+ check({ files }) {
779
+ return checkPHP(this, files,
780
+ /['"]APP_DEBUG['"]\s*(?:=>|,)\s*true/,
781
+ );
782
+ },
783
+ },
784
+
785
+ // SEC-PHP-045: Laravel whereRaw with user input
786
+ {
787
+ id: 'SEC-PHP-045',
788
+ category: 'security',
789
+ severity: 'critical',
790
+ confidence: 'likely',
791
+ title: 'SQL Injection via Laravel whereRaw()',
792
+ description: 'Using whereRaw() with concatenated user input enables SQL injection.',
793
+ fix: { suggestion: 'Pass bindings as second argument: whereRaw("col = ?", [$value]).' },
794
+ check({ files }) {
795
+ return checkPHP(this, files,
796
+ /whereRaw\s*\(.*\.\s*\$_(?:GET|POST|REQUEST)/,
797
+ );
798
+ },
799
+ },
800
+
801
+ // ===========================================================================
802
+ // WordPress (046–050)
803
+ // ===========================================================================
804
+
805
+ // SEC-PHP-046: WordPress direct SQL query without prepare
806
+ {
807
+ id: 'SEC-PHP-046',
808
+ category: 'security',
809
+ severity: 'critical',
810
+ confidence: 'likely',
811
+ title: 'WordPress SQL Injection via $wpdb->query()',
812
+ description: 'Using $wpdb->query() without $wpdb->prepare() allows SQL injection.',
813
+ fix: { suggestion: 'Use $wpdb->prepare() for all queries with user input.' },
814
+ check({ files }) {
815
+ return checkPHP(this, files,
816
+ /\$wpdb->query\s*\(\s*["'].*\.\s*\$/,
817
+ );
818
+ },
819
+ },
820
+
821
+ // SEC-PHP-047: WordPress missing nonce verification
822
+ {
823
+ id: 'SEC-PHP-047',
824
+ category: 'security',
825
+ severity: 'high',
826
+ confidence: 'suggestion',
827
+ title: 'WordPress Missing Nonce Verification',
828
+ description: 'Processing form data without wp_verify_nonce() is vulnerable to CSRF.',
829
+ fix: { suggestion: 'Add wp_verify_nonce() or check_admin_referer() before processing.' },
830
+ check({ files }) {
831
+ return checkPHP(this, files,
832
+ /\$_POST\[.*\].*(?:update_option|delete_option|add_option)/,
833
+ );
834
+ },
835
+ },
836
+
837
+ // SEC-PHP-048: WordPress unescaped output
838
+ {
839
+ id: 'SEC-PHP-048',
840
+ category: 'security',
841
+ severity: 'high',
842
+ confidence: 'likely',
843
+ title: 'WordPress XSS via Unescaped Output',
844
+ description: 'Echoing user input without esc_html() or esc_attr() enables XSS.',
845
+ fix: { suggestion: 'Use esc_html(), esc_attr(), esc_url() for output escaping.' },
846
+ check({ files }) {
847
+ return checkPHP(this, files,
848
+ /echo\s+\$_(?:GET|POST|REQUEST)(?!.*esc_)/,
849
+ );
850
+ },
851
+ },
852
+
853
+ // SEC-PHP-049: WordPress file upload without validation
854
+ {
855
+ id: 'SEC-PHP-049',
856
+ category: 'security',
857
+ severity: 'high',
858
+ confidence: 'suggestion',
859
+ title: 'WordPress Insecure File Upload',
860
+ description: 'Using move_uploaded_file without proper WordPress upload handling.',
861
+ fix: { suggestion: 'Use wp_handle_upload() with proper mime type checking.' },
862
+ check({ files }) {
863
+ return checkPHP(this, files,
864
+ /move_uploaded_file\s*\(\s*\$_FILES/,
865
+ );
866
+ },
867
+ },
868
+
869
+ // SEC-PHP-050: WordPress register_rest_route without permission_callback
870
+ {
871
+ id: 'SEC-PHP-050',
872
+ category: 'security',
873
+ severity: 'high',
874
+ confidence: 'suggestion',
875
+ title: 'WordPress REST API Without Permission Check',
876
+ description: 'REST routes without permission_callback are publicly accessible.',
877
+ fix: { suggestion: 'Add permission_callback to all register_rest_route() calls.' },
878
+ check({ files }) {
879
+ return checkPHP(this, files,
880
+ /register_rest_route\s*\([^)]*(?!permission_callback)/,
881
+ );
882
+ },
883
+ },
884
+
885
+ // ===========================================================================
886
+ // Session / Upload (051–055)
887
+ // ===========================================================================
888
+
889
+ // SEC-PHP-051: Session fixation via session_id
890
+ {
891
+ id: 'SEC-PHP-051',
892
+ category: 'security',
893
+ severity: 'high',
894
+ confidence: 'likely',
895
+ title: 'Session Fixation via session_id()',
896
+ description: 'Setting session_id from user input allows session fixation attacks.',
897
+ fix: { suggestion: 'Call session_regenerate_id(true) after authentication.' },
898
+ check({ files }) {
899
+ return checkPHP(this, files,
900
+ /session_id\s*\(\s*\$_(?:GET|POST|REQUEST|COOKIE)/,
901
+ );
902
+ },
903
+ },
904
+
905
+ // SEC-PHP-052: File upload without mime validation
906
+ {
907
+ id: 'SEC-PHP-052',
908
+ category: 'security',
909
+ severity: 'high',
910
+ confidence: 'likely',
911
+ title: 'File Upload Without MIME Type Validation',
912
+ description: 'Accepting file uploads without validating MIME types allows malicious uploads.',
913
+ fix: { suggestion: 'Validate file type using finfo_file() and check against an allowlist.' },
914
+ check({ files }) {
915
+ return checkPHP(this, files,
916
+ /move_uploaded_file\s*\(\s*\$_FILES\s*\[/,
917
+ );
918
+ },
919
+ },
920
+
921
+ // SEC-PHP-053: Insecure session configuration
922
+ {
923
+ id: 'SEC-PHP-053',
924
+ category: 'security',
925
+ severity: 'high',
926
+ confidence: 'likely',
927
+ title: 'Insecure Session Cookie Configuration',
928
+ description: 'Session cookies without httponly and secure flags are vulnerable to theft.',
929
+ fix: { suggestion: 'Set session.cookie_httponly=1 and session.cookie_secure=1.' },
930
+ check({ files }) {
931
+ return checkPHP(this, files,
932
+ /session\.cookie_httponly\s*=\s*(?:0|false|off)/i,
933
+ );
934
+ },
935
+ },
936
+
937
+ // SEC-PHP-054: File upload directory in web root
938
+ {
939
+ id: 'SEC-PHP-054',
940
+ category: 'security',
941
+ severity: 'high',
942
+ confidence: 'suggestion',
943
+ title: 'Upload Directory in Web Root Without .htaccess',
944
+ description: 'Uploading files to web-accessible directories allows direct execution.',
945
+ fix: { suggestion: 'Store uploads outside web root or use .htaccess to deny execution.' },
946
+ check({ files }) {
947
+ return checkPHP(this, files,
948
+ /move_uploaded_file\s*\(.*["']uploads?\//,
949
+ );
950
+ },
951
+ },
952
+
953
+ // SEC-PHP-055: Missing session_regenerate_id after login
954
+ {
955
+ id: 'SEC-PHP-055',
956
+ category: 'security',
957
+ severity: 'high',
958
+ confidence: 'suggestion',
959
+ title: 'Missing Session Regeneration After Login',
960
+ description: 'Not calling session_regenerate_id after authentication allows session fixation.',
961
+ fix: { suggestion: 'Call session_regenerate_id(true) immediately after successful login.' },
962
+ check({ files }) {
963
+ return checkPHP(this, files,
964
+ /\$_SESSION\s*\[\s*['"](?:logged_in|user_id|authenticated)['"].*=\s*(?:true|\$)/,
965
+ );
966
+ },
967
+ },
968
+
969
+ // ===========================================================================
970
+ // Encryption (056–060)
971
+ // ===========================================================================
972
+
973
+ // SEC-PHP-056: Use of MD5 for passwords
974
+ {
975
+ id: 'SEC-PHP-056',
976
+ category: 'security',
977
+ severity: 'critical',
978
+ confidence: 'likely',
979
+ title: 'Weak Password Hashing: md5()',
980
+ description: 'Using md5() for password hashing is trivially crackable.',
981
+ fix: { suggestion: 'Use password_hash() with PASSWORD_BCRYPT or PASSWORD_ARGON2ID.' },
982
+ check({ files }) {
983
+ return checkPHP(this, files,
984
+ /md5\s*\(\s*\$(?:_POST|_REQUEST|password|pass)/,
985
+ );
986
+ },
987
+ },
988
+
989
+ // SEC-PHP-057: Use of SHA1 for passwords
990
+ {
991
+ id: 'SEC-PHP-057',
992
+ category: 'security',
993
+ severity: 'high',
994
+ confidence: 'likely',
995
+ title: 'Weak Password Hashing: sha1()',
996
+ description: 'Using sha1() for password hashing is not sufficient for security.',
997
+ fix: { suggestion: 'Use password_hash() with PASSWORD_BCRYPT or PASSWORD_ARGON2ID.' },
998
+ check({ files }) {
999
+ return checkPHP(this, files,
1000
+ /sha1\s*\(\s*\$(?:_POST|_REQUEST|password|pass)/,
1001
+ );
1002
+ },
1003
+ },
1004
+
1005
+ // SEC-PHP-058: Hardcoded encryption key
1006
+ {
1007
+ id: 'SEC-PHP-058',
1008
+ category: 'security',
1009
+ severity: 'critical',
1010
+ confidence: 'likely',
1011
+ title: 'Hardcoded Encryption Key',
1012
+ description: 'Hardcoded encryption keys in source code can be extracted by attackers.',
1013
+ fix: { suggestion: 'Load encryption keys from environment variables or a key management service.' },
1014
+ check({ files }) {
1015
+ return checkPHP(this, files,
1016
+ /(?:encryption_key|secret_key|cipher_key|ENCRYPTION_KEY)\s*=\s*['"][a-zA-Z0-9+\/=]{16,}['"]/,
1017
+ );
1018
+ },
1019
+ },
1020
+
1021
+ // SEC-PHP-059: Weak cipher algorithm (DES)
1022
+ {
1023
+ id: 'SEC-PHP-059',
1024
+ category: 'security',
1025
+ severity: 'high',
1026
+ confidence: 'likely',
1027
+ title: 'Weak Cipher: DES',
1028
+ description: 'DES has a 56-bit key and is easily brute-forced.',
1029
+ fix: { suggestion: 'Use AES-256-GCM or AES-256-CBC via openssl_encrypt().' },
1030
+ check({ files }) {
1031
+ return checkPHP(this, files,
1032
+ /(?:mcrypt_encrypt|openssl_encrypt)\s*\(.*['"](?:des-|DES|MCRYPT_DES)/,
1033
+ );
1034
+ },
1035
+ },
1036
+
1037
+ // SEC-PHP-060: ECB mode usage
1038
+ {
1039
+ id: 'SEC-PHP-060',
1040
+ category: 'security',
1041
+ severity: 'high',
1042
+ confidence: 'likely',
1043
+ title: 'Weak Cipher Mode: ECB',
1044
+ description: 'ECB mode does not provide semantic security and leaks data patterns.',
1045
+ fix: { suggestion: 'Use GCM or CBC mode with HMAC instead of ECB.' },
1046
+ check({ files }) {
1047
+ return checkPHP(this, files,
1048
+ /(?:openssl_encrypt|mcrypt_encrypt)\s*\(.*(?:ECB|ecb)/,
1049
+ );
1050
+ },
1051
+ },
1052
+ ];
1053
+
1054
+ export default rules;