@nahisaho/musubix-security 1.8.0 → 1.8.5

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 (186) hide show
  1. package/README.md +27 -0
  2. package/dist/analyzers/ai/index.d.ts +6 -0
  3. package/dist/analyzers/ai/index.d.ts.map +1 -0
  4. package/dist/analyzers/ai/index.js +6 -0
  5. package/dist/analyzers/ai/index.js.map +1 -0
  6. package/dist/analyzers/ai/prompt-injection-detector.d.ts +152 -0
  7. package/dist/analyzers/ai/prompt-injection-detector.d.ts.map +1 -0
  8. package/dist/analyzers/ai/prompt-injection-detector.js +468 -0
  9. package/dist/analyzers/ai/prompt-injection-detector.js.map +1 -0
  10. package/dist/analyzers/api/api-security-analyzer.d.ts +263 -0
  11. package/dist/analyzers/api/api-security-analyzer.d.ts.map +1 -0
  12. package/dist/analyzers/api/api-security-analyzer.js +581 -0
  13. package/dist/analyzers/api/api-security-analyzer.js.map +1 -0
  14. package/dist/analyzers/compliance/compliance-checker.d.ts +201 -0
  15. package/dist/analyzers/compliance/compliance-checker.d.ts.map +1 -0
  16. package/dist/analyzers/compliance/compliance-checker.js +772 -0
  17. package/dist/analyzers/compliance/compliance-checker.js.map +1 -0
  18. package/dist/analyzers/container/image-scanner.d.ts +163 -0
  19. package/dist/analyzers/container/image-scanner.d.ts.map +1 -0
  20. package/dist/analyzers/container/image-scanner.js +459 -0
  21. package/dist/analyzers/container/image-scanner.js.map +1 -0
  22. package/dist/analyzers/container/index.d.ts +6 -0
  23. package/dist/analyzers/container/index.d.ts.map +1 -0
  24. package/dist/analyzers/container/index.js +6 -0
  25. package/dist/analyzers/container/index.js.map +1 -0
  26. package/dist/analyzers/dashboard/security-dashboard.d.ts +286 -0
  27. package/dist/analyzers/dashboard/security-dashboard.d.ts.map +1 -0
  28. package/dist/analyzers/dashboard/security-dashboard.js +796 -0
  29. package/dist/analyzers/dashboard/security-dashboard.js.map +1 -0
  30. package/dist/analyzers/iac/iac-checker.d.ts +124 -0
  31. package/dist/analyzers/iac/iac-checker.d.ts.map +1 -0
  32. package/dist/analyzers/iac/iac-checker.js +755 -0
  33. package/dist/analyzers/iac/iac-checker.js.map +1 -0
  34. package/dist/analyzers/iac/index.d.ts +6 -0
  35. package/dist/analyzers/iac/index.d.ts.map +1 -0
  36. package/dist/analyzers/iac/index.js +6 -0
  37. package/dist/analyzers/iac/index.js.map +1 -0
  38. package/dist/analyzers/index.d.ts +9 -0
  39. package/dist/analyzers/index.d.ts.map +1 -0
  40. package/dist/analyzers/index.js +13 -0
  41. package/dist/analyzers/index.js.map +1 -0
  42. package/dist/analyzers/monitor/realtime-monitor.d.ts +216 -0
  43. package/dist/analyzers/monitor/realtime-monitor.d.ts.map +1 -0
  44. package/dist/analyzers/monitor/realtime-monitor.js +601 -0
  45. package/dist/analyzers/monitor/realtime-monitor.js.map +1 -0
  46. package/dist/analyzers/sast/index.d.ts +7 -0
  47. package/dist/analyzers/sast/index.d.ts.map +1 -0
  48. package/dist/analyzers/sast/index.js +7 -0
  49. package/dist/analyzers/sast/index.js.map +1 -0
  50. package/dist/analyzers/sast/interprocedural-analyzer.d.ts +276 -0
  51. package/dist/analyzers/sast/interprocedural-analyzer.d.ts.map +1 -0
  52. package/dist/analyzers/sast/interprocedural-analyzer.js +635 -0
  53. package/dist/analyzers/sast/interprocedural-analyzer.js.map +1 -0
  54. package/dist/analyzers/sast/zero-day-detector.d.ts +183 -0
  55. package/dist/analyzers/sast/zero-day-detector.d.ts.map +1 -0
  56. package/dist/analyzers/sast/zero-day-detector.js +593 -0
  57. package/dist/analyzers/sast/zero-day-detector.js.map +1 -0
  58. package/dist/analyzers/sca/dependency-scanner.d.ts +275 -0
  59. package/dist/analyzers/sca/dependency-scanner.d.ts.map +1 -0
  60. package/dist/analyzers/sca/dependency-scanner.js +642 -0
  61. package/dist/analyzers/sca/dependency-scanner.js.map +1 -0
  62. package/dist/core/index.d.ts +8 -0
  63. package/dist/core/index.d.ts.map +1 -0
  64. package/dist/core/index.js +10 -0
  65. package/dist/core/index.js.map +1 -0
  66. package/dist/core/pipeline-manager.d.ts +105 -0
  67. package/dist/core/pipeline-manager.d.ts.map +1 -0
  68. package/dist/core/pipeline-manager.js +449 -0
  69. package/dist/core/pipeline-manager.js.map +1 -0
  70. package/dist/core/result-aggregator.d.ts +96 -0
  71. package/dist/core/result-aggregator.d.ts.map +1 -0
  72. package/dist/core/result-aggregator.js +462 -0
  73. package/dist/core/result-aggregator.js.map +1 -0
  74. package/dist/index.d.ts +15 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +68 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/integrations/ci-integration.d.ts +227 -0
  79. package/dist/integrations/ci-integration.d.ts.map +1 -0
  80. package/dist/integrations/ci-integration.js +472 -0
  81. package/dist/integrations/ci-integration.js.map +1 -0
  82. package/dist/integrations/git-hooks.d.ts +155 -0
  83. package/dist/integrations/git-hooks.d.ts.map +1 -0
  84. package/dist/integrations/git-hooks.js +425 -0
  85. package/dist/integrations/git-hooks.js.map +1 -0
  86. package/dist/integrations/index.d.ts +9 -0
  87. package/dist/integrations/index.d.ts.map +1 -0
  88. package/dist/integrations/index.js +9 -0
  89. package/dist/integrations/index.js.map +1 -0
  90. package/dist/integrations/report-aggregator.d.ts +250 -0
  91. package/dist/integrations/report-aggregator.d.ts.map +1 -0
  92. package/dist/integrations/report-aggregator.js +488 -0
  93. package/dist/integrations/report-aggregator.js.map +1 -0
  94. package/dist/integrations/vscode-integration.d.ts +245 -0
  95. package/dist/integrations/vscode-integration.d.ts.map +1 -0
  96. package/dist/integrations/vscode-integration.js +449 -0
  97. package/dist/integrations/vscode-integration.js.map +1 -0
  98. package/dist/intelligence/attack-pattern-matcher.d.ts +217 -0
  99. package/dist/intelligence/attack-pattern-matcher.d.ts.map +1 -0
  100. package/dist/intelligence/attack-pattern-matcher.js +887 -0
  101. package/dist/intelligence/attack-pattern-matcher.js.map +1 -0
  102. package/dist/intelligence/index.d.ts +12 -0
  103. package/dist/intelligence/index.d.ts.map +1 -0
  104. package/dist/intelligence/index.js +18 -0
  105. package/dist/intelligence/index.js.map +1 -0
  106. package/dist/intelligence/neuro-symbolic-core.d.ts +88 -0
  107. package/dist/intelligence/neuro-symbolic-core.d.ts.map +1 -0
  108. package/dist/intelligence/neuro-symbolic-core.js +403 -0
  109. package/dist/intelligence/neuro-symbolic-core.js.map +1 -0
  110. package/dist/intelligence/predictive-analyzer.d.ts +317 -0
  111. package/dist/intelligence/predictive-analyzer.d.ts.map +1 -0
  112. package/dist/intelligence/predictive-analyzer.js +714 -0
  113. package/dist/intelligence/predictive-analyzer.js.map +1 -0
  114. package/dist/intelligence/risk-scorer.d.ts +333 -0
  115. package/dist/intelligence/risk-scorer.d.ts.map +1 -0
  116. package/dist/intelligence/risk-scorer.js +824 -0
  117. package/dist/intelligence/risk-scorer.js.map +1 -0
  118. package/dist/intelligence/security-analytics.d.ts +349 -0
  119. package/dist/intelligence/security-analytics.d.ts.map +1 -0
  120. package/dist/intelligence/security-analytics.js +813 -0
  121. package/dist/intelligence/security-analytics.js.map +1 -0
  122. package/dist/intelligence/threat-intelligence.d.ts +288 -0
  123. package/dist/intelligence/threat-intelligence.d.ts.map +1 -0
  124. package/dist/intelligence/threat-intelligence.js +639 -0
  125. package/dist/intelligence/threat-intelligence.js.map +1 -0
  126. package/dist/policy/index.d.ts +6 -0
  127. package/dist/policy/index.d.ts.map +1 -0
  128. package/dist/policy/index.js +6 -0
  129. package/dist/policy/index.js.map +1 -0
  130. package/dist/policy/policy-engine.d.ts +254 -0
  131. package/dist/policy/policy-engine.d.ts.map +1 -0
  132. package/dist/policy/policy-engine.js +651 -0
  133. package/dist/policy/policy-engine.js.map +1 -0
  134. package/dist/remediation/auto-fixer.d.ts +179 -0
  135. package/dist/remediation/auto-fixer.d.ts.map +1 -0
  136. package/dist/remediation/auto-fixer.js +540 -0
  137. package/dist/remediation/auto-fixer.js.map +1 -0
  138. package/dist/remediation/fix-validator.d.ts +195 -0
  139. package/dist/remediation/fix-validator.d.ts.map +1 -0
  140. package/dist/remediation/fix-validator.js +462 -0
  141. package/dist/remediation/fix-validator.js.map +1 -0
  142. package/dist/remediation/index.d.ts +10 -0
  143. package/dist/remediation/index.d.ts.map +1 -0
  144. package/dist/remediation/index.js +15 -0
  145. package/dist/remediation/index.js.map +1 -0
  146. package/dist/remediation/patch-generator.d.ts +203 -0
  147. package/dist/remediation/patch-generator.d.ts.map +1 -0
  148. package/dist/remediation/patch-generator.js +533 -0
  149. package/dist/remediation/patch-generator.js.map +1 -0
  150. package/dist/remediation/remediation-planner.d.ts +262 -0
  151. package/dist/remediation/remediation-planner.d.ts.map +1 -0
  152. package/dist/remediation/remediation-planner.js +531 -0
  153. package/dist/remediation/remediation-planner.js.map +1 -0
  154. package/dist/remediation/secure-code-transformer.d.ts +222 -0
  155. package/dist/remediation/secure-code-transformer.d.ts.map +1 -0
  156. package/dist/remediation/secure-code-transformer.js +625 -0
  157. package/dist/remediation/secure-code-transformer.js.map +1 -0
  158. package/dist/types/fix.d.ts +3 -1
  159. package/dist/types/fix.d.ts.map +1 -1
  160. package/dist/types/index.d.ts +6 -0
  161. package/dist/types/index.d.ts.map +1 -1
  162. package/dist/types/index.js +1 -0
  163. package/dist/types/index.js.map +1 -1
  164. package/dist/types/interprocedural.d.ts +203 -0
  165. package/dist/types/interprocedural.d.ts.map +1 -0
  166. package/dist/types/interprocedural.js +7 -0
  167. package/dist/types/interprocedural.js.map +1 -0
  168. package/dist/types/neuro-symbolic.d.ts +179 -0
  169. package/dist/types/neuro-symbolic.d.ts.map +1 -0
  170. package/dist/types/neuro-symbolic.js +7 -0
  171. package/dist/types/neuro-symbolic.js.map +1 -0
  172. package/dist/types/pipeline.d.ts +173 -0
  173. package/dist/types/pipeline.d.ts.map +1 -0
  174. package/dist/types/pipeline.js +7 -0
  175. package/dist/types/pipeline.js.map +1 -0
  176. package/dist/types/result.d.ts +134 -0
  177. package/dist/types/result.d.ts.map +1 -0
  178. package/dist/types/result.js +25 -0
  179. package/dist/types/result.js.map +1 -0
  180. package/dist/types/vulnerability.d.ts +2 -2
  181. package/dist/types/vulnerability.d.ts.map +1 -1
  182. package/dist/types/zero-day.d.ts +146 -0
  183. package/dist/types/zero-day.d.ts.map +1 -0
  184. package/dist/types/zero-day.js +7 -0
  185. package/dist/types/zero-day.js.map +1 -0
  186. package/package.json +2 -2
@@ -0,0 +1,755 @@
1
+ /**
2
+ * @fileoverview Infrastructure as Code (IaC) Checker
3
+ * @module @nahisaho/musubix-security/analyzers/iac/iac-checker
4
+ * @trace DES-SEC2-IAC-001, REQ-SEC2-IAC-001
5
+ */
6
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ /**
9
+ * Built-in Terraform rules
10
+ */
11
+ const TERRAFORM_RULES = [
12
+ {
13
+ id: 'TF-001',
14
+ name: 'AWS S3 Bucket Public Access',
15
+ description: 'S3 bucket should not allow public access',
16
+ severity: 'critical',
17
+ fileTypes: ['terraform'],
18
+ cwes: ['CWE-284'],
19
+ references: ['https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html'],
20
+ remediation: 'Add block_public_acls = true, block_public_policy = true, ignore_public_acls = true, restrict_public_buckets = true',
21
+ check: (content, _filePath) => {
22
+ const matches = [];
23
+ const lines = content.split('\n');
24
+ // Find S3 bucket resources without public access block
25
+ let inS3Bucket = false;
26
+ let bucketStartLine = 0;
27
+ let hasPublicAccessBlock = false;
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const line = lines[i];
30
+ if (line.includes('resource "aws_s3_bucket"')) {
31
+ inS3Bucket = true;
32
+ bucketStartLine = i + 1;
33
+ hasPublicAccessBlock = false;
34
+ }
35
+ if (inS3Bucket && line.includes('aws_s3_bucket_public_access_block')) {
36
+ hasPublicAccessBlock = true;
37
+ }
38
+ if (inS3Bucket && line.trim() === '}' && !line.includes('{')) {
39
+ if (!hasPublicAccessBlock) {
40
+ matches.push({
41
+ line: bucketStartLine,
42
+ matchedText: 'aws_s3_bucket without public access block',
43
+ });
44
+ }
45
+ inS3Bucket = false;
46
+ }
47
+ }
48
+ // Also check for explicit public ACLs
49
+ for (let i = 0; i < lines.length; i++) {
50
+ if (lines[i].match(/acl\s*=\s*["']public-(read|read-write)["']/)) {
51
+ matches.push({
52
+ line: i + 1,
53
+ matchedText: lines[i].trim(),
54
+ });
55
+ }
56
+ }
57
+ return matches;
58
+ },
59
+ },
60
+ {
61
+ id: 'TF-002',
62
+ name: 'AWS Security Group Open to World',
63
+ description: 'Security group should not allow unrestricted inbound access',
64
+ severity: 'high',
65
+ fileTypes: ['terraform'],
66
+ cwes: ['CWE-284'],
67
+ references: ['https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules-reference.html'],
68
+ remediation: 'Restrict cidr_blocks to specific IP ranges instead of 0.0.0.0/0',
69
+ check: (content, _filePath) => {
70
+ const matches = [];
71
+ const lines = content.split('\n');
72
+ for (let i = 0; i < lines.length; i++) {
73
+ if (lines[i].match(/cidr_blocks\s*=\s*\[\s*["']0\.0\.0\.0\/0["']\s*\]/)) {
74
+ matches.push({
75
+ line: i + 1,
76
+ matchedText: lines[i].trim(),
77
+ });
78
+ }
79
+ }
80
+ return matches;
81
+ },
82
+ },
83
+ {
84
+ id: 'TF-003',
85
+ name: 'AWS RDS Public Accessibility',
86
+ description: 'RDS instance should not be publicly accessible',
87
+ severity: 'high',
88
+ fileTypes: ['terraform'],
89
+ cwes: ['CWE-284'],
90
+ references: ['https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html'],
91
+ remediation: 'Set publicly_accessible = false',
92
+ check: (content, _filePath) => {
93
+ const matches = [];
94
+ const lines = content.split('\n');
95
+ for (let i = 0; i < lines.length; i++) {
96
+ if (lines[i].match(/publicly_accessible\s*=\s*true/)) {
97
+ matches.push({
98
+ line: i + 1,
99
+ matchedText: lines[i].trim(),
100
+ });
101
+ }
102
+ }
103
+ return matches;
104
+ },
105
+ },
106
+ {
107
+ id: 'TF-004',
108
+ name: 'AWS EC2 Unencrypted EBS',
109
+ description: 'EBS volumes should be encrypted',
110
+ severity: 'medium',
111
+ fileTypes: ['terraform'],
112
+ cwes: ['CWE-311'],
113
+ references: ['https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html'],
114
+ remediation: 'Set encrypted = true in ebs_block_device',
115
+ check: (content, _filePath) => {
116
+ const matches = [];
117
+ const lines = content.split('\n');
118
+ let inEbsBlock = false;
119
+ let ebsStartLine = 0;
120
+ let hasEncryption = false;
121
+ for (let i = 0; i < lines.length; i++) {
122
+ const line = lines[i];
123
+ if (line.includes('ebs_block_device') || line.includes('aws_ebs_volume')) {
124
+ inEbsBlock = true;
125
+ ebsStartLine = i + 1;
126
+ hasEncryption = false;
127
+ }
128
+ if (inEbsBlock && line.includes('encrypted')) {
129
+ hasEncryption = true;
130
+ }
131
+ if (inEbsBlock && line.trim() === '}') {
132
+ if (!hasEncryption) {
133
+ matches.push({
134
+ line: ebsStartLine,
135
+ matchedText: 'EBS volume without encryption',
136
+ });
137
+ }
138
+ inEbsBlock = false;
139
+ }
140
+ }
141
+ return matches;
142
+ },
143
+ },
144
+ {
145
+ id: 'TF-005',
146
+ name: 'Hardcoded Credentials',
147
+ description: 'Credentials should not be hardcoded',
148
+ severity: 'critical',
149
+ fileTypes: ['terraform'],
150
+ cwes: ['CWE-798'],
151
+ references: ['https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_password'],
152
+ remediation: 'Use variables, environment variables, or secret management services',
153
+ check: (content, _filePath) => {
154
+ const matches = [];
155
+ const lines = content.split('\n');
156
+ const patterns = [
157
+ /password\s*=\s*["'][^"']+["']/i,
158
+ /secret\s*=\s*["'][^"']+["']/i,
159
+ /api_key\s*=\s*["'][^"']+["']/i,
160
+ /access_key\s*=\s*["']AKIA[0-9A-Z]+["']/i,
161
+ ];
162
+ for (let i = 0; i < lines.length; i++) {
163
+ for (const pattern of patterns) {
164
+ if (lines[i].match(pattern) && !lines[i].includes('var.')) {
165
+ matches.push({
166
+ line: i + 1,
167
+ matchedText: lines[i].trim().replace(/["'][^"']{10,}["']/, '"***"'),
168
+ });
169
+ }
170
+ }
171
+ }
172
+ return matches;
173
+ },
174
+ },
175
+ {
176
+ id: 'TF-006',
177
+ name: 'AWS CloudWatch Logs Not Encrypted',
178
+ description: 'CloudWatch log groups should be encrypted',
179
+ severity: 'medium',
180
+ fileTypes: ['terraform'],
181
+ cwes: ['CWE-311'],
182
+ references: ['https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html'],
183
+ remediation: 'Add kms_key_id to aws_cloudwatch_log_group',
184
+ check: (content, _filePath) => {
185
+ const matches = [];
186
+ const lines = content.split('\n');
187
+ let inLogGroup = false;
188
+ let logGroupStartLine = 0;
189
+ let hasKmsKey = false;
190
+ for (let i = 0; i < lines.length; i++) {
191
+ const line = lines[i];
192
+ if (line.includes('resource "aws_cloudwatch_log_group"')) {
193
+ inLogGroup = true;
194
+ logGroupStartLine = i + 1;
195
+ hasKmsKey = false;
196
+ }
197
+ if (inLogGroup && line.includes('kms_key_id')) {
198
+ hasKmsKey = true;
199
+ }
200
+ if (inLogGroup && line.trim() === '}' && !line.includes('{')) {
201
+ if (!hasKmsKey) {
202
+ matches.push({
203
+ line: logGroupStartLine,
204
+ matchedText: 'CloudWatch log group without KMS encryption',
205
+ });
206
+ }
207
+ inLogGroup = false;
208
+ }
209
+ }
210
+ return matches;
211
+ },
212
+ },
213
+ ];
214
+ /**
215
+ * Built-in Kubernetes rules
216
+ */
217
+ const KUBERNETES_RULES = [
218
+ {
219
+ id: 'K8S-001',
220
+ name: 'Container Running as Root',
221
+ description: 'Containers should not run as root',
222
+ severity: 'high',
223
+ fileTypes: ['kubernetes'],
224
+ cwes: ['CWE-250'],
225
+ references: ['https://kubernetes.io/docs/concepts/security/pod-security-standards/'],
226
+ remediation: 'Set securityContext.runAsNonRoot = true',
227
+ check: (content, _filePath) => {
228
+ const matches = [];
229
+ const lines = content.split('\n');
230
+ // Check for missing runAsNonRoot or runAsUser: 0
231
+ let inContainer = false;
232
+ let hasSecurityContext = false;
233
+ let containerStartLine = 0;
234
+ for (let i = 0; i < lines.length; i++) {
235
+ const line = lines[i];
236
+ if (line.match(/^\s*-?\s*name:\s*\S+/) && lines[i - 1]?.includes('containers:')) {
237
+ inContainer = true;
238
+ containerStartLine = i + 1;
239
+ hasSecurityContext = false;
240
+ }
241
+ if (inContainer && line.includes('securityContext:')) {
242
+ hasSecurityContext = true;
243
+ }
244
+ if (line.match(/runAsUser:\s*0/)) {
245
+ matches.push({
246
+ line: i + 1,
247
+ matchedText: line.trim(),
248
+ });
249
+ }
250
+ if (inContainer && line.match(/^\s*-?\s*name:/) && i > containerStartLine) {
251
+ if (!hasSecurityContext) {
252
+ matches.push({
253
+ line: containerStartLine,
254
+ matchedText: 'Container without securityContext',
255
+ });
256
+ }
257
+ containerStartLine = i + 1;
258
+ hasSecurityContext = false;
259
+ }
260
+ }
261
+ return matches;
262
+ },
263
+ },
264
+ {
265
+ id: 'K8S-002',
266
+ name: 'Privileged Container',
267
+ description: 'Containers should not run in privileged mode',
268
+ severity: 'critical',
269
+ fileTypes: ['kubernetes'],
270
+ cwes: ['CWE-250'],
271
+ references: ['https://kubernetes.io/docs/concepts/security/pod-security-standards/'],
272
+ remediation: 'Set securityContext.privileged = false or remove it',
273
+ check: (content, _filePath) => {
274
+ const matches = [];
275
+ const lines = content.split('\n');
276
+ for (let i = 0; i < lines.length; i++) {
277
+ if (lines[i].match(/privileged:\s*true/)) {
278
+ matches.push({
279
+ line: i + 1,
280
+ matchedText: lines[i].trim(),
281
+ });
282
+ }
283
+ }
284
+ return matches;
285
+ },
286
+ },
287
+ {
288
+ id: 'K8S-003',
289
+ name: 'Host Network Enabled',
290
+ description: 'Pods should not use host network',
291
+ severity: 'high',
292
+ fileTypes: ['kubernetes'],
293
+ cwes: ['CWE-668'],
294
+ references: ['https://kubernetes.io/docs/concepts/security/pod-security-standards/'],
295
+ remediation: 'Set hostNetwork = false or remove it',
296
+ check: (content, _filePath) => {
297
+ const matches = [];
298
+ const lines = content.split('\n');
299
+ for (let i = 0; i < lines.length; i++) {
300
+ if (lines[i].match(/hostNetwork:\s*true/)) {
301
+ matches.push({
302
+ line: i + 1,
303
+ matchedText: lines[i].trim(),
304
+ });
305
+ }
306
+ }
307
+ return matches;
308
+ },
309
+ },
310
+ {
311
+ id: 'K8S-004',
312
+ name: 'No Resource Limits',
313
+ description: 'Containers should have resource limits',
314
+ severity: 'medium',
315
+ fileTypes: ['kubernetes'],
316
+ cwes: ['CWE-400'],
317
+ references: ['https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'],
318
+ remediation: 'Add resources.limits for CPU and memory',
319
+ check: (content, _filePath) => {
320
+ const matches = [];
321
+ const lines = content.split('\n');
322
+ let inContainer = false;
323
+ let hasLimits = false;
324
+ let containerStartLine = 0;
325
+ for (let i = 0; i < lines.length; i++) {
326
+ const line = lines[i];
327
+ if (line.match(/^\s*-?\s*name:/) && lines[i - 1]?.includes('containers:')) {
328
+ if (inContainer && !hasLimits) {
329
+ matches.push({
330
+ line: containerStartLine,
331
+ matchedText: 'Container without resource limits',
332
+ });
333
+ }
334
+ inContainer = true;
335
+ containerStartLine = i + 1;
336
+ hasLimits = false;
337
+ }
338
+ if (inContainer && line.includes('limits:')) {
339
+ hasLimits = true;
340
+ }
341
+ }
342
+ // Check last container
343
+ if (inContainer && !hasLimits) {
344
+ matches.push({
345
+ line: containerStartLine,
346
+ matchedText: 'Container without resource limits',
347
+ });
348
+ }
349
+ return matches;
350
+ },
351
+ },
352
+ {
353
+ id: 'K8S-005',
354
+ name: 'Secrets in Environment Variables',
355
+ description: 'Secrets should not be exposed as environment variables',
356
+ severity: 'medium',
357
+ fileTypes: ['kubernetes'],
358
+ cwes: ['CWE-200'],
359
+ references: ['https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets'],
360
+ remediation: 'Use secretKeyRef with volume mounts instead',
361
+ check: (content, _filePath) => {
362
+ const matches = [];
363
+ const lines = content.split('\n');
364
+ for (let i = 0; i < lines.length; i++) {
365
+ // Check for inline secret values in env vars
366
+ if (lines[i].match(/^\s*-?\s*name:\s*(password|secret|key|token)/i) &&
367
+ lines[i + 1]?.match(/^\s*value:/)) {
368
+ matches.push({
369
+ line: i + 1,
370
+ matchedText: `${lines[i].trim()} with inline value`,
371
+ });
372
+ }
373
+ }
374
+ return matches;
375
+ },
376
+ },
377
+ ];
378
+ /**
379
+ * Built-in CloudFormation rules
380
+ */
381
+ const CLOUDFORMATION_RULES = [
382
+ {
383
+ id: 'CFN-001',
384
+ name: 'S3 Bucket Without Encryption',
385
+ description: 'S3 buckets should have encryption enabled',
386
+ severity: 'high',
387
+ fileTypes: ['cloudformation'],
388
+ cwes: ['CWE-311'],
389
+ references: ['https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html'],
390
+ remediation: 'Add BucketEncryption property with ServerSideEncryptionConfiguration',
391
+ check: (content, _filePath) => {
392
+ const matches = [];
393
+ const lines = content.split('\n');
394
+ let inS3Bucket = false;
395
+ let bucketStartLine = 0;
396
+ let hasEncryption = false;
397
+ for (let i = 0; i < lines.length; i++) {
398
+ const line = lines[i];
399
+ if (line.includes('AWS::S3::Bucket')) {
400
+ inS3Bucket = true;
401
+ bucketStartLine = i + 1;
402
+ hasEncryption = false;
403
+ }
404
+ if (inS3Bucket && line.includes('BucketEncryption')) {
405
+ hasEncryption = true;
406
+ }
407
+ // Simple heuristic for resource end
408
+ if (inS3Bucket && line.match(/^\s{2}\w+:/) && i > bucketStartLine + 2) {
409
+ if (!hasEncryption) {
410
+ matches.push({
411
+ line: bucketStartLine,
412
+ matchedText: 'S3 bucket without encryption',
413
+ });
414
+ }
415
+ inS3Bucket = false;
416
+ }
417
+ }
418
+ return matches;
419
+ },
420
+ },
421
+ {
422
+ id: 'CFN-002',
423
+ name: 'Security Group Unrestricted Ingress',
424
+ description: 'Security groups should not allow unrestricted ingress',
425
+ severity: 'high',
426
+ fileTypes: ['cloudformation'],
427
+ cwes: ['CWE-284'],
428
+ references: ['https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group.html'],
429
+ remediation: 'Restrict CidrIp to specific IP ranges',
430
+ check: (content, _filePath) => {
431
+ const matches = [];
432
+ const lines = content.split('\n');
433
+ for (let i = 0; i < lines.length; i++) {
434
+ if (lines[i].match(/CidrIp:\s*['"]?0\.0\.0\.0\/0['"]?/)) {
435
+ matches.push({
436
+ line: i + 1,
437
+ matchedText: lines[i].trim(),
438
+ });
439
+ }
440
+ }
441
+ return matches;
442
+ },
443
+ },
444
+ ];
445
+ /**
446
+ * All built-in rules
447
+ */
448
+ const BUILTIN_RULES = [
449
+ ...TERRAFORM_RULES,
450
+ ...KUBERNETES_RULES,
451
+ ...CLOUDFORMATION_RULES,
452
+ ];
453
+ /**
454
+ * IaC Checker implementation
455
+ * @trace DES-SEC2-IAC-001
456
+ */
457
+ export class IaCChecker {
458
+ options;
459
+ rules;
460
+ constructor(options = {}) {
461
+ this.options = {
462
+ fileTypes: options.fileTypes ?? ['terraform', 'cloudformation', 'kubernetes'],
463
+ minSeverity: options.minSeverity ?? 'low',
464
+ skipRules: options.skipRules ?? [],
465
+ };
466
+ // Build rule set
467
+ this.rules = [
468
+ ...BUILTIN_RULES.filter(r => !this.options.skipRules?.includes(r.id)),
469
+ ...(options.customRules ?? []),
470
+ ];
471
+ }
472
+ /**
473
+ * Analyze IaC files in a directory
474
+ * @trace REQ-SEC2-IAC-001
475
+ */
476
+ async analyze(dirPath, options) {
477
+ const mergedOptions = { ...this.options, ...options };
478
+ const results = [];
479
+ // Find IaC files
480
+ const files = this.findIaCFiles(dirPath, mergedOptions.fileTypes ?? []);
481
+ for (const file of files) {
482
+ const result = await this.analyzeFile(file.path, file.type, mergedOptions);
483
+ results.push(result);
484
+ }
485
+ return results;
486
+ }
487
+ /**
488
+ * Analyze a single IaC file
489
+ */
490
+ async analyzeFile(filePath, fileType, options) {
491
+ const startTime = Date.now();
492
+ const mergedOptions = { ...this.options, ...options };
493
+ if (!existsSync(filePath)) {
494
+ throw new Error(`File not found: ${filePath}`);
495
+ }
496
+ const content = readFileSync(filePath, 'utf-8');
497
+ const issues = [];
498
+ const resources = [];
499
+ // Get applicable rules
500
+ const applicableRules = this.rules.filter(r => r.fileTypes.includes(fileType) && !mergedOptions.skipRules?.includes(r.id));
501
+ // Run rules
502
+ const minSeverityLevel = this.getSeverityLevel(mergedOptions.minSeverity ?? 'low');
503
+ for (const rule of applicableRules) {
504
+ if (this.getSeverityLevel(rule.severity) < minSeverityLevel) {
505
+ continue;
506
+ }
507
+ const matches = rule.check(content, filePath);
508
+ for (const match of matches) {
509
+ issues.push({
510
+ id: `${rule.id}-${match.line}`,
511
+ ruleId: rule.id,
512
+ severity: rule.severity,
513
+ title: rule.name,
514
+ description: rule.description,
515
+ location: {
516
+ file: filePath,
517
+ startLine: match.line,
518
+ endLine: match.endLine ?? match.line,
519
+ startColumn: match.column ?? 0,
520
+ endColumn: match.endColumn ?? 0,
521
+ },
522
+ remediation: rule.remediation,
523
+ references: rule.references,
524
+ cwes: rule.cwes,
525
+ });
526
+ }
527
+ }
528
+ // Extract resources (simplified)
529
+ resources.push(...this.extractResources(content, fileType, filePath));
530
+ return {
531
+ filePath,
532
+ fileType,
533
+ issues,
534
+ resources,
535
+ analysisTime: Date.now() - startTime,
536
+ };
537
+ }
538
+ /**
539
+ * Convert IaC issues to standard vulnerability format
540
+ */
541
+ toVulnerabilities(results) {
542
+ const vulnerabilities = [];
543
+ for (const result of results) {
544
+ for (const issue of result.issues) {
545
+ vulnerabilities.push({
546
+ id: issue.id,
547
+ type: 'configuration',
548
+ severity: issue.severity,
549
+ cwes: issue.cwes ?? [],
550
+ owasp: ['A05:2021'], // Security Misconfiguration
551
+ location: issue.location,
552
+ description: `${issue.title}: ${issue.description}`,
553
+ recommendation: issue.remediation,
554
+ confidence: 0.9,
555
+ ruleId: issue.ruleId,
556
+ codeSnippet: '',
557
+ detectedAt: new Date(),
558
+ });
559
+ }
560
+ }
561
+ return vulnerabilities;
562
+ }
563
+ /**
564
+ * Find IaC files in a directory
565
+ */
566
+ findIaCFiles(dirPath, fileTypes) {
567
+ const files = [];
568
+ if (!existsSync(dirPath)) {
569
+ return files;
570
+ }
571
+ const scan = (dir) => {
572
+ const entries = readdirSync(dir);
573
+ for (const entry of entries) {
574
+ const fullPath = path.join(dir, entry);
575
+ const stat = statSync(fullPath);
576
+ if (stat.isDirectory()) {
577
+ // Skip common non-IaC directories
578
+ if (!['node_modules', '.git', '.terraform', 'vendor'].includes(entry)) {
579
+ scan(fullPath);
580
+ }
581
+ }
582
+ else {
583
+ const type = this.detectFileType(entry, fullPath);
584
+ if (type && fileTypes.includes(type)) {
585
+ files.push({ path: fullPath, type });
586
+ }
587
+ }
588
+ }
589
+ };
590
+ const stat = statSync(dirPath);
591
+ if (stat.isFile()) {
592
+ const type = this.detectFileType(path.basename(dirPath), dirPath);
593
+ if (type && fileTypes.includes(type)) {
594
+ files.push({ path: dirPath, type });
595
+ }
596
+ }
597
+ else {
598
+ scan(dirPath);
599
+ }
600
+ return files;
601
+ }
602
+ /**
603
+ * Detect IaC file type
604
+ */
605
+ detectFileType(filename, filePath) {
606
+ // Terraform
607
+ if (filename.endsWith('.tf') || filename.endsWith('.tf.json')) {
608
+ return 'terraform';
609
+ }
610
+ // Kubernetes
611
+ if (filename.endsWith('.yaml') || filename.endsWith('.yml')) {
612
+ try {
613
+ const content = readFileSync(filePath, 'utf-8');
614
+ if (content.includes('apiVersion:') && content.includes('kind:')) {
615
+ return 'kubernetes';
616
+ }
617
+ }
618
+ catch {
619
+ // Ignore read errors
620
+ }
621
+ }
622
+ // CloudFormation
623
+ if (filename.includes('cloudformation') || filename.includes('cfn')) {
624
+ return 'cloudformation';
625
+ }
626
+ if ((filename.endsWith('.yaml') || filename.endsWith('.yml') || filename.endsWith('.json'))) {
627
+ try {
628
+ const content = readFileSync(filePath, 'utf-8');
629
+ if (content.includes('AWSTemplateFormatVersion') || content.includes('AWS::')) {
630
+ return 'cloudformation';
631
+ }
632
+ }
633
+ catch {
634
+ // Ignore read errors
635
+ }
636
+ }
637
+ // Docker Compose
638
+ if (filename === 'docker-compose.yml' || filename === 'docker-compose.yaml') {
639
+ return 'docker-compose';
640
+ }
641
+ // Helm
642
+ if (filename === 'Chart.yaml' || (filename.endsWith('.yaml') && filePath.includes('/templates/'))) {
643
+ return 'helm';
644
+ }
645
+ // Ansible
646
+ if (filename.endsWith('.yml') || filename.endsWith('.yaml')) {
647
+ try {
648
+ const content = readFileSync(filePath, 'utf-8');
649
+ if (content.includes('- hosts:') || content.includes('- name:') && content.includes('tasks:')) {
650
+ return 'ansible';
651
+ }
652
+ }
653
+ catch {
654
+ // Ignore read errors
655
+ }
656
+ }
657
+ return null;
658
+ }
659
+ /**
660
+ * Extract resources from IaC content
661
+ */
662
+ extractResources(content, fileType, filePath) {
663
+ const resources = [];
664
+ const lines = content.split('\n');
665
+ if (fileType === 'terraform') {
666
+ // Extract Terraform resources
667
+ const resourcePattern = /resource\s+"([^"]+)"\s+"([^"]+)"/;
668
+ for (let i = 0; i < lines.length; i++) {
669
+ const match = lines[i].match(resourcePattern);
670
+ if (match) {
671
+ resources.push({
672
+ type: match[1],
673
+ name: match[2],
674
+ location: {
675
+ file: filePath,
676
+ startLine: i + 1,
677
+ endLine: i + 1,
678
+ startColumn: 0,
679
+ endColumn: lines[i].length,
680
+ },
681
+ properties: {},
682
+ });
683
+ }
684
+ }
685
+ }
686
+ else if (fileType === 'kubernetes') {
687
+ // Extract Kubernetes resources
688
+ let currentKind = '';
689
+ let currentName = '';
690
+ let kindLine = 0;
691
+ for (let i = 0; i < lines.length; i++) {
692
+ const kindMatch = lines[i].match(/^kind:\s*(\S+)/);
693
+ const nameMatch = lines[i].match(/^\s*name:\s*(\S+)/);
694
+ if (kindMatch) {
695
+ if (currentKind && currentName) {
696
+ resources.push({
697
+ type: currentKind,
698
+ name: currentName,
699
+ location: {
700
+ file: filePath,
701
+ startLine: kindLine,
702
+ endLine: i,
703
+ startColumn: 0,
704
+ endColumn: 0,
705
+ },
706
+ properties: {},
707
+ });
708
+ }
709
+ currentKind = kindMatch[1];
710
+ kindLine = i + 1;
711
+ currentName = '';
712
+ }
713
+ if (nameMatch && currentKind && !currentName) {
714
+ currentName = nameMatch[1];
715
+ }
716
+ }
717
+ // Add last resource
718
+ if (currentKind && currentName) {
719
+ resources.push({
720
+ type: currentKind,
721
+ name: currentName,
722
+ location: {
723
+ file: filePath,
724
+ startLine: kindLine,
725
+ endLine: lines.length,
726
+ startColumn: 0,
727
+ endColumn: 0,
728
+ },
729
+ properties: {},
730
+ });
731
+ }
732
+ }
733
+ return resources;
734
+ }
735
+ /**
736
+ * Get numeric severity level
737
+ */
738
+ getSeverityLevel(severity) {
739
+ const levels = {
740
+ critical: 4,
741
+ high: 3,
742
+ medium: 2,
743
+ low: 1,
744
+ info: 0,
745
+ };
746
+ return levels[severity] ?? 0;
747
+ }
748
+ }
749
+ /**
750
+ * Create IaC checker instance
751
+ */
752
+ export function createIaCChecker(options) {
753
+ return new IaCChecker(options);
754
+ }
755
+ //# sourceMappingURL=iac-checker.js.map