guardrail-compliance 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 (149) hide show
  1. package/dist/audit/emitter.d.ts +97 -0
  2. package/dist/audit/emitter.d.ts.map +1 -0
  3. package/dist/audit/emitter.js +197 -0
  4. package/dist/audit/events.d.ts +304 -0
  5. package/dist/audit/events.d.ts.map +1 -0
  6. package/dist/audit/events.js +267 -0
  7. package/dist/audit/index.d.ts +11 -0
  8. package/dist/audit/index.d.ts.map +1 -0
  9. package/dist/audit/index.js +51 -0
  10. package/dist/audit/storage.d.ts +93 -0
  11. package/dist/audit/storage.d.ts.map +1 -0
  12. package/dist/audit/storage.js +337 -0
  13. package/dist/automation/__tests__/compliance-scheduler.test.d.ts +2 -0
  14. package/dist/automation/__tests__/compliance-scheduler.test.d.ts.map +1 -0
  15. package/dist/automation/__tests__/compliance-scheduler.test.js +140 -0
  16. package/dist/automation/audit-logger.d.ts +129 -0
  17. package/dist/automation/audit-logger.d.ts.map +1 -0
  18. package/dist/automation/audit-logger.js +473 -0
  19. package/dist/automation/compliance-scheduler-fixed.d.ts +1 -0
  20. package/dist/automation/compliance-scheduler-fixed.d.ts.map +1 -0
  21. package/dist/automation/compliance-scheduler-fixed.js +1 -0
  22. package/dist/automation/compliance-scheduler.d.ts +83 -0
  23. package/dist/automation/compliance-scheduler.d.ts.map +1 -0
  24. package/dist/automation/compliance-scheduler.js +414 -0
  25. package/dist/automation/dashboard.d.ts +194 -0
  26. package/dist/automation/dashboard.d.ts.map +1 -0
  27. package/dist/automation/dashboard.js +768 -0
  28. package/dist/automation/email-service.d.ts +69 -0
  29. package/dist/automation/email-service.d.ts.map +1 -0
  30. package/dist/automation/email-service.js +218 -0
  31. package/dist/automation/evidence-collector.d.ts +140 -0
  32. package/dist/automation/evidence-collector.d.ts.map +1 -0
  33. package/dist/automation/evidence-collector.js +682 -0
  34. package/dist/automation/index.d.ts +8 -0
  35. package/dist/automation/index.d.ts.map +1 -0
  36. package/dist/automation/index.js +24 -0
  37. package/dist/automation/pdf-exporter.d.ts +90 -0
  38. package/dist/automation/pdf-exporter.d.ts.map +1 -0
  39. package/dist/automation/pdf-exporter.js +381 -0
  40. package/dist/automation/reporting-engine.d.ts +116 -0
  41. package/dist/automation/reporting-engine.d.ts.map +1 -0
  42. package/dist/automation/reporting-engine.js +329 -0
  43. package/dist/container/index.d.ts +4 -0
  44. package/dist/container/index.d.ts.map +1 -0
  45. package/dist/container/index.js +19 -0
  46. package/dist/container/kubernetes.d.ts +94 -0
  47. package/dist/container/kubernetes.d.ts.map +1 -0
  48. package/dist/container/kubernetes.js +268 -0
  49. package/dist/container/rules.d.ts +27 -0
  50. package/dist/container/rules.d.ts.map +1 -0
  51. package/dist/container/rules.js +216 -0
  52. package/dist/container/scanner.d.ts +50 -0
  53. package/dist/container/scanner.d.ts.map +1 -0
  54. package/dist/container/scanner.js +143 -0
  55. package/dist/frameworks/engine.d.ts +108 -0
  56. package/dist/frameworks/engine.d.ts.map +1 -0
  57. package/dist/frameworks/engine.js +206 -0
  58. package/dist/frameworks/gdpr.d.ts +6 -0
  59. package/dist/frameworks/gdpr.d.ts.map +1 -0
  60. package/dist/frameworks/gdpr.js +198 -0
  61. package/dist/frameworks/hipaa.d.ts +6 -0
  62. package/dist/frameworks/hipaa.d.ts.map +1 -0
  63. package/dist/frameworks/hipaa.js +183 -0
  64. package/dist/frameworks/index.d.ts +8 -0
  65. package/dist/frameworks/index.d.ts.map +1 -0
  66. package/dist/frameworks/index.js +30 -0
  67. package/dist/frameworks/iso27001.d.ts +63 -0
  68. package/dist/frameworks/iso27001.d.ts.map +1 -0
  69. package/dist/frameworks/iso27001.js +331 -0
  70. package/dist/frameworks/nist.d.ts +62 -0
  71. package/dist/frameworks/nist.d.ts.map +1 -0
  72. package/dist/frameworks/nist.js +424 -0
  73. package/dist/frameworks/pci.d.ts +6 -0
  74. package/dist/frameworks/pci.d.ts.map +1 -0
  75. package/dist/frameworks/pci.js +201 -0
  76. package/dist/frameworks/soc2.d.ts +7 -0
  77. package/dist/frameworks/soc2.d.ts.map +1 -0
  78. package/dist/frameworks/soc2.js +248 -0
  79. package/dist/iac/drift-detector.d.ts +64 -0
  80. package/dist/iac/drift-detector.d.ts.map +1 -0
  81. package/dist/iac/drift-detector.js +134 -0
  82. package/dist/iac/index.d.ts +4 -0
  83. package/dist/iac/index.d.ts.map +1 -0
  84. package/dist/iac/index.js +19 -0
  85. package/dist/iac/rules.d.ts +17 -0
  86. package/dist/iac/rules.d.ts.map +1 -0
  87. package/dist/iac/rules.js +385 -0
  88. package/dist/iac/scanner.d.ts +104 -0
  89. package/dist/iac/scanner.d.ts.map +1 -0
  90. package/dist/iac/scanner.js +343 -0
  91. package/dist/index.d.ts +7 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +28 -0
  94. package/dist/pii/data-flow.d.ts +58 -0
  95. package/dist/pii/data-flow.d.ts.map +1 -0
  96. package/dist/pii/data-flow.js +154 -0
  97. package/dist/pii/detector.d.ts +60 -0
  98. package/dist/pii/detector.d.ts.map +1 -0
  99. package/dist/pii/detector.js +267 -0
  100. package/dist/pii/index.d.ts +4 -0
  101. package/dist/pii/index.d.ts.map +1 -0
  102. package/dist/pii/index.js +19 -0
  103. package/dist/pii/patterns.d.ts +36 -0
  104. package/dist/pii/patterns.d.ts.map +1 -0
  105. package/dist/pii/patterns.js +108 -0
  106. package/dist/policy/index.d.ts +5 -0
  107. package/dist/policy/index.d.ts.map +1 -0
  108. package/dist/policy/index.js +20 -0
  109. package/dist/policy/opa-engine.d.ts +121 -0
  110. package/dist/policy/opa-engine.d.ts.map +1 -0
  111. package/dist/policy/opa-engine.js +423 -0
  112. package/package.json +31 -0
  113. package/src/audit/emitter.ts +383 -0
  114. package/src/audit/events.ts +351 -0
  115. package/src/audit/index.ts +35 -0
  116. package/src/audit/storage.ts +394 -0
  117. package/src/automation/__tests__/compliance-scheduler.test.ts +183 -0
  118. package/src/automation/audit-logger.ts +629 -0
  119. package/src/automation/compliance-scheduler-fixed.ts +0 -0
  120. package/src/automation/compliance-scheduler.ts +516 -0
  121. package/src/automation/dashboard.ts +947 -0
  122. package/src/automation/email-service.ts +230 -0
  123. package/src/automation/evidence-collector.ts +866 -0
  124. package/src/automation/index.ts +8 -0
  125. package/src/automation/pdf-exporter.ts +434 -0
  126. package/src/automation/reporting-engine.ts +462 -0
  127. package/src/container/index.ts +3 -0
  128. package/src/container/kubernetes.ts +379 -0
  129. package/src/container/rules.ts +244 -0
  130. package/src/container/scanner.ts +202 -0
  131. package/src/frameworks/engine.ts +298 -0
  132. package/src/frameworks/gdpr.ts +204 -0
  133. package/src/frameworks/hipaa.ts +209 -0
  134. package/src/frameworks/index.ts +23 -0
  135. package/src/frameworks/iso27001.ts +398 -0
  136. package/src/frameworks/nist.ts +518 -0
  137. package/src/frameworks/pci.ts +226 -0
  138. package/src/frameworks/soc2.ts +281 -0
  139. package/src/iac/drift-detector.ts +197 -0
  140. package/src/iac/index.ts +3 -0
  141. package/src/iac/rules.ts +420 -0
  142. package/src/iac/scanner.ts +445 -0
  143. package/src/index.ts +17 -0
  144. package/src/pii/data-flow.ts +216 -0
  145. package/src/pii/detector.ts +327 -0
  146. package/src/pii/index.ts +3 -0
  147. package/src/pii/patterns.ts +128 -0
  148. package/src/policy/index.ts +5 -0
  149. package/src/policy/opa-engine.ts +504 -0
@@ -0,0 +1,379 @@
1
+ import { prisma } from '@guardrail/database';
2
+ import { readFileSync, readdirSync, statSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { K8S_RULES } from './rules';
5
+
6
+ export interface K8sManifest {
7
+ apiVersion: string;
8
+ kind: string;
9
+ metadata: {
10
+ name: string;
11
+ namespace?: string;
12
+ };
13
+ spec: any;
14
+ }
15
+
16
+ export interface K8sFinding {
17
+ ruleId: string;
18
+ title: string;
19
+ description: string;
20
+ severity: 'critical' | 'high' | 'medium' | 'low';
21
+ resourceType: string;
22
+ resourceName: string;
23
+ namespace?: string;
24
+ filePath: string;
25
+ recommendation: string;
26
+ }
27
+
28
+ export interface RBACAnalysis {
29
+ roles: Array<{
30
+ name: string;
31
+ namespace?: string;
32
+ rules: any[];
33
+ riskyPermissions: string[];
34
+ }>;
35
+ roleBindings: Array<{
36
+ name: string;
37
+ namespace?: string;
38
+ subjects: any[];
39
+ roleRef: any;
40
+ }>;
41
+ findings: string[];
42
+ }
43
+
44
+ export interface PodSecurityAnalysis {
45
+ totalPods: number;
46
+ privilegedPods: number;
47
+ hostNetworkPods: number;
48
+ runAsRootPods: number;
49
+ findings: K8sFinding[];
50
+ }
51
+
52
+ export interface NetworkPolicyAnalysis {
53
+ hasNetworkPolicies: boolean;
54
+ totalPolicies: number;
55
+ unprotectedNamespaces: string[];
56
+ findings: string[];
57
+ }
58
+
59
+ export interface KubernetesScanResult {
60
+ projectId: string;
61
+ summary: {
62
+ totalResources: number;
63
+ critical: number;
64
+ high: number;
65
+ medium: number;
66
+ low: number;
67
+ };
68
+ findings: K8sFinding[];
69
+ rbacAnalysis?: RBACAnalysis;
70
+ podSecurity?: PodSecurityAnalysis;
71
+ networkPolicies?: NetworkPolicyAnalysis;
72
+ }
73
+
74
+ export class KubernetesScanner {
75
+ /**
76
+ * Scan Kubernetes manifests
77
+ */
78
+ async scanManifests(projectPath: string, projectId: string): Promise<KubernetesScanResult> {
79
+ // Find all Kubernetes manifest files
80
+ const manifestFiles = this.findManifestFiles(projectPath);
81
+ const manifests: Array<{ manifest: K8sManifest; filePath: string }> = [];
82
+
83
+ for (const file of manifestFiles) {
84
+ try {
85
+ const content = readFileSync(file, 'utf-8');
86
+ const docs = this.parseYAML(content);
87
+
88
+ // Handle multiple documents in one file
89
+ const docArray = Array.isArray(docs) ? docs : [docs];
90
+
91
+ for (const doc of docArray) {
92
+ if (doc && doc.kind && doc.metadata) {
93
+ manifests.push({
94
+ manifest: doc,
95
+ filePath: file
96
+ });
97
+ }
98
+ }
99
+ } catch (error) {
100
+ console.error(`Error parsing manifest ${file}:`, error);
101
+ }
102
+ }
103
+
104
+ // Scan for security issues
105
+ const findings = this.scanForSecurityIssues(manifests);
106
+
107
+ // Analyze RBAC
108
+ const rbacAnalysis = await this.analyzeRBAC(manifests.map(m => m.manifest));
109
+
110
+ // Check pod security
111
+ const podSecurity = await this.checkPodSecurity(manifests.map(m => m.manifest));
112
+
113
+ // Validate network policies
114
+ const networkPolicies = await this.validateNetworkPolicies(manifests.map(m => m.manifest));
115
+
116
+ const summary = {
117
+ totalResources: manifests.length,
118
+ critical: findings.filter(f => f.severity === 'critical').length,
119
+ high: findings.filter(f => f.severity === 'high').length,
120
+ medium: findings.filter(f => f.severity === 'medium').length,
121
+ low: findings.filter(f => f.severity === 'low').length
122
+ };
123
+
124
+ // Save to database
125
+ try {
126
+ await (prisma as any).kubernetesScan.create({
127
+ data: {
128
+ projectId,
129
+ clusterName: 'default',
130
+ findings: findings as any,
131
+ results: {
132
+ findings,
133
+ summary,
134
+ rbacAnalysis,
135
+ podSecurity,
136
+ networkPolicies
137
+ } as any,
138
+ status: summary.critical > 0 ? 'failed' : summary.high > 0 ? 'warning' : 'passed'
139
+ }
140
+ });
141
+ } catch (error) {
142
+ // Table may not exist - continue
143
+ }
144
+
145
+ return {
146
+ projectId,
147
+ summary,
148
+ findings,
149
+ rbacAnalysis,
150
+ podSecurity,
151
+ networkPolicies
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Analyze RBAC configuration
157
+ */
158
+ async analyzeRBAC(manifests: K8sManifest[]): Promise<RBACAnalysis> {
159
+ const roles = manifests.filter(m => m.kind === 'Role' || m.kind === 'ClusterRole');
160
+ const roleBindings = manifests.filter(m => m.kind === 'RoleBinding' || m.kind === 'ClusterRoleBinding');
161
+
162
+ const findings: string[] = [];
163
+ const analyzedRoles = [];
164
+
165
+ for (const role of roles) {
166
+ const riskyPermissions: string[] = [];
167
+ const rules = role.spec?.rules || [];
168
+
169
+ for (const rule of rules) {
170
+ // Check for wildcard permissions
171
+ if (rule.verbs?.includes('*')) {
172
+ riskyPermissions.push('Wildcard verb permissions');
173
+ }
174
+
175
+ if (rule.resources?.includes('*')) {
176
+ riskyPermissions.push('Wildcard resource permissions');
177
+ }
178
+
179
+ // Check for dangerous permissions
180
+ const dangerousVerbs = ['create', 'delete', 'deletecollection'];
181
+ const dangerousResources = ['secrets', 'pods/exec', 'pods/portforward'];
182
+
183
+ if (dangerousVerbs.some(v => rule.verbs?.includes(v)) &&
184
+ dangerousResources.some(r => rule.resources?.includes(r))) {
185
+ riskyPermissions.push(`Dangerous permissions: ${rule.verbs.join(',')} on ${rule.resources.join(',')}`);
186
+ }
187
+ }
188
+
189
+ analyzedRoles.push({
190
+ name: role.metadata.name,
191
+ namespace: role.metadata.namespace,
192
+ rules,
193
+ riskyPermissions
194
+ });
195
+
196
+ if (riskyPermissions.length > 0) {
197
+ findings.push(`Role ${role.metadata.name} has risky permissions: ${riskyPermissions.join(', ')}`);
198
+ }
199
+ }
200
+
201
+ return {
202
+ roles: analyzedRoles,
203
+ roleBindings: roleBindings.map(rb => ({
204
+ name: rb.metadata.name,
205
+ namespace: rb.metadata.namespace,
206
+ subjects: rb.spec?.subjects || [],
207
+ roleRef: rb.spec?.roleRef || {}
208
+ })),
209
+ findings
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Check pod security
215
+ */
216
+ async checkPodSecurity(manifests: K8sManifest[]): Promise<PodSecurityAnalysis> {
217
+ const pods = manifests.filter(m =>
218
+ m.kind === 'Pod' || m.kind === 'Deployment' || m.kind === 'StatefulSet' || m.kind === 'DaemonSet'
219
+ );
220
+
221
+ let privilegedPods = 0;
222
+ let hostNetworkPods = 0;
223
+ let runAsRootPods = 0;
224
+ const findings: K8sFinding[] = [];
225
+
226
+ for (const pod of pods) {
227
+ // Get pod spec (handle Deployment/StatefulSet template)
228
+ const podSpec = pod.kind === 'Pod' ? pod.spec : pod.spec?.template?.spec;
229
+
230
+ if (!podSpec) continue;
231
+
232
+ // Check for privileged containers
233
+ const containers = podSpec.containers || [];
234
+ const isPrivileged = containers.some((c: any) => c.securityContext?.privileged === true);
235
+ if (isPrivileged) privilegedPods++;
236
+
237
+ // Check for host network
238
+ if (podSpec.hostNetwork === true) hostNetworkPods++;
239
+
240
+ // Check for running as root
241
+ const runAsRoot = containers.some((c: any) =>
242
+ !c.securityContext?.runAsNonRoot && c.securityContext?.runAsUser === 0
243
+ );
244
+ if (runAsRoot) runAsRootPods++;
245
+ }
246
+
247
+ return {
248
+ totalPods: pods.length,
249
+ privilegedPods,
250
+ hostNetworkPods,
251
+ runAsRootPods,
252
+ findings
253
+ };
254
+ }
255
+
256
+ /**
257
+ * Validate network policies
258
+ */
259
+ async validateNetworkPolicies(manifests: K8sManifest[]): Promise<NetworkPolicyAnalysis> {
260
+ const networkPolicies = manifests.filter(m => m.kind === 'NetworkPolicy');
261
+ const namespaces = new Set<string>();
262
+ const findings: string[] = [];
263
+
264
+ // Collect all namespaces
265
+ for (const manifest of manifests) {
266
+ if (manifest.metadata.namespace) {
267
+ namespaces.add(manifest.metadata.namespace);
268
+ }
269
+ }
270
+
271
+ // Check which namespaces have network policies
272
+ const namespacesWithPolicies = new Set(
273
+ networkPolicies.map(np => np.metadata.namespace).filter(Boolean)
274
+ );
275
+
276
+ const unprotectedNamespaces = Array.from(namespaces).filter(ns =>
277
+ !namespacesWithPolicies.has(ns)
278
+ );
279
+
280
+ if (unprotectedNamespaces.length > 0) {
281
+ findings.push(`${unprotectedNamespaces.length} namespaces without network policies`);
282
+ }
283
+
284
+ return {
285
+ hasNetworkPolicies: networkPolicies.length > 0,
286
+ totalPolicies: networkPolicies.length,
287
+ unprotectedNamespaces,
288
+ findings
289
+ };
290
+ }
291
+
292
+ /**
293
+ * Scan for security issues using rules
294
+ */
295
+ private scanForSecurityIssues(manifests: Array<{ manifest: K8sManifest; filePath: string }>): K8sFinding[] {
296
+ const findings: K8sFinding[] = [];
297
+
298
+ for (const { manifest, filePath } of manifests) {
299
+ // Skip non-pod resources for pod security checks
300
+ const isPodResource = ['Pod', 'Deployment', 'StatefulSet', 'DaemonSet', 'Job', 'CronJob'].includes(manifest.kind);
301
+
302
+ if (!isPodResource) continue;
303
+
304
+ // Get pod spec
305
+ const podSpec = manifest.kind === 'Pod' ? manifest.spec : manifest.spec?.template?.spec;
306
+
307
+ if (!podSpec) continue;
308
+
309
+ // Create a resource object compatible with rules
310
+ const resource = {
311
+ kind: manifest.kind,
312
+ metadata: manifest.metadata,
313
+ spec: podSpec
314
+ };
315
+
316
+ // Check against rules
317
+ for (const rule of K8S_RULES) {
318
+ if (rule.check(resource)) {
319
+ findings.push({
320
+ ruleId: rule.id,
321
+ title: rule.title,
322
+ description: rule.description,
323
+ severity: rule.severity,
324
+ resourceType: manifest.kind,
325
+ resourceName: manifest.metadata.name,
326
+ namespace: manifest.metadata.namespace,
327
+ filePath,
328
+ recommendation: rule.recommendation
329
+ });
330
+ }
331
+ }
332
+ }
333
+
334
+ return findings;
335
+ }
336
+
337
+ /**
338
+ * Find Kubernetes manifest files
339
+ */
340
+ private findManifestFiles(dir: string): string[] {
341
+ const files: string[] = [];
342
+ const extensions = ['.yaml', '.yml'];
343
+
344
+ try {
345
+ const entries = readdirSync(dir);
346
+
347
+ for (const entry of entries) {
348
+ const fullPath = join(dir, entry);
349
+ const stat = statSync(fullPath);
350
+
351
+ if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
352
+ files.push(...this.findManifestFiles(fullPath));
353
+ } else if (stat.isFile()) {
354
+ if (extensions.some(ext => entry.endsWith(ext))) {
355
+ files.push(fullPath);
356
+ }
357
+ }
358
+ }
359
+ } catch (error) {
360
+ // Ignore permission errors
361
+ }
362
+
363
+ return files;
364
+ }
365
+
366
+ /**
367
+ * Simple YAML parser (in production, use proper YAML library)
368
+ */
369
+ private parseYAML(content: string): any {
370
+ try {
371
+ return JSON.parse(content);
372
+ } catch {
373
+ // Simplified - would use yaml library in production
374
+ return {};
375
+ }
376
+ }
377
+ }
378
+
379
+ export const kubernetesScanner = new KubernetesScanner();
@@ -0,0 +1,244 @@
1
+ export interface DockerfileRule {
2
+ id: string;
3
+ title: string;
4
+ description: string;
5
+ severity: 'critical' | 'high' | 'medium' | 'low';
6
+ category: string;
7
+ check: (instruction: string, value: string) => boolean;
8
+ recommendation: string;
9
+ }
10
+
11
+ export interface K8sRule {
12
+ id: string;
13
+ title: string;
14
+ description: string;
15
+ severity: 'critical' | 'high' | 'medium' | 'low';
16
+ category: string;
17
+ check: (resource: any) => boolean;
18
+ recommendation: string;
19
+ }
20
+
21
+ /**
22
+ * Dockerfile Security Rules
23
+ */
24
+ export const DOCKERFILE_RULES: DockerfileRule[] = [
25
+ {
26
+ id: 'DOCKER-001',
27
+ title: 'Running as Root',
28
+ description: 'Container runs as root user',
29
+ severity: 'high',
30
+ category: 'user-security',
31
+ check: (_instruction, _value) => {
32
+ // Check if USER instruction is missing or set to root
33
+ return _instruction === 'USER' && (_value === 'root' || _value === '0');
34
+ },
35
+ recommendation: 'Use a non-root user: USER node or USER 1000'
36
+ },
37
+ {
38
+ id: 'DOCKER-002',
39
+ title: 'Using Latest Tag',
40
+ description: 'Base image uses :latest tag',
41
+ severity: 'medium',
42
+ category: 'versioning',
43
+ check: (_instruction, _value) => {
44
+ return _instruction === 'FROM' && _value.endsWith(':latest');
45
+ },
46
+ recommendation: 'Pin to specific version: FROM node:20.11.0'
47
+ },
48
+ {
49
+ id: 'DOCKER-003',
50
+ title: 'Missing Health Check',
51
+ description: 'No HEALTHCHECK instruction defined',
52
+ severity: 'low',
53
+ category: 'reliability',
54
+ check: (_instruction, _value) => {
55
+ // This would be checked at the Dockerfile level, not per instruction
56
+ return false;
57
+ },
58
+ recommendation: 'Add HEALTHCHECK instruction'
59
+ },
60
+ {
61
+ id: 'DOCKER-004',
62
+ title: 'Secrets in ENV',
63
+ description: 'Potential secrets in ENV instruction',
64
+ severity: 'critical',
65
+ category: 'secrets',
66
+ check: (_instruction, _value) => {
67
+ if (_instruction !== 'ENV') return false;
68
+ const secretKeywords = ['password', 'secret', 'key', 'token', 'api_key'];
69
+ return secretKeywords.some(keyword => _value.toLowerCase().includes(keyword));
70
+ },
71
+ recommendation: 'Use build-time secrets or runtime secret management'
72
+ },
73
+ {
74
+ id: 'DOCKER-005',
75
+ title: 'ADD Instead of COPY',
76
+ description: 'Using ADD instead of COPY',
77
+ severity: 'low',
78
+ category: 'best-practices',
79
+ check: (_instruction, _value) => {
80
+ return _instruction === 'ADD';
81
+ },
82
+ recommendation: 'Use COPY instead of ADD unless you need tar extraction or URL fetching'
83
+ },
84
+ {
85
+ id: 'DOCKER-006',
86
+ title: 'Missing Multi-Stage Build',
87
+ description: 'Not using multi-stage builds',
88
+ severity: 'low',
89
+ category: 'optimization',
90
+ check: (_instruction, _value) => {
91
+ // Would check if multiple FROM instructions exist
92
+ return false;
93
+ },
94
+ recommendation: 'Use multi-stage builds to reduce image size'
95
+ },
96
+ {
97
+ id: 'DOCKER-007',
98
+ title: 'Exposing Privileged Ports',
99
+ description: 'Exposing ports below 1024',
100
+ severity: 'medium',
101
+ category: 'security',
102
+ check: (_instruction, _value) => {
103
+ if (_instruction !== 'EXPOSE' || !_value) return false;
104
+ const port = parseInt(_value.split('/')[0] || '', 10);
105
+ return port < 1024;
106
+ },
107
+ recommendation: 'Use ports above 1024 to avoid running as root'
108
+ }
109
+ ];
110
+
111
+ /**
112
+ * Kubernetes Security Rules
113
+ */
114
+ export const K8S_RULES: K8sRule[] = [
115
+ {
116
+ id: 'K8S-SEC-001',
117
+ title: 'Privileged Container',
118
+ description: 'Container running in privileged mode',
119
+ severity: 'critical',
120
+ category: 'pod-security',
121
+ check: (resource) => {
122
+ const containers = resource.spec?.containers || [];
123
+ return containers.some((c: any) => c.securityContext?.privileged === true);
124
+ },
125
+ recommendation: 'Remove privileged: true from securityContext'
126
+ },
127
+ {
128
+ id: 'K8S-SEC-002',
129
+ title: 'Host Network',
130
+ description: 'Pod has access to host network',
131
+ severity: 'high',
132
+ category: 'pod-security',
133
+ check: (resource) => {
134
+ return resource.spec?.hostNetwork === true;
135
+ },
136
+ recommendation: 'Set hostNetwork: false or remove the field'
137
+ },
138
+ {
139
+ id: 'K8S-SEC-003',
140
+ title: 'Host PID Namespace',
141
+ description: 'Pod has access to host PID namespace',
142
+ severity: 'high',
143
+ category: 'pod-security',
144
+ check: (resource) => {
145
+ return resource.spec?.hostPID === true;
146
+ },
147
+ recommendation: 'Set hostPID: false or remove the field'
148
+ },
149
+ {
150
+ id: 'K8S-SEC-004',
151
+ title: 'Host IPC Namespace',
152
+ description: 'Pod has access to host IPC namespace',
153
+ severity: 'high',
154
+ category: 'pod-security',
155
+ check: (resource) => {
156
+ return resource.spec?.hostIPC === true;
157
+ },
158
+ recommendation: 'Set hostIPC: false or remove the field'
159
+ },
160
+ {
161
+ id: 'K8S-SEC-005',
162
+ title: 'Root User',
163
+ description: 'Container running as root',
164
+ severity: 'high',
165
+ category: 'pod-security',
166
+ check: (resource) => {
167
+ const containers = resource.spec?.containers || [];
168
+ return containers.some((c: any) =>
169
+ !c.securityContext?.runAsNonRoot && c.securityContext?.runAsUser === 0
170
+ );
171
+ },
172
+ recommendation: 'Set runAsNonRoot: true in securityContext'
173
+ },
174
+ {
175
+ id: 'K8S-SEC-006',
176
+ title: 'Dangerous Capabilities',
177
+ description: 'Container has dangerous Linux capabilities',
178
+ severity: 'high',
179
+ category: 'pod-security',
180
+ check: (resource) => {
181
+ const containers = resource.spec?.containers || [];
182
+ const dangerous = ['SYS_ADMIN', 'NET_ADMIN', 'SYS_MODULE'];
183
+ return containers.some((c: any) =>
184
+ c.securityContext?.capabilities?.add?.some((cap: string) =>
185
+ dangerous.includes(cap)
186
+ )
187
+ );
188
+ },
189
+ recommendation: 'Remove dangerous capabilities or use allowPrivilegeEscalation: false'
190
+ },
191
+ {
192
+ id: 'K8S-SEC-007',
193
+ title: 'Read-Only Root Filesystem',
194
+ description: 'Root filesystem is not read-only',
195
+ severity: 'medium',
196
+ category: 'pod-security',
197
+ check: (resource) => {
198
+ const containers = resource.spec?.containers || [];
199
+ return containers.some((c: any) =>
200
+ c.securityContext?.readOnlyRootFilesystem !== true
201
+ );
202
+ },
203
+ recommendation: 'Set readOnlyRootFilesystem: true in securityContext'
204
+ },
205
+ {
206
+ id: 'K8S-RES-001',
207
+ title: 'Missing Resource Limits',
208
+ description: 'Container missing CPU/memory limits',
209
+ severity: 'medium',
210
+ category: 'resource-management',
211
+ check: (resource) => {
212
+ const containers = resource.spec?.containers || [];
213
+ return containers.some((c: any) =>
214
+ !c.resources?.limits || !c.resources?.limits?.cpu || !c.resources?.limits?.memory
215
+ );
216
+ },
217
+ recommendation: 'Set resources.limits.cpu and resources.limits.memory'
218
+ },
219
+ {
220
+ id: 'K8S-RES-002',
221
+ title: 'Missing Resource Requests',
222
+ description: 'Container missing CPU/memory requests',
223
+ severity: 'low',
224
+ category: 'resource-management',
225
+ check: (resource) => {
226
+ const containers = resource.spec?.containers || [];
227
+ return containers.some((c: any) =>
228
+ !c.resources?.requests || !c.resources?.requests?.cpu || !c.resources?.requests?.memory
229
+ );
230
+ },
231
+ recommendation: 'Set resources.requests.cpu and resources.requests.memory'
232
+ },
233
+ {
234
+ id: 'K8S-SA-001',
235
+ title: 'Service Account Token Auto-Mount',
236
+ description: 'Service account token auto-mounted',
237
+ severity: 'medium',
238
+ category: 'pod-security',
239
+ check: (resource) => {
240
+ return resource.spec?.automountServiceAccountToken !== false;
241
+ },
242
+ recommendation: 'Set automountServiceAccountToken: false unless needed'
243
+ }
244
+ ];