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,507 @@
1
+ function isSourceFile(f) { return ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.py', '.rb', '.go', '.java', '.cs', '.php'].some(e => f.endsWith(e)); }
2
+
3
+ const rules = [
4
+ // === NIST 800-53 / Cybersecurity Framework Rules ===
5
+
6
+ {
7
+ id: 'COMP-NIST-001',
8
+ category: 'compliance',
9
+ severity: 'high',
10
+ confidence: 'likely',
11
+ title: 'No Access Control Policy',
12
+ check({ files }) {
13
+ const findings = [];
14
+ const accessControlPattern = /(?:rbac|role[_\-.]?based|acl|access[_\-.]?control|permission[_\-.]?model|policy[_\-.]?engine|authorize|checkPermission|requireRole|can(?:Access|Read|Write|Delete)|guard|protect)/i;
15
+ const hasAuth = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:endpoint|route|api|handler|controller|app|server)/i.test(c));
16
+ if (!hasAuth) return findings;
17
+ const hasAccessControl = [...files.entries()].some(([f, c]) => isSourceFile(f) && accessControlPattern.test(c));
18
+ if (!hasAccessControl) {
19
+ findings.push({
20
+ ruleId: 'COMP-NIST-001', category: 'compliance', severity: 'high',
21
+ title: 'No RBAC/ACL/permission model detected',
22
+ description: 'NIST 800-53 AC-1 requires an access control policy and procedures. Implement role-based access control (RBAC), access control lists (ACLs), or a permission model to enforce least privilege.',
23
+ fix: null,
24
+ });
25
+ }
26
+ return findings;
27
+ },
28
+ },
29
+
30
+ {
31
+ id: 'COMP-NIST-002',
32
+ category: 'compliance',
33
+ severity: 'high',
34
+ confidence: 'likely',
35
+ title: 'Missing Multi-Factor Authentication',
36
+ check({ files }) {
37
+ const findings = [];
38
+ const authPattern = /(?:login|signin|sign[_\-.]?in|authenticate|auth[_\-.]?handler|passport|auth0|cognito|firebase[_\-.]?auth)/i;
39
+ const mfaPattern = /(?:mfa|multi[_\-.]?factor|two[_\-.]?factor|2fa|totp|otp|authenticator[_\-.]?app|sms[_\-.]?verif|second[_\-.]?factor|u2f|webauthn|fido)/i;
40
+ const hasAuth = [...files.entries()].some(([f, c]) => isSourceFile(f) && authPattern.test(c));
41
+ if (!hasAuth) return findings;
42
+ const hasMfa = [...files.entries()].some(([f, c]) => isSourceFile(f) && mfaPattern.test(c));
43
+ if (!hasMfa) {
44
+ findings.push({
45
+ ruleId: 'COMP-NIST-002', category: 'compliance', severity: 'high',
46
+ title: 'Authentication without multi-factor authentication support',
47
+ description: 'NIST 800-53 IA-2 requires multi-factor authentication for privileged and network access. Implement MFA/2FA using TOTP, WebAuthn, or SMS verification.',
48
+ fix: null,
49
+ });
50
+ }
51
+ return findings;
52
+ },
53
+ },
54
+
55
+ {
56
+ id: 'COMP-NIST-003',
57
+ category: 'compliance',
58
+ severity: 'medium',
59
+ confidence: 'likely',
60
+ title: 'No Continuous Monitoring',
61
+ check({ files }) {
62
+ const findings = [];
63
+ const monitoringPattern = /(?:monitoring|health[_\-.]?check|alerting|prometheus|grafana|datadog|newrelic|cloudwatch|sentry|pagerduty|opsgenie|status[_\-.]?page|uptime|heartbeat)/i;
64
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler)/i.test(c));
65
+ if (!hasApp) return findings;
66
+ const hasMonitoring = [...files.entries()].some(([f, c]) => monitoringPattern.test(c) || f.match(/monitor|health|alert/i));
67
+ if (!hasMonitoring) {
68
+ findings.push({
69
+ ruleId: 'COMP-NIST-003', category: 'compliance', severity: 'medium',
70
+ title: 'No continuous monitoring or health checks detected',
71
+ description: 'NIST 800-53 CA-7 requires continuous monitoring of information systems. Implement health checks, alerting, and monitoring using tools like Prometheus, Datadog, or CloudWatch.',
72
+ fix: null,
73
+ });
74
+ }
75
+ return findings;
76
+ },
77
+ },
78
+
79
+ {
80
+ id: 'COMP-NIST-004',
81
+ category: 'compliance',
82
+ severity: 'medium',
83
+ confidence: 'likely',
84
+ title: 'Missing Incident Response Plan',
85
+ check({ files }) {
86
+ const findings = [];
87
+ const incidentPattern = /(?:incident[_\-.]?response|incident[_\-.]?plan|security[_\-.]?incident|breach[_\-.]?notification|playbook|escalation[_\-.]?procedure|runbook|on[_\-.]?call|incident[_\-.]?management)/i;
88
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler)/i.test(c));
89
+ if (!hasApp) return findings;
90
+ const hasIncident = [...files.entries()].some(([f, c]) => incidentPattern.test(c) || f.match(/incident|playbook|runbook/i));
91
+ if (!hasIncident) {
92
+ findings.push({
93
+ ruleId: 'COMP-NIST-004', category: 'compliance', severity: 'medium',
94
+ title: 'No incident response plan or playbook references',
95
+ description: 'NIST 800-53 IR-1 requires an incident response policy and plan. Document and reference incident response procedures, escalation paths, and playbooks.',
96
+ fix: null,
97
+ });
98
+ }
99
+ return findings;
100
+ },
101
+ },
102
+
103
+ {
104
+ id: 'COMP-NIST-005',
105
+ category: 'compliance',
106
+ severity: 'medium',
107
+ confidence: 'likely',
108
+ title: 'No Configuration Management',
109
+ check({ files }) {
110
+ const findings = [];
111
+ const configMgmtPattern = /(?:config[_\-.]?validat|config[_\-.]?schema|joi|zod|yup|ajv|json[_\-.]?schema|env[_\-.]?validat|config[_\-.]?check|baseline[_\-.]?config|hardening)/i;
112
+ const hasConfig = [...files.entries()].some(([f, c]) =>
113
+ isSourceFile(f) && /(?:config|settings|environment|env)/i.test(f)
114
+ );
115
+ if (!hasConfig) return findings;
116
+ const hasValidation = [...files.entries()].some(([f, c]) => isSourceFile(f) && configMgmtPattern.test(c));
117
+ if (!hasValidation) {
118
+ findings.push({
119
+ ruleId: 'COMP-NIST-005', category: 'compliance', severity: 'medium',
120
+ title: 'Configuration files without validation or schema enforcement',
121
+ description: 'NIST 800-53 CM-1 requires configuration management policies. Validate configuration values using schemas (Joi, Zod, JSON Schema) to prevent misconfigurations.',
122
+ fix: null,
123
+ });
124
+ }
125
+ return findings;
126
+ },
127
+ },
128
+
129
+ {
130
+ id: 'COMP-NIST-006',
131
+ category: 'compliance',
132
+ severity: 'medium',
133
+ confidence: 'likely',
134
+ title: 'Missing System Integrity',
135
+ check({ files }) {
136
+ const findings = [];
137
+ const integrityPattern = /(?:integrity|checksum|hmac|digital[_\-.]?signature|code[_\-.]?signing|hash[_\-.]?verif|subresource[_\-.]?integrity|sri|tamper[_\-.]?detect|file[_\-.]?integrity)/i;
138
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler)/i.test(c));
139
+ if (!hasApp) return findings;
140
+ const hasIntegrity = [...files.entries()].some(([f, c]) => isSourceFile(f) && integrityPattern.test(c));
141
+ if (!hasIntegrity) {
142
+ findings.push({
143
+ ruleId: 'COMP-NIST-006', category: 'compliance', severity: 'medium',
144
+ title: 'No integrity checking mechanisms detected',
145
+ description: 'NIST 800-53 SI-7 requires software and information integrity verification. Implement checksums, HMAC verification, code signing, or subresource integrity (SRI) checks.',
146
+ fix: null,
147
+ });
148
+ }
149
+ return findings;
150
+ },
151
+ },
152
+
153
+ {
154
+ id: 'COMP-NIST-007',
155
+ category: 'compliance',
156
+ severity: 'low',
157
+ confidence: 'likely',
158
+ title: 'No Personnel Security',
159
+ check({ files }) {
160
+ const findings = [];
161
+ const personnelPattern = /(?:offboard|deprovisio|access[_\-.]?review|employee[_\-.]?term|revoke[_\-.]?access|disable[_\-.]?account|personnel[_\-.]?security|background[_\-.]?check|clearance)/i;
162
+ const hasUserMgmt = [...files.entries()].some(([f, c]) =>
163
+ isSourceFile(f) && /(?:user[_\-.]?management|admin|employee|staff|personnel)/i.test(c)
164
+ );
165
+ if (!hasUserMgmt) return findings;
166
+ const hasPersonnel = [...files.entries()].some(([f, c]) => personnelPattern.test(c));
167
+ if (!hasPersonnel) {
168
+ findings.push({
169
+ ruleId: 'COMP-NIST-007', category: 'compliance', severity: 'low',
170
+ title: 'No employee access review or offboarding process',
171
+ description: 'NIST 800-53 PS-4 requires personnel termination procedures including revoking access. Implement access review and offboarding automation for departing employees.',
172
+ fix: null,
173
+ });
174
+ }
175
+ return findings;
176
+ },
177
+ },
178
+
179
+ {
180
+ id: 'COMP-NIST-008',
181
+ category: 'compliance',
182
+ severity: 'medium',
183
+ confidence: 'likely',
184
+ title: 'Missing Media Protection',
185
+ check({ files }) {
186
+ const findings = [];
187
+ const mediaPattern = /(?:disk[_\-.]?encrypt|volume[_\-.]?encrypt|luks|bitlocker|filevault|secure[_\-.]?delet|wipe|shred|degauss|media[_\-.]?sanitiz|data[_\-.]?destruct)/i;
188
+ const infraPattern = /(?:terraform|ansible|cloudformation|kubernetes|docker|infrastructure|deploy)/i;
189
+ const hasInfra = [...files.entries()].some(([f, c]) => infraPattern.test(f) || infraPattern.test(c));
190
+ if (!hasInfra) return findings;
191
+ const hasMediaProtection = [...files.entries()].some(([f, c]) => mediaPattern.test(c));
192
+ if (!hasMediaProtection) {
193
+ findings.push({
194
+ ruleId: 'COMP-NIST-008', category: 'compliance', severity: 'medium',
195
+ title: 'No disk encryption or secure deletion references',
196
+ description: 'NIST 800-53 MP-4/MP-6 requires media protection including encryption of storage media and secure sanitization. Configure disk encryption and implement secure data deletion.',
197
+ fix: null,
198
+ });
199
+ }
200
+ return findings;
201
+ },
202
+ },
203
+
204
+ {
205
+ id: 'COMP-NIST-009',
206
+ category: 'compliance',
207
+ severity: 'low',
208
+ confidence: 'likely',
209
+ title: 'No Physical Security Reference',
210
+ check({ files }) {
211
+ const findings = [];
212
+ const physicalPattern = /(?:physical[_\-.]?access|badge|keycard|biometric[_\-.]?access|datacenter|data[_\-.]?center|server[_\-.]?room|colo(?:cation)?|facility[_\-.]?security|cctv|surveillance)/i;
213
+ const infraPattern = /(?:terraform|ansible|cloudformation|kubernetes|infrastructure)/i;
214
+ const hasInfra = [...files.entries()].some(([f, c]) => infraPattern.test(f) || infraPattern.test(c));
215
+ if (!hasInfra) return findings;
216
+ const hasPhysical = [...files.entries()].some(([f, c]) => physicalPattern.test(c));
217
+ if (!hasPhysical) {
218
+ findings.push({
219
+ ruleId: 'COMP-NIST-009', category: 'compliance', severity: 'low',
220
+ title: 'No physical access controls in infrastructure code',
221
+ description: 'NIST 800-53 PE-1 requires physical security policies. Reference physical access controls, datacenter security, or cloud provider physical security certifications in infrastructure documentation.',
222
+ fix: null,
223
+ });
224
+ }
225
+ return findings;
226
+ },
227
+ },
228
+
229
+ {
230
+ id: 'COMP-NIST-010',
231
+ category: 'compliance',
232
+ severity: 'medium',
233
+ confidence: 'likely',
234
+ title: 'Missing Contingency Planning',
235
+ check({ files }) {
236
+ const findings = [];
237
+ const contingencyPattern = /(?:failover|redundan|disaster[_\-.]?recovery|dr[_\-.]?plan|business[_\-.]?continuity|bcp|backup[_\-.]?strateg|high[_\-.]?availability|ha[_\-.]?cluster|multi[_\-.]?az|multi[_\-.]?region|replica)/i;
238
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler|database)/i.test(c));
239
+ if (!hasApp) return findings;
240
+ const hasContingency = [...files.entries()].some(([f, c]) => contingencyPattern.test(c) || f.match(/disaster|recovery|backup|continuity/i));
241
+ if (!hasContingency) {
242
+ findings.push({
243
+ ruleId: 'COMP-NIST-010', category: 'compliance', severity: 'medium',
244
+ title: 'No failover, redundancy, or disaster recovery references',
245
+ description: 'NIST 800-53 CP-1 requires contingency planning. Implement failover mechanisms, redundancy, and disaster recovery procedures to ensure system availability.',
246
+ fix: null,
247
+ });
248
+ }
249
+ return findings;
250
+ },
251
+ },
252
+
253
+ {
254
+ id: 'COMP-NIST-011',
255
+ category: 'compliance',
256
+ severity: 'medium',
257
+ confidence: 'likely',
258
+ title: 'No Risk Assessment',
259
+ check({ files }) {
260
+ const findings = [];
261
+ const riskPattern = /(?:risk[_\-.]?assessment|threat[_\-.]?model|vulnerability[_\-.]?assessment|security[_\-.]?audit|pentest|penetration[_\-.]?test|risk[_\-.]?analysis|stride|dread|attack[_\-.]?surface)/i;
262
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler)/i.test(c));
263
+ if (!hasApp) return findings;
264
+ const hasRisk = [...files.entries()].some(([f, c]) => riskPattern.test(c) || f.match(/threat|risk[_\-.]?assess/i));
265
+ if (!hasRisk) {
266
+ findings.push({
267
+ ruleId: 'COMP-NIST-011', category: 'compliance', severity: 'medium',
268
+ title: 'No risk assessment or threat model references',
269
+ description: 'NIST 800-53 RA-3 requires risk assessments. Document threat models, conduct vulnerability assessments, and reference risk analysis processes (e.g., STRIDE, DREAD).',
270
+ fix: null,
271
+ });
272
+ }
273
+ return findings;
274
+ },
275
+ },
276
+
277
+ {
278
+ id: 'COMP-NIST-012',
279
+ category: 'compliance',
280
+ severity: 'low',
281
+ confidence: 'likely',
282
+ title: 'Missing Security Training Reference',
283
+ check({ files }) {
284
+ const findings = [];
285
+ const trainingPattern = /(?:security[_\-.]?(?:training|awareness|education)|phishing[_\-.]?(?:training|simulation)|secure[_\-.]?coding[_\-.]?training|awareness[_\-.]?program|training[_\-.]?complet|onboarding[_\-.]?security)/i;
286
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler)/i.test(c));
287
+ if (!hasApp) return findings;
288
+ const hasTraining = [...files.entries()].some(([f, c]) => trainingPattern.test(c));
289
+ if (!hasTraining) {
290
+ findings.push({
291
+ ruleId: 'COMP-NIST-012', category: 'compliance', severity: 'low',
292
+ title: 'No security awareness or training references',
293
+ description: 'NIST 800-53 AT-1 requires security awareness and training policies. Reference security training programs, awareness campaigns, or secure coding guidelines.',
294
+ fix: null,
295
+ });
296
+ }
297
+ return findings;
298
+ },
299
+ },
300
+
301
+ // === ISO 27001 Rules ===
302
+
303
+ {
304
+ id: 'COMP-ISO27001-001',
305
+ category: 'compliance',
306
+ severity: 'medium',
307
+ confidence: 'likely',
308
+ title: 'No Information Security Policy',
309
+ check({ files }) {
310
+ const findings = [];
311
+ const policyPattern = /(?:security[_\-.]?policy|infosec[_\-.]?policy|information[_\-.]?security[_\-.]?policy|isms|iso[_\-.]?27001|security[_\-.]?governance|security[_\-.]?standard)/i;
312
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler)/i.test(c));
313
+ if (!hasApp) return findings;
314
+ const hasPolicy = [...files.entries()].some(([f, c]) => policyPattern.test(c) || f.match(/security[_\-.]?policy/i));
315
+ if (!hasPolicy) {
316
+ findings.push({
317
+ ruleId: 'COMP-ISO27001-001', category: 'compliance', severity: 'medium',
318
+ title: 'No information security policy document reference',
319
+ description: 'ISO 27001 A.5.1 requires an information security policy approved by management. Reference your organization\'s security policy in the codebase or documentation.',
320
+ fix: null,
321
+ });
322
+ }
323
+ return findings;
324
+ },
325
+ },
326
+
327
+ {
328
+ id: 'COMP-ISO27001-002',
329
+ category: 'compliance',
330
+ severity: 'medium',
331
+ confidence: 'likely',
332
+ title: 'Missing Asset Inventory',
333
+ check({ files }) {
334
+ const findings = [];
335
+ const assetPattern = /(?:asset[_\-.]?(?:inventory|register|tracking|catalog|management)|cmdb|configuration[_\-.]?management[_\-.]?database|service[_\-.]?catalog|resource[_\-.]?inventory)/i;
336
+ const hasInfra = [...files.entries()].some(([f, c]) =>
337
+ /(?:terraform|ansible|cloudformation|kubernetes|docker|infrastructure|deploy)/i.test(f) ||
338
+ /(?:terraform|ansible|cloudformation|kubernetes|docker)/i.test(c)
339
+ );
340
+ if (!hasInfra) return findings;
341
+ const hasAsset = [...files.entries()].some(([f, c]) => assetPattern.test(c) || f.match(/asset|inventory|cmdb/i));
342
+ if (!hasAsset) {
343
+ findings.push({
344
+ ruleId: 'COMP-ISO27001-002', category: 'compliance', severity: 'medium',
345
+ title: 'No asset tracking or inventory management detected',
346
+ description: 'ISO 27001 A.8.1 requires identification and inventory of information assets. Maintain an asset inventory or CMDB to track systems, services, and data stores.',
347
+ fix: null,
348
+ });
349
+ }
350
+ return findings;
351
+ },
352
+ },
353
+
354
+ {
355
+ id: 'COMP-ISO27001-003',
356
+ category: 'compliance',
357
+ severity: 'medium',
358
+ confidence: 'likely',
359
+ title: 'No Access Control Review',
360
+ check({ files }) {
361
+ const findings = [];
362
+ const reviewPattern = /(?:access[_\-.]?review|periodic[_\-.]?review|recertif|access[_\-.]?audit|permission[_\-.]?review|entitlement[_\-.]?review|quarterly[_\-.]?review|annual[_\-.]?review|user[_\-.]?access[_\-.]?review)/i;
363
+ const hasAuth = [...files.entries()].some(([f, c]) =>
364
+ isSourceFile(f) && /(?:auth|login|permission|role|user[_\-.]?management)/i.test(c)
365
+ );
366
+ if (!hasAuth) return findings;
367
+ const hasReview = [...files.entries()].some(([f, c]) => reviewPattern.test(c));
368
+ if (!hasReview) {
369
+ findings.push({
370
+ ruleId: 'COMP-ISO27001-003', category: 'compliance', severity: 'medium',
371
+ title: 'No periodic access review process detected',
372
+ description: 'ISO 27001 A.9.2.5 requires regular review of user access rights. Implement periodic access recertification reviews to ensure access remains appropriate.',
373
+ fix: null,
374
+ });
375
+ }
376
+ return findings;
377
+ },
378
+ },
379
+
380
+ {
381
+ id: 'COMP-ISO27001-004',
382
+ category: 'compliance',
383
+ severity: 'medium',
384
+ confidence: 'likely',
385
+ title: 'Missing Cryptographic Controls',
386
+ check({ files }) {
387
+ const findings = [];
388
+ const cryptoPolicyPattern = /(?:crypto(?:graphic)?[_\-.]?policy|key[_\-.]?management|key[_\-.]?rotation|kms|key[_\-.]?vault|certificate[_\-.]?management|pki|key[_\-.]?lifecycle|crypto[_\-.]?standard)/i;
389
+ const hasCrypto = [...files.entries()].some(([f, c]) =>
390
+ isSourceFile(f) && /(?:encrypt|decrypt|cipher|hash|sign|verify|crypto|tls|ssl)/i.test(c)
391
+ );
392
+ if (!hasCrypto) return findings;
393
+ const hasCryptoPolicy = [...files.entries()].some(([f, c]) => cryptoPolicyPattern.test(c));
394
+ if (!hasCryptoPolicy) {
395
+ findings.push({
396
+ ruleId: 'COMP-ISO27001-004', category: 'compliance', severity: 'medium',
397
+ title: 'Cryptographic operations without key management policy',
398
+ description: 'ISO 27001 A.10.1 requires a cryptographic controls policy including key management. Implement key rotation, use KMS/Key Vault, and document cryptographic standards.',
399
+ fix: null,
400
+ });
401
+ }
402
+ return findings;
403
+ },
404
+ },
405
+
406
+ {
407
+ id: 'COMP-ISO27001-005',
408
+ category: 'compliance',
409
+ severity: 'medium',
410
+ confidence: 'likely',
411
+ title: 'No Supplier Security',
412
+ check({ files }) {
413
+ const findings = [];
414
+ const supplierPattern = /(?:vendor[_\-.]?(?:security|assessment|review|audit)|supplier[_\-.]?(?:security|assessment|review)|third[_\-.]?party[_\-.]?(?:security|risk|assessment)|supply[_\-.]?chain[_\-.]?security|sbom|software[_\-.]?bill)/i;
415
+ const hasThirdParty = [...files.entries()].some(([f, c]) =>
416
+ isSourceFile(f) && /(?:vendor|supplier|third[_\-.]?party|external[_\-.]?(?:api|service))/i.test(c)
417
+ );
418
+ if (!hasThirdParty) return findings;
419
+ const hasSupplierSecurity = [...files.entries()].some(([f, c]) => supplierPattern.test(c) || f.match(/vendor[_\-.]?security|sbom/i));
420
+ if (!hasSupplierSecurity) {
421
+ findings.push({
422
+ ruleId: 'COMP-ISO27001-005', category: 'compliance', severity: 'medium',
423
+ title: 'No vendor/supplier security assessment process',
424
+ description: 'ISO 27001 A.15.1 requires information security in supplier relationships. Conduct vendor security assessments, maintain SBOMs, and document supplier security requirements.',
425
+ fix: null,
426
+ });
427
+ }
428
+ return findings;
429
+ },
430
+ },
431
+
432
+ {
433
+ id: 'COMP-ISO27001-006',
434
+ category: 'compliance',
435
+ severity: 'medium',
436
+ confidence: 'likely',
437
+ title: 'Missing Incident Management',
438
+ check({ files }) {
439
+ const findings = [];
440
+ const incidentMgmtPattern = /(?:incident[_\-.]?classif|incident[_\-.]?categor|incident[_\-.]?escalat|severity[_\-.]?level|priority[_\-.]?matrix|triage|incident[_\-.]?workflow|sev[_\-.]?[0-4]|p[0-4][_\-.]?incident)/i;
441
+ const hasIncident = [...files.entries()].some(([f, c]) =>
442
+ isSourceFile(f) && /(?:incident|alert|error[_\-.]?handler|exception)/i.test(c)
443
+ );
444
+ if (!hasIncident) return findings;
445
+ const hasIncidentMgmt = [...files.entries()].some(([f, c]) => incidentMgmtPattern.test(c));
446
+ if (!hasIncidentMgmt) {
447
+ findings.push({
448
+ ruleId: 'COMP-ISO27001-006', category: 'compliance', severity: 'medium',
449
+ title: 'No incident classification or escalation process',
450
+ description: 'ISO 27001 A.16.1 requires incident management procedures including classification and escalation. Define severity levels, triage procedures, and escalation paths.',
451
+ fix: null,
452
+ });
453
+ }
454
+ return findings;
455
+ },
456
+ },
457
+
458
+ {
459
+ id: 'COMP-ISO27001-007',
460
+ category: 'compliance',
461
+ severity: 'medium',
462
+ confidence: 'likely',
463
+ title: 'No Business Continuity',
464
+ check({ files }) {
465
+ const findings = [];
466
+ const bcpPattern = /(?:business[_\-.]?continuity|bcp|continuity[_\-.]?plan|disaster[_\-.]?recovery|dr[_\-.]?plan|failover|high[_\-.]?availability|rpo|rto|recovery[_\-.]?time|recovery[_\-.]?point)/i;
467
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler|database)/i.test(c));
468
+ if (!hasApp) return findings;
469
+ const hasBcp = [...files.entries()].some(([f, c]) => bcpPattern.test(c) || f.match(/continuity|disaster|recovery/i));
470
+ if (!hasBcp) {
471
+ findings.push({
472
+ ruleId: 'COMP-ISO27001-007', category: 'compliance', severity: 'medium',
473
+ title: 'No business continuity plan (BCP) reference',
474
+ description: 'ISO 27001 A.17.1 requires business continuity planning. Document BCP procedures, define RPO/RTO targets, and implement disaster recovery mechanisms.',
475
+ fix: null,
476
+ });
477
+ }
478
+ return findings;
479
+ },
480
+ },
481
+
482
+ {
483
+ id: 'COMP-ISO27001-008',
484
+ category: 'compliance',
485
+ severity: 'medium',
486
+ confidence: 'likely',
487
+ title: 'Missing Compliance Audit',
488
+ check({ files }) {
489
+ const findings = [];
490
+ const auditPattern = /(?:compliance[_\-.]?audit|audit[_\-.]?schedule|internal[_\-.]?audit|external[_\-.]?audit|compliance[_\-.]?review|compliance[_\-.]?check|audit[_\-.]?program|audit[_\-.]?plan|sox[_\-.]?audit|pci[_\-.]?audit|iso[_\-.]?audit)/i;
491
+ const hasApp = [...files.entries()].some(([f, c]) => isSourceFile(f) && /(?:server|app|service|handler)/i.test(c));
492
+ if (!hasApp) return findings;
493
+ const hasAudit = [...files.entries()].some(([f, c]) => auditPattern.test(c) || f.match(/audit[_\-.]?(?:plan|schedule)/i));
494
+ if (!hasAudit) {
495
+ findings.push({
496
+ ruleId: 'COMP-ISO27001-008', category: 'compliance', severity: 'medium',
497
+ title: 'No compliance audit schedule or review process',
498
+ description: 'ISO 27001 A.18.2 requires compliance reviews and internal audits. Establish an audit schedule, conduct periodic compliance reviews, and document findings.',
499
+ fix: null,
500
+ });
501
+ }
502
+ return findings;
503
+ },
504
+ },
505
+ ];
506
+
507
+ export default rules;