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,322 @@
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 educationPattern = /(?:student|grade|transcript|enrollment|gpa|course|classroom|school|university|campus|academic|diploma|semester)/i;
4
+ const studentDataPattern = /(?:student[_\-.]?(?:id|name|record|data|info|email|grade|score|gpa|address)|grade[_\-.]?(?:book|report|point|average)|transcript|enrollment|academic[_\-.]?record)/i;
5
+
6
+ function hasEducationContext(files) {
7
+ return [...files.entries()].some(([f, c]) => isSourceFile(f) && educationPattern.test(c));
8
+ }
9
+
10
+ const rules = [
11
+ {
12
+ id: 'COMP-FERPA-001',
13
+ category: 'compliance',
14
+ severity: 'high',
15
+ confidence: 'likely',
16
+ title: 'Student Records Without Access Controls',
17
+ check({ files }) {
18
+ const findings = [];
19
+ if (!hasEducationContext(files)) return findings;
20
+ for (const [filepath, content] of files.entries()) {
21
+ if (!isSourceFile(filepath)) continue;
22
+ if (!studentDataPattern.test(content)) continue;
23
+ const lines = content.split('\n');
24
+ const hasAuth = /(?:auth|authenticate|authorize|requireAuth|isAuthenticated|checkPermission|middleware.*auth|@login_required|@requires_auth|protect|guard)/i.test(content);
25
+ for (let i = 0; i < lines.length; i++) {
26
+ if (studentDataPattern.test(lines[i]) && /(?:get|fetch|find|query|select|read|endpoint|route|api|handler)/i.test(lines[i])) {
27
+ if (!hasAuth) {
28
+ findings.push({
29
+ ruleId: 'COMP-FERPA-001', category: 'compliance', severity: 'high',
30
+ title: 'Student records endpoint without access controls',
31
+ description: 'FERPA requires that access to student education records be restricted to authorized individuals. Ensure authentication and authorization are enforced on all student data endpoints.',
32
+ file: filepath, line: i + 1, fix: null,
33
+ });
34
+ }
35
+ }
36
+ }
37
+ }
38
+ return findings;
39
+ },
40
+ },
41
+
42
+ {
43
+ id: 'COMP-FERPA-002',
44
+ category: 'compliance',
45
+ severity: 'high',
46
+ confidence: 'likely',
47
+ title: 'Education Records Shared Without Consent',
48
+ check({ files }) {
49
+ const findings = [];
50
+ if (!hasEducationContext(files)) return findings;
51
+ const thirdPartyPattern = /(?:sendgrid|twilio|mailchimp|slack|webhook|third[_\-]?party|external[_\-]?api|partner[_\-]?api|vendor|analytics\.track|mixpanel|segment|amplitude)/i;
52
+ const consentPattern = /(?:consent|ferpa[_\-]?consent|parental[_\-]?consent|disclosure[_\-]?agreement|authorized[_\-]?disclosure|opt[_\-]?in)/i;
53
+ for (const [filepath, content] of files.entries()) {
54
+ if (!isSourceFile(filepath)) continue;
55
+ if (!studentDataPattern.test(content)) continue;
56
+ const lines = content.split('\n');
57
+ for (let i = 0; i < lines.length; i++) {
58
+ if (thirdPartyPattern.test(lines[i]) && studentDataPattern.test(content)) {
59
+ const contextBlock = lines.slice(Math.max(0, i - 15), Math.min(lines.length, i + 15)).join('\n');
60
+ if (!consentPattern.test(contextBlock)) {
61
+ findings.push({
62
+ ruleId: 'COMP-FERPA-002', category: 'compliance', severity: 'high',
63
+ title: 'Student data shared with third-party without consent verification',
64
+ description: 'FERPA prohibits disclosure of education records to third parties without prior written consent from the student or parent. Verify consent before sharing student data externally.',
65
+ file: filepath, line: i + 1, fix: null,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ }
71
+ return findings;
72
+ },
73
+ },
74
+
75
+ {
76
+ id: 'COMP-FERPA-003',
77
+ category: 'compliance',
78
+ severity: 'medium',
79
+ confidence: 'likely',
80
+ title: 'Directory Information Without Opt-Out',
81
+ check({ files }) {
82
+ const findings = [];
83
+ if (!hasEducationContext(files)) return findings;
84
+ const directoryInfoPattern = /(?:directory[_\-]?info|student[_\-]?(?:name|address|phone|email|photo|dob|birth)|public[_\-]?(?:directory|listing|profile))/i;
85
+ const optOutPattern = /(?:opt[_\-]?out|suppress|exclude|do[_\-]?not[_\-]?share|withhold|directory[_\-]?restriction)/i;
86
+ for (const [filepath, content] of files.entries()) {
87
+ if (!isSourceFile(filepath)) continue;
88
+ if (!directoryInfoPattern.test(content)) continue;
89
+ if (!educationPattern.test(content)) continue;
90
+ const lines = content.split('\n');
91
+ for (let i = 0; i < lines.length; i++) {
92
+ if (directoryInfoPattern.test(lines[i]) && /(?:public|expose|share|display|list|show)/i.test(lines[i])) {
93
+ if (!optOutPattern.test(content)) {
94
+ findings.push({
95
+ ruleId: 'COMP-FERPA-003', category: 'compliance', severity: 'medium',
96
+ title: 'Student directory information exposed without opt-out mechanism',
97
+ description: 'FERPA allows institutions to disclose directory information only if students have been given the opportunity to opt out. Implement an opt-out mechanism before exposing directory data.',
98
+ file: filepath, line: i + 1, fix: null,
99
+ });
100
+ }
101
+ }
102
+ }
103
+ }
104
+ return findings;
105
+ },
106
+ },
107
+
108
+ {
109
+ id: 'COMP-FERPA-004',
110
+ category: 'compliance',
111
+ severity: 'medium',
112
+ confidence: 'likely',
113
+ title: 'No Parental Access Mechanism',
114
+ check({ files }) {
115
+ const findings = [];
116
+ if (!hasEducationContext(files)) return findings;
117
+ const parentAccessPattern = /(?:parent[_\-]?(?:access|portal|login|view|dashboard)|guardian[_\-]?(?:access|portal|login)|family[_\-]?(?:access|portal)|parental[_\-]?(?:rights|access))/i;
118
+ const hasParentAccess = [...files.entries()].some(([f, c]) => isSourceFile(f) && parentAccessPattern.test(c));
119
+ if (!hasParentAccess) {
120
+ const hasStudentRecords = [...files.entries()].some(([f, c]) =>
121
+ isSourceFile(f) && studentDataPattern.test(c) && /(?:endpoint|route|api|handler|controller)/i.test(c)
122
+ );
123
+ if (hasStudentRecords) {
124
+ findings.push({
125
+ ruleId: 'COMP-FERPA-004', category: 'compliance', severity: 'medium',
126
+ title: 'No parental access mechanism for student records',
127
+ description: 'FERPA grants parents the right to inspect and review their child\'s education records. Provide a mechanism for parents/guardians to access student records.',
128
+ fix: null,
129
+ });
130
+ }
131
+ }
132
+ return findings;
133
+ },
134
+ },
135
+
136
+ {
137
+ id: 'COMP-FERPA-005',
138
+ category: 'compliance',
139
+ severity: 'high',
140
+ confidence: 'likely',
141
+ title: 'Student Data in Analytics Without De-identification',
142
+ check({ files }) {
143
+ const findings = [];
144
+ if (!hasEducationContext(files)) return findings;
145
+ const analyticsPattern = /(?:analytics|tracking|telemetry|metrics|mixpanel|segment|amplitude|google[_\-]?analytics|gtag|datadog|newrelic)/i;
146
+ const deidentifyPattern = /(?:de[_\-]?identify|anonymize|pseudonymize|hash|redact|mask|strip[_\-]?pii|remove[_\-]?pii|aggregate)/i;
147
+ for (const [filepath, content] of files.entries()) {
148
+ if (!isSourceFile(filepath)) continue;
149
+ if (!analyticsPattern.test(content)) continue;
150
+ const lines = content.split('\n');
151
+ for (let i = 0; i < lines.length; i++) {
152
+ if (analyticsPattern.test(lines[i]) && studentDataPattern.test(content)) {
153
+ const contextBlock = lines.slice(Math.max(0, i - 10), Math.min(lines.length, i + 10)).join('\n');
154
+ if (studentDataPattern.test(contextBlock) && !deidentifyPattern.test(contextBlock)) {
155
+ findings.push({
156
+ ruleId: 'COMP-FERPA-005', category: 'compliance', severity: 'high',
157
+ title: 'Student PII sent to analytics without de-identification',
158
+ description: 'FERPA requires that student data used for analytics or research be de-identified. Remove or anonymize student PII before sending to analytics services.',
159
+ file: filepath, line: i + 1, fix: null,
160
+ });
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return findings;
166
+ },
167
+ },
168
+
169
+ {
170
+ id: 'COMP-FERPA-006',
171
+ category: 'compliance',
172
+ severity: 'high',
173
+ confidence: 'likely',
174
+ title: 'Education Records Without Audit Trail',
175
+ check({ files }) {
176
+ const findings = [];
177
+ if (!hasEducationContext(files)) return findings;
178
+ const auditPattern = /(?:audit[_\-]?(?:log|trail|record|entry)|access[_\-]?log|log[_\-]?access|track[_\-]?access|record[_\-]?access|logAccess|auditLog)/i;
179
+ const hasAudit = [...files.entries()].some(([f, c]) => isSourceFile(f) && auditPattern.test(c));
180
+ if (!hasAudit) {
181
+ for (const [filepath, content] of files.entries()) {
182
+ if (!isSourceFile(filepath)) continue;
183
+ if (!studentDataPattern.test(content)) continue;
184
+ if (/(?:get|read|update|delete|modify|access)/i.test(content) && studentDataPattern.test(content)) {
185
+ findings.push({
186
+ ruleId: 'COMP-FERPA-006', category: 'compliance', severity: 'high',
187
+ title: 'Student record access without audit trail',
188
+ description: 'FERPA requires institutions to maintain a record of each request for access to and disclosure of education records. Implement audit logging for all student record access.',
189
+ file: filepath, fix: null,
190
+ });
191
+ break;
192
+ }
193
+ }
194
+ }
195
+ return findings;
196
+ },
197
+ },
198
+
199
+ {
200
+ id: 'COMP-FERPA-007',
201
+ category: 'compliance',
202
+ severity: 'high',
203
+ confidence: 'likely',
204
+ title: 'Third-Party Access Without Agreement',
205
+ check({ files }) {
206
+ const findings = [];
207
+ if (!hasEducationContext(files)) return findings;
208
+ const thirdPartyPattern = /(?:vendor|partner|contractor|third[_\-]?party|external[_\-]?(?:service|api|provider)|outsource)/i;
209
+ const agreementPattern = /(?:agreement|contract|dpa|data[_\-]?processing|mou|memorandum|terms[_\-]?of[_\-]?service|ferpa[_\-]?compliant|school[_\-]?official)/i;
210
+ for (const [filepath, content] of files.entries()) {
211
+ if (!isSourceFile(filepath)) continue;
212
+ if (!studentDataPattern.test(content)) continue;
213
+ const lines = content.split('\n');
214
+ for (let i = 0; i < lines.length; i++) {
215
+ if (thirdPartyPattern.test(lines[i]) && studentDataPattern.test(content)) {
216
+ const contextBlock = lines.slice(Math.max(0, i - 10), Math.min(lines.length, i + 10)).join('\n');
217
+ if (!agreementPattern.test(contextBlock) && !agreementPattern.test(content)) {
218
+ findings.push({
219
+ ruleId: 'COMP-FERPA-007', category: 'compliance', severity: 'high',
220
+ title: 'Student data shared with vendor without agreement reference',
221
+ description: 'FERPA requires written agreements with third parties accessing student records, defining the purpose, data scope, and security requirements. Reference a data processing agreement in vendor integrations.',
222
+ file: filepath, line: i + 1, fix: null,
223
+ });
224
+ }
225
+ }
226
+ }
227
+ }
228
+ return findings;
229
+ },
230
+ },
231
+
232
+ {
233
+ id: 'COMP-FERPA-008',
234
+ category: 'compliance',
235
+ severity: 'medium',
236
+ confidence: 'likely',
237
+ title: 'Student Data Retained Beyond Enrollment',
238
+ check({ files }) {
239
+ const findings = [];
240
+ if (!hasEducationContext(files)) return findings;
241
+ const retentionPattern = /(?:retention|cleanup|purge|archive|expir|ttl|delete[_\-]?old|data[_\-]?lifecycle|auto[_\-]?delete|scheduled[_\-]?deletion)/i;
242
+ const hasRetention = [...files.entries()].some(([f, c]) => isSourceFile(f) && retentionPattern.test(c) && studentDataPattern.test(c));
243
+ if (!hasRetention) {
244
+ const hasStudentStorage = [...files.entries()].some(([f, c]) =>
245
+ isSourceFile(f) && studentDataPattern.test(c) && /(?:save|store|insert|create|persist|database|collection|table)/i.test(c)
246
+ );
247
+ if (hasStudentStorage) {
248
+ findings.push({
249
+ ruleId: 'COMP-FERPA-008', category: 'compliance', severity: 'medium',
250
+ title: 'No retention/cleanup policy for student data',
251
+ description: 'FERPA best practices require institutions to define retention periods for student data and securely dispose of records no longer needed. Implement a data retention and cleanup policy.',
252
+ fix: null,
253
+ });
254
+ }
255
+ }
256
+ return findings;
257
+ },
258
+ },
259
+
260
+ {
261
+ id: 'COMP-FERPA-009',
262
+ category: 'compliance',
263
+ severity: 'high',
264
+ confidence: 'likely',
265
+ title: 'Grade/Transcript Data Without Encryption',
266
+ check({ files }) {
267
+ const findings = [];
268
+ if (!hasEducationContext(files)) return findings;
269
+ const academicDataPattern = /(?:grade|transcript|gpa|academic[_\-]?record|report[_\-]?card|score|assessment[_\-]?result)/i;
270
+ const encryptionPattern = /(?:encrypt|cipher|aes|crypto|tls|ssl|https|bcrypt|argon|scrypt|hash(?:ed)?|kms|vault)/i;
271
+ for (const [filepath, content] of files.entries()) {
272
+ if (!isSourceFile(filepath)) continue;
273
+ if (!academicDataPattern.test(content)) continue;
274
+ const lines = content.split('\n');
275
+ for (let i = 0; i < lines.length; i++) {
276
+ if (academicDataPattern.test(lines[i]) && /(?:store|save|send|transmit|write|insert|post|put)/i.test(lines[i])) {
277
+ const contextBlock = lines.slice(Math.max(0, i - 10), Math.min(lines.length, i + 10)).join('\n');
278
+ if (!encryptionPattern.test(contextBlock) && !encryptionPattern.test(content)) {
279
+ findings.push({
280
+ ruleId: 'COMP-FERPA-009', category: 'compliance', severity: 'high',
281
+ title: 'Academic records stored/transmitted without encryption',
282
+ description: 'FERPA requires reasonable safeguards for education records. Grade and transcript data should be encrypted both at rest and in transit to prevent unauthorized access.',
283
+ file: filepath, line: i + 1, fix: null,
284
+ });
285
+ }
286
+ }
287
+ }
288
+ }
289
+ return findings;
290
+ },
291
+ },
292
+
293
+ {
294
+ id: 'COMP-FERPA-010',
295
+ category: 'compliance',
296
+ severity: 'medium',
297
+ confidence: 'likely',
298
+ title: 'Missing FERPA Notification Reference',
299
+ check({ files }) {
300
+ const findings = [];
301
+ if (!hasEducationContext(files)) return findings;
302
+ const notificationPattern = /(?:ferpa[_\-]?noti(?:ce|fication)|annual[_\-]?noti(?:ce|fication)|rights[_\-]?noti(?:ce|fication)|ferpa[_\-]?disclosure|family[_\-]?educational[_\-]?rights)/i;
303
+ const hasNotification = [...files.entries()].some(([f, c]) => notificationPattern.test(c));
304
+ if (!hasNotification) {
305
+ const hasStudentApp = [...files.entries()].some(([f, c]) =>
306
+ isSourceFile(f) && studentDataPattern.test(c) && /(?:endpoint|route|api|handler|controller|app|server)/i.test(c)
307
+ );
308
+ if (hasStudentApp) {
309
+ findings.push({
310
+ ruleId: 'COMP-FERPA-010', category: 'compliance', severity: 'medium',
311
+ title: 'Education application without FERPA notification reference',
312
+ description: 'FERPA requires institutions to provide annual notification to students and parents regarding their rights under FERPA. Include a reference to FERPA rights notification in your education application.',
313
+ fix: null,
314
+ });
315
+ }
316
+ }
317
+ return findings;
318
+ },
319
+ },
320
+ ];
321
+
322
+ export default rules;