@build-astron-co/nimbus 0.2.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 (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,1608 @@
1
+ /**
2
+ * Best Practices Engine
3
+ *
4
+ * Merged module containing:
5
+ * - BestPracticesEngine class (analyze, analyzeAll, autofix, scoring, reporting)
6
+ * - All 60+ best practice rules across security, tagging, cost, reliability,
7
+ * performance, networking, compliance, ECS, and KMS categories
8
+ */
9
+
10
+ import { logger } from '../utils';
11
+
12
+ // ==========================================
13
+ // Types
14
+ // ==========================================
15
+
16
+ export interface BestPracticeRule {
17
+ id: string;
18
+ category:
19
+ | 'security'
20
+ | 'tagging'
21
+ | 'cost'
22
+ | 'reliability'
23
+ | 'performance'
24
+ | 'networking'
25
+ | 'compliance';
26
+ severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
27
+ title: string;
28
+ description: string;
29
+ recommendation: string;
30
+ applies_to: string[]; // Component types this rule applies to
31
+ check: (config: Record<string, unknown>) => boolean;
32
+ autofix?: (config: Record<string, unknown>) => Record<string, unknown>;
33
+ }
34
+
35
+ export interface BestPracticeViolation {
36
+ rule_id: string;
37
+ category: string;
38
+ severity: string;
39
+ title: string;
40
+ description: string;
41
+ recommendation: string;
42
+ component: string;
43
+ can_autofix: boolean;
44
+ }
45
+
46
+ export interface BestPracticeReport {
47
+ summary: {
48
+ total_rules_checked: number;
49
+ violations_found: number;
50
+ violations_by_severity: Record<string, number>;
51
+ violations_by_category: Record<string, number>;
52
+ autofixable_violations: number;
53
+ };
54
+ violations: BestPracticeViolation[];
55
+ recommendations: string[];
56
+ }
57
+
58
+ export interface SecurityBestPractices {
59
+ encryption_at_rest: boolean;
60
+ encryption_in_transit: boolean;
61
+ principle_of_least_privilege: boolean;
62
+ network_isolation: boolean;
63
+ secret_management: boolean;
64
+ audit_logging: boolean;
65
+ mfa_enabled: boolean;
66
+ }
67
+
68
+ export interface TaggingBestPractices {
69
+ required_tags: string[];
70
+ tag_format: 'PascalCase' | 'camelCase' | 'snake_case' | 'kebab-case';
71
+ enforce_tags: boolean;
72
+ }
73
+
74
+ export interface CostOptimizationBestPractices {
75
+ right_sizing: boolean;
76
+ reserved_instances: boolean;
77
+ spot_instances: boolean;
78
+ lifecycle_policies: boolean;
79
+ unused_resource_detection: boolean;
80
+ cost_allocation_tags: boolean;
81
+ }
82
+
83
+ // ==========================================
84
+ // Rules
85
+ // ==========================================
86
+
87
+ /**
88
+ * Security Best Practice Rules
89
+ */
90
+ export const securityRules: BestPracticeRule[] = [
91
+ {
92
+ id: 'sec-001',
93
+ category: 'security',
94
+ severity: 'critical',
95
+ title: 'Enable Encryption at Rest',
96
+ description: 'All data stores should have encryption at rest enabled',
97
+ recommendation: 'Enable encryption at rest using AWS KMS for all data storage services',
98
+ applies_to: ['rds', 's3', 'ebs', 'efs'],
99
+ check: config => {
100
+ return config.storage_encrypted === true || config.encryption_enabled === true;
101
+ },
102
+ autofix: config => ({
103
+ ...config,
104
+ storage_encrypted: true,
105
+ encryption_enabled: true,
106
+ }),
107
+ },
108
+ {
109
+ id: 'sec-002',
110
+ category: 'security',
111
+ severity: 'high',
112
+ title: 'Enable VPC Flow Logs',
113
+ description: 'VPC should have flow logs enabled for security monitoring',
114
+ recommendation: 'Enable VPC flow logs to monitor and troubleshoot network traffic',
115
+ applies_to: ['vpc'],
116
+ check: config => config.enable_flow_logs === true,
117
+ autofix: config => ({
118
+ ...config,
119
+ enable_flow_logs: true,
120
+ flow_logs_retention_days: 30,
121
+ }),
122
+ },
123
+ {
124
+ id: 'sec-003',
125
+ category: 'security',
126
+ severity: 'critical',
127
+ title: 'Block Public Access for S3',
128
+ description: 'S3 buckets should block all public access unless explicitly required',
129
+ recommendation: 'Enable all S3 public access block settings',
130
+ applies_to: ['s3'],
131
+ check: config => {
132
+ return (
133
+ config.block_public_acls === true &&
134
+ config.block_public_policy === true &&
135
+ config.ignore_public_acls === true &&
136
+ config.restrict_public_buckets === true
137
+ );
138
+ },
139
+ autofix: config => ({
140
+ ...config,
141
+ block_public_acls: true,
142
+ block_public_policy: true,
143
+ ignore_public_acls: true,
144
+ restrict_public_buckets: true,
145
+ }),
146
+ },
147
+ {
148
+ id: 'sec-004',
149
+ category: 'security',
150
+ severity: 'high',
151
+ title: 'Enable S3 Versioning',
152
+ description: 'S3 buckets should have versioning enabled for data protection',
153
+ recommendation: 'Enable S3 versioning to protect against accidental deletion',
154
+ applies_to: ['s3'],
155
+ check: config => config.enable_versioning === true,
156
+ autofix: config => ({
157
+ ...config,
158
+ enable_versioning: true,
159
+ }),
160
+ },
161
+ {
162
+ id: 'sec-005',
163
+ category: 'security',
164
+ severity: 'critical',
165
+ title: 'Use Private Subnets for Databases',
166
+ description: 'Databases should not be publicly accessible',
167
+ recommendation: 'Deploy RDS instances in private subnets only',
168
+ applies_to: ['rds'],
169
+ check: config => config.publicly_accessible === false,
170
+ autofix: config => ({
171
+ ...config,
172
+ publicly_accessible: false,
173
+ }),
174
+ },
175
+ {
176
+ id: 'sec-006',
177
+ category: 'security',
178
+ severity: 'high',
179
+ title: 'Enable Deletion Protection',
180
+ description: 'Production databases should have deletion protection enabled',
181
+ recommendation: 'Enable deletion protection for RDS instances',
182
+ applies_to: ['rds'],
183
+ check: config => {
184
+ if (config.environment === 'production') {
185
+ return config.deletion_protection === true;
186
+ }
187
+ return true; // Not required for non-production
188
+ },
189
+ autofix: config => {
190
+ if (config.environment === 'production') {
191
+ return { ...config, deletion_protection: true };
192
+ }
193
+ return config;
194
+ },
195
+ },
196
+ {
197
+ id: 'sec-007',
198
+ category: 'security',
199
+ severity: 'medium',
200
+ title: 'Enable EKS Cluster Logging',
201
+ description: 'EKS clusters should have control plane logging enabled',
202
+ recommendation: 'Enable all EKS cluster log types for auditing and troubleshooting',
203
+ applies_to: ['eks'],
204
+ check: config => config.enable_cluster_logs === true,
205
+ autofix: config => ({
206
+ ...config,
207
+ enable_cluster_logs: true,
208
+ cluster_log_types: ['api', 'audit', 'authenticator', 'controllerManager', 'scheduler'],
209
+ cluster_log_retention_days: 30,
210
+ }),
211
+ },
212
+ {
213
+ id: 'sec-008',
214
+ category: 'security',
215
+ severity: 'high',
216
+ title: 'Enable EKS Secret Encryption',
217
+ description: 'EKS should encrypt Kubernetes secrets at rest',
218
+ recommendation: 'Configure KMS key for EKS secret encryption',
219
+ applies_to: ['eks'],
220
+ check: config => config.enable_secret_encryption === true,
221
+ autofix: config => ({
222
+ ...config,
223
+ enable_secret_encryption: true,
224
+ }),
225
+ },
226
+ {
227
+ id: 'sec-009',
228
+ category: 'security',
229
+ severity: 'medium',
230
+ title: 'Restrict EKS API Access',
231
+ description: 'EKS API endpoint should not be publicly accessible without restrictions',
232
+ recommendation: 'Limit public access CIDR blocks or use private endpoint only',
233
+ applies_to: ['eks'],
234
+ check: config => {
235
+ if (config.endpoint_public_access === false) {
236
+ return true;
237
+ }
238
+ return Array.isArray(config.public_access_cidrs) && config.public_access_cidrs.length > 0;
239
+ },
240
+ },
241
+ {
242
+ id: 'sec-010',
243
+ category: 'security',
244
+ severity: 'high',
245
+ title: 'Enable RDS Enhanced Monitoring',
246
+ description: 'RDS instances should have enhanced monitoring enabled',
247
+ recommendation: 'Enable enhanced monitoring with 60-second granularity',
248
+ applies_to: ['rds'],
249
+ check: config => config.enable_enhanced_monitoring === true,
250
+ autofix: config => ({
251
+ ...config,
252
+ enable_enhanced_monitoring: true,
253
+ }),
254
+ },
255
+ ];
256
+
257
+ /**
258
+ * Additional Security Best Practice Rules
259
+ */
260
+ export const additionalSecurityRules: BestPracticeRule[] = [
261
+ {
262
+ id: 'sec-011',
263
+ category: 'security',
264
+ severity: 'high',
265
+ title: 'Enable CloudTrail Logging',
266
+ description: 'CloudTrail should be enabled for all API calls',
267
+ recommendation: 'Enable CloudTrail logging in all regions for audit and compliance',
268
+ applies_to: ['cloudtrail', 'account'],
269
+ check: config => config.enable_cloudtrail === true,
270
+ autofix: config => ({
271
+ ...config,
272
+ enable_cloudtrail: true,
273
+ cloudtrail_multi_region: true,
274
+ }),
275
+ },
276
+ {
277
+ id: 'sec-012',
278
+ category: 'security',
279
+ severity: 'high',
280
+ title: 'Use HTTPS-Only for ALB Listeners',
281
+ description: 'Application Load Balancers should only use HTTPS listeners',
282
+ recommendation: 'Configure ALB listeners to use HTTPS with valid SSL certificates',
283
+ applies_to: ['alb', 'elb', 'ecs'],
284
+ check: config => config.listener_protocol === 'HTTPS' || config.redirect_http_to_https === true,
285
+ autofix: config => ({
286
+ ...config,
287
+ redirect_http_to_https: true,
288
+ }),
289
+ },
290
+ {
291
+ id: 'sec-013',
292
+ category: 'security',
293
+ severity: 'critical',
294
+ title: 'Restrict SSH Access',
295
+ description: 'SSH access should not be open to 0.0.0.0/0',
296
+ recommendation: 'Restrict SSH (port 22) access to known CIDR ranges only',
297
+ applies_to: ['security_group', 'vpc'],
298
+ check: config => {
299
+ const rules = config.ingress_rules as Array<{ port: number; cidr: string }> | undefined;
300
+ if (!rules) {
301
+ return true;
302
+ }
303
+ return !rules.some(r => r.port === 22 && r.cidr === '0.0.0.0/0');
304
+ },
305
+ },
306
+ {
307
+ id: 'sec-014',
308
+ category: 'security',
309
+ severity: 'medium',
310
+ title: 'Enable WAF for Public-Facing ALBs',
311
+ description: 'Public-facing ALBs should have WAF enabled',
312
+ recommendation: 'Attach AWS WAF to public-facing Application Load Balancers',
313
+ applies_to: ['alb', 'ecs'],
314
+ check: config => config.enable_waf === true || config.internal === true,
315
+ autofix: config => {
316
+ if (!config.internal) {
317
+ return { ...config, enable_waf: true };
318
+ }
319
+ return config;
320
+ },
321
+ },
322
+ {
323
+ id: 'sec-015',
324
+ category: 'security',
325
+ severity: 'medium',
326
+ title: 'Enable GuardDuty',
327
+ description: 'GuardDuty should be enabled for threat detection',
328
+ recommendation: 'Enable Amazon GuardDuty for intelligent threat detection',
329
+ applies_to: ['account', 'guardduty'],
330
+ check: config => config.enable_guardduty === true,
331
+ autofix: config => ({
332
+ ...config,
333
+ enable_guardduty: true,
334
+ }),
335
+ },
336
+ {
337
+ id: 'sec-016',
338
+ category: 'security',
339
+ severity: 'high',
340
+ title: 'Use IAM Roles Instead of Access Keys',
341
+ description: 'Prefer IAM roles over long-lived access keys',
342
+ recommendation: 'Use IAM roles for EC2/ECS/EKS instead of embedding access keys',
343
+ applies_to: ['iam', 'ec2', 'ecs', 'eks'],
344
+ check: config => config.use_iam_role === true || config.access_key_id === undefined,
345
+ },
346
+ {
347
+ id: 'sec-017',
348
+ category: 'security',
349
+ severity: 'medium',
350
+ title: 'Enable MFA Delete on S3 Buckets',
351
+ description: 'S3 buckets should have MFA delete enabled for critical data',
352
+ recommendation: 'Enable MFA delete to prevent accidental or malicious deletion',
353
+ applies_to: ['s3'],
354
+ check: config => {
355
+ if (config.environment === 'production') {
356
+ return config.mfa_delete === true;
357
+ }
358
+ return true;
359
+ },
360
+ },
361
+ {
362
+ id: 'sec-018',
363
+ category: 'security',
364
+ severity: 'high',
365
+ title: 'Encrypt EBS Volumes by Default',
366
+ description: 'EBS volumes should be encrypted by default',
367
+ recommendation: 'Enable default EBS encryption for all new volumes',
368
+ applies_to: ['ebs', 'ec2'],
369
+ check: config => config.ebs_encryption === true || config.encrypted === true,
370
+ autofix: config => ({
371
+ ...config,
372
+ ebs_encryption: true,
373
+ encrypted: true,
374
+ }),
375
+ },
376
+ ];
377
+
378
+ /**
379
+ * Tagging Best Practice Rules
380
+ */
381
+ export const taggingRules: BestPracticeRule[] = [
382
+ {
383
+ id: 'tag-001',
384
+ category: 'tagging',
385
+ severity: 'medium',
386
+ title: 'Mandatory Tags Present',
387
+ description: 'All resources should have mandatory tags',
388
+ recommendation: 'Include Environment, ManagedBy, Project, and Owner tags on all resources',
389
+ applies_to: ['vpc', 'eks', 'rds', 's3', 'ecs', 'kms'],
390
+ check: config => {
391
+ const tags = config.tags as Record<string, string> | undefined;
392
+ if (!tags) {
393
+ return false;
394
+ }
395
+
396
+ const requiredTags = ['Environment', 'ManagedBy', 'Project'];
397
+ return requiredTags.every(tag => tag in tags);
398
+ },
399
+ autofix: config => ({
400
+ ...config,
401
+ tags: {
402
+ ...((config.tags as Record<string, string>) || {}),
403
+ Environment: config.environment || 'development',
404
+ ManagedBy: 'Terraform',
405
+ Project: config.project_name,
406
+ },
407
+ }),
408
+ },
409
+ {
410
+ id: 'tag-002',
411
+ category: 'tagging',
412
+ severity: 'low',
413
+ title: 'Cost Allocation Tags',
414
+ description: 'Resources should include cost allocation tags',
415
+ recommendation: 'Add CostCenter and Team tags for cost tracking',
416
+ applies_to: ['vpc', 'eks', 'rds', 's3', 'ecs', 'kms'],
417
+ check: config => {
418
+ const tags = config.tags as Record<string, string> | undefined;
419
+ if (!tags) {
420
+ return false;
421
+ }
422
+ return 'CostCenter' in tags || 'Team' in tags;
423
+ },
424
+ },
425
+ {
426
+ id: 'tag-003',
427
+ category: 'tagging',
428
+ severity: 'low',
429
+ title: 'Use Consistent Naming Convention',
430
+ description: 'Resources should follow a consistent naming convention',
431
+ recommendation: 'Use format: {project}-{environment}-{component}-{resource_type}',
432
+ applies_to: ['vpc', 'eks', 'rds', 's3', 'ec2', 'ecs', 'kms'],
433
+ check: config => {
434
+ const name = config.name || config.resource_name;
435
+ if (!name) {
436
+ return true;
437
+ }
438
+ return /^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+/.test(name as string);
439
+ },
440
+ },
441
+ ];
442
+
443
+ /**
444
+ * Cost Optimization Best Practice Rules
445
+ */
446
+ export const costRules: BestPracticeRule[] = [
447
+ {
448
+ id: 'cost-001',
449
+ category: 'cost',
450
+ severity: 'medium',
451
+ title: 'Enable S3 Lifecycle Policies',
452
+ description: 'S3 buckets should have lifecycle policies to optimize storage costs',
453
+ recommendation:
454
+ 'Configure lifecycle rules to transition old objects to cheaper storage classes',
455
+ applies_to: ['s3'],
456
+ check: config => config.enable_lifecycle_rules === true,
457
+ autofix: config => ({
458
+ ...config,
459
+ enable_lifecycle_rules: true,
460
+ transition_to_ia_days: 30,
461
+ transition_to_glacier_days: 90,
462
+ expiration_days: 365,
463
+ }),
464
+ },
465
+ {
466
+ id: 'cost-002',
467
+ category: 'cost',
468
+ severity: 'low',
469
+ title: 'Use Single NAT Gateway for Non-Production',
470
+ description: 'Non-production environments can use a single NAT gateway to reduce costs',
471
+ recommendation: 'Use single_nat_gateway = true for development/staging environments',
472
+ applies_to: ['vpc'],
473
+ check: config => {
474
+ if (config.environment === 'production') {
475
+ return true;
476
+ }
477
+ return config.single_nat_gateway === true;
478
+ },
479
+ autofix: config => {
480
+ if (config.environment !== 'production') {
481
+ return {
482
+ ...config,
483
+ single_nat_gateway: true,
484
+ nat_gateway_count: 1,
485
+ };
486
+ }
487
+ return config;
488
+ },
489
+ },
490
+ {
491
+ id: 'cost-003',
492
+ category: 'cost',
493
+ severity: 'medium',
494
+ title: 'Use Spot Instances for EKS Node Groups',
495
+ description: 'Consider using Spot instances for non-production EKS workloads',
496
+ recommendation:
497
+ 'Set capacity_type = "SPOT" for development environments to reduce costs by up to 90%',
498
+ applies_to: ['eks'],
499
+ check: config => {
500
+ if (config.environment === 'production') {
501
+ return true;
502
+ }
503
+ return config.node_capacity_type === 'SPOT';
504
+ },
505
+ },
506
+ {
507
+ id: 'cost-004',
508
+ category: 'cost',
509
+ severity: 'low',
510
+ title: 'Enable RDS Storage Autoscaling',
511
+ description: 'RDS should use storage autoscaling to optimize costs',
512
+ recommendation: 'Set max_allocated_storage to enable storage autoscaling',
513
+ applies_to: ['rds'],
514
+ check: (config): boolean => {
515
+ if (config.create_cluster) {
516
+ return true;
517
+ } // Aurora handles this differently
518
+ return Boolean(
519
+ config.max_allocated_storage &&
520
+ Number(config.max_allocated_storage) > Number(config.db_allocated_storage || 0)
521
+ );
522
+ },
523
+ },
524
+ {
525
+ id: 'cost-005',
526
+ category: 'cost',
527
+ severity: 'medium',
528
+ title: 'Clean Up Incomplete Multipart Uploads',
529
+ description: 'S3 should automatically clean up incomplete multipart uploads',
530
+ recommendation: 'Add lifecycle rule to abort incomplete uploads after 7 days',
531
+ applies_to: ['s3'],
532
+ check: config => config.abort_incomplete_multipart_days !== undefined,
533
+ autofix: config => ({
534
+ ...config,
535
+ enable_lifecycle_rules: true,
536
+ abort_incomplete_multipart_days: 7,
537
+ }),
538
+ },
539
+ ];
540
+
541
+ /**
542
+ * Additional Cost Optimization Best Practice Rules
543
+ */
544
+ export const additionalCostRules: BestPracticeRule[] = [
545
+ {
546
+ id: 'cost-006',
547
+ category: 'cost',
548
+ severity: 'medium',
549
+ title: 'Use Reserved Capacity for Production',
550
+ description: 'Production workloads should consider reserved capacity',
551
+ recommendation: 'Use Reserved Instances or Savings Plans for predictable production workloads',
552
+ applies_to: ['ec2', 'rds', 'eks'],
553
+ check: config => {
554
+ if (config.environment === 'production') {
555
+ return config.reserved_capacity === true || config.savings_plan === true;
556
+ }
557
+ return true;
558
+ },
559
+ },
560
+ {
561
+ id: 'cost-007',
562
+ category: 'cost',
563
+ severity: 'low',
564
+ title: 'Enable Intelligent Tiering for S3',
565
+ description: 'S3 buckets should use Intelligent-Tiering for cost optimization',
566
+ recommendation: 'Enable S3 Intelligent-Tiering to automatically move data to cheaper tiers',
567
+ applies_to: ['s3'],
568
+ check: config => config.intelligent_tiering === true,
569
+ autofix: config => ({
570
+ ...config,
571
+ intelligent_tiering: true,
572
+ }),
573
+ },
574
+ {
575
+ id: 'cost-008',
576
+ category: 'cost',
577
+ severity: 'medium',
578
+ title: 'Right-Size EKS Node Groups',
579
+ description: 'EKS node groups should have appropriate min/max/desired counts',
580
+ recommendation: 'Set min, max, and desired node counts to avoid over-provisioning',
581
+ applies_to: ['eks'],
582
+ check: config => {
583
+ const min = Number(config.node_min_size || 0);
584
+ const max = Number(config.node_max_size || 0);
585
+ return min > 0 && max > 0 && max >= min;
586
+ },
587
+ },
588
+ {
589
+ id: 'cost-009',
590
+ category: 'cost',
591
+ severity: 'low',
592
+ title: 'Set Budget Alerts for Cost Thresholds',
593
+ description: 'AWS Budget alerts should be configured for cost monitoring',
594
+ recommendation: 'Set up AWS Budgets with alerts at 80% and 100% of expected spend',
595
+ applies_to: ['account', 'budget'],
596
+ check: config => config.enable_budget_alerts === true,
597
+ autofix: config => ({
598
+ ...config,
599
+ enable_budget_alerts: true,
600
+ }),
601
+ },
602
+ ];
603
+
604
+ /**
605
+ * Reliability Best Practice Rules
606
+ */
607
+ export const reliabilityRules: BestPracticeRule[] = [
608
+ {
609
+ id: 'rel-001',
610
+ category: 'reliability',
611
+ severity: 'high',
612
+ title: 'Enable Multi-AZ for Production RDS',
613
+ description: 'Production databases should be deployed across multiple availability zones',
614
+ recommendation: 'Enable multi_az = true for production RDS instances',
615
+ applies_to: ['rds'],
616
+ check: config => {
617
+ if (config.environment !== 'production') {
618
+ return true;
619
+ }
620
+ return config.enable_multi_az === true || config.create_cluster === true;
621
+ },
622
+ autofix: config => {
623
+ if (config.environment === 'production' && !config.create_cluster) {
624
+ return { ...config, enable_multi_az: true };
625
+ }
626
+ return config;
627
+ },
628
+ },
629
+ {
630
+ id: 'rel-002',
631
+ category: 'reliability',
632
+ severity: 'high',
633
+ title: 'Enable RDS Automated Backups',
634
+ description: 'RDS should have automated backups with sufficient retention',
635
+ recommendation: 'Set backup retention to at least 7 days for production',
636
+ applies_to: ['rds'],
637
+ check: config => {
638
+ const retentionDays = Number(config.backup_retention_days || 0);
639
+ if (config.environment === 'production') {
640
+ return retentionDays >= 7;
641
+ }
642
+ return retentionDays >= 1;
643
+ },
644
+ autofix: config => ({
645
+ ...config,
646
+ backup_retention_days: config.environment === 'production' ? 7 : 3,
647
+ }),
648
+ },
649
+ {
650
+ id: 'rel-003',
651
+ category: 'reliability',
652
+ severity: 'medium',
653
+ title: 'Deploy EKS Across Multiple AZs',
654
+ description: 'EKS should be deployed across at least 2 availability zones',
655
+ recommendation: 'Use at least 2 private subnets in different AZs',
656
+ applies_to: ['eks'],
657
+ check: config => {
658
+ const subnetCount = Number(config.private_subnet_count || 0);
659
+ return subnetCount >= 2;
660
+ },
661
+ },
662
+ {
663
+ id: 'rel-004',
664
+ category: 'reliability',
665
+ severity: 'medium',
666
+ title: 'Enable Auto Minor Version Upgrades',
667
+ description: 'Enable automatic minor version upgrades for security patches',
668
+ recommendation: 'Set auto_minor_version_upgrade = true for RDS',
669
+ applies_to: ['rds'],
670
+ check: config => config.auto_minor_version_upgrade === true,
671
+ autofix: config => ({
672
+ ...config,
673
+ auto_minor_version_upgrade: true,
674
+ }),
675
+ },
676
+ ];
677
+
678
+ /**
679
+ * Additional Reliability Best Practice Rules
680
+ */
681
+ export const additionalReliabilityRules: BestPracticeRule[] = [
682
+ {
683
+ id: 'rel-005',
684
+ category: 'reliability',
685
+ severity: 'high',
686
+ title: 'Configure Health Checks for ALB Target Groups',
687
+ description: 'ALB target groups should have health checks configured',
688
+ recommendation: 'Configure health check path, interval, and thresholds for target groups',
689
+ applies_to: ['alb', 'target_group', 'ecs'],
690
+ check: config => config.health_check_path !== undefined,
691
+ autofix: config => ({
692
+ ...config,
693
+ health_check_path: config.health_check_path || '/health',
694
+ health_check_interval: config.health_check_interval || 30,
695
+ healthy_threshold: config.healthy_threshold || 3,
696
+ unhealthy_threshold: config.unhealthy_threshold || 3,
697
+ }),
698
+ },
699
+ {
700
+ id: 'rel-006',
701
+ category: 'reliability',
702
+ severity: 'medium',
703
+ title: 'Set Termination Protection on Production Instances',
704
+ description: 'Production instances should have termination protection enabled',
705
+ recommendation: 'Enable termination protection to prevent accidental instance termination',
706
+ applies_to: ['ec2', 'rds'],
707
+ check: config => {
708
+ if (config.environment === 'production') {
709
+ return config.disable_api_termination === true || config.deletion_protection === true;
710
+ }
711
+ return true;
712
+ },
713
+ autofix: config => {
714
+ if (config.environment === 'production') {
715
+ return { ...config, disable_api_termination: true, deletion_protection: true };
716
+ }
717
+ return config;
718
+ },
719
+ },
720
+ {
721
+ id: 'rel-007',
722
+ category: 'reliability',
723
+ severity: 'medium',
724
+ title: 'Enable Cross-Region Backup for Production RDS',
725
+ description: 'Production RDS instances should have cross-region backups',
726
+ recommendation: 'Enable cross-region automated backups for disaster recovery',
727
+ applies_to: ['rds'],
728
+ check: config => {
729
+ if (config.environment === 'production') {
730
+ return config.enable_cross_region_backup === true;
731
+ }
732
+ return true;
733
+ },
734
+ },
735
+ {
736
+ id: 'rel-008',
737
+ category: 'reliability',
738
+ severity: 'medium',
739
+ title: 'Configure Pod Disruption Budgets',
740
+ description: 'Kubernetes deployments should have Pod Disruption Budgets',
741
+ recommendation: 'Set PodDisruptionBudget to ensure minimum availability during disruptions',
742
+ applies_to: ['eks', 'kubernetes', 'deployment'],
743
+ check: config => config.pod_disruption_budget !== undefined,
744
+ },
745
+ ];
746
+
747
+ /**
748
+ * Performance Best Practice Rules
749
+ */
750
+ export const performanceRules: BestPracticeRule[] = [
751
+ {
752
+ id: 'perf-001',
753
+ category: 'performance',
754
+ severity: 'medium',
755
+ title: 'Enable RDS Performance Insights',
756
+ description: 'Enable Performance Insights for database performance monitoring',
757
+ recommendation: 'Enable performance_insights for RDS instances',
758
+ applies_to: ['rds'],
759
+ check: config => config.enable_performance_insights === true,
760
+ autofix: config => ({
761
+ ...config,
762
+ enable_performance_insights: true,
763
+ performance_insights_retention: 7,
764
+ }),
765
+ },
766
+ {
767
+ id: 'perf-002',
768
+ category: 'performance',
769
+ severity: 'low',
770
+ title: 'Use GP3 Storage for RDS',
771
+ description: 'Use gp3 storage type for better price-performance ratio',
772
+ recommendation: 'Set storage_type = "gp3" for RDS instances',
773
+ applies_to: ['rds'],
774
+ check: config => {
775
+ if (config.create_cluster) {
776
+ return true;
777
+ } // Aurora uses cluster storage
778
+ return config.db_storage_type === 'gp3';
779
+ },
780
+ autofix: config => {
781
+ if (!config.create_cluster) {
782
+ return { ...config, db_storage_type: 'gp3' };
783
+ }
784
+ return config;
785
+ },
786
+ },
787
+ {
788
+ id: 'perf-003',
789
+ category: 'performance',
790
+ severity: 'low',
791
+ title: 'Enable S3 Transfer Acceleration',
792
+ description: 'Enable transfer acceleration for faster uploads from distant locations',
793
+ recommendation: 'Consider enabling S3 transfer acceleration for global access patterns',
794
+ applies_to: ['s3'],
795
+ check: () => true, // Informational only
796
+ },
797
+ {
798
+ id: 'perf-004',
799
+ category: 'performance',
800
+ severity: 'medium',
801
+ title: 'Enable CloudFront for Static Assets',
802
+ description: 'Use CloudFront CDN for serving static assets to reduce latency',
803
+ recommendation: 'Configure CloudFront distribution for S3 static content delivery',
804
+ applies_to: ['s3', 'cloudfront'],
805
+ check: config => config.enable_cdn === true || config.is_private_bucket === true,
806
+ },
807
+ ];
808
+
809
+ /**
810
+ * Networking Best Practice Rules
811
+ */
812
+ export const networkingRules: BestPracticeRule[] = [
813
+ {
814
+ id: 'net-001',
815
+ category: 'networking',
816
+ severity: 'high',
817
+ title: 'Use Private Subnets for Application Workloads',
818
+ description: 'Application workloads should be deployed in private subnets',
819
+ recommendation:
820
+ 'Deploy application servers, databases, and backend services in private subnets',
821
+ applies_to: ['vpc', 'subnet', 'eks', 'rds', 'ecs'],
822
+ check: config => config.use_private_subnets === true || config.publicly_accessible === false,
823
+ autofix: config => ({
824
+ ...config,
825
+ use_private_subnets: true,
826
+ publicly_accessible: false,
827
+ }),
828
+ },
829
+ {
830
+ id: 'net-002',
831
+ category: 'networking',
832
+ severity: 'critical',
833
+ title: 'Restrict Security Group CIDR Ranges',
834
+ description: 'Security groups should not allow 0.0.0.0/0 ingress',
835
+ recommendation: 'Restrict security group ingress rules to specific CIDR ranges',
836
+ applies_to: ['security_group', 'vpc'],
837
+ check: config => {
838
+ const rules = config.ingress_rules as Array<{ cidr: string }> | undefined;
839
+ if (!rules) {
840
+ return true;
841
+ }
842
+ return !rules.some(r => r.cidr === '0.0.0.0/0');
843
+ },
844
+ },
845
+ {
846
+ id: 'net-003',
847
+ category: 'networking',
848
+ severity: 'low',
849
+ title: 'Use Network ACLs as Additional Defense',
850
+ description: 'Network ACLs provide an additional layer of security',
851
+ recommendation: 'Configure Network ACLs in addition to security groups for defense in depth',
852
+ applies_to: ['vpc', 'subnet'],
853
+ check: config => config.enable_network_acls === true,
854
+ },
855
+ {
856
+ id: 'net-004',
857
+ category: 'networking',
858
+ severity: 'medium',
859
+ title: 'Enable DNS Hostnames and Resolution in VPC',
860
+ description: 'VPCs should have DNS hostnames and resolution enabled',
861
+ recommendation: 'Enable enable_dns_hostnames and enable_dns_support in VPC',
862
+ applies_to: ['vpc'],
863
+ check: config => config.enable_dns_hostnames === true && config.enable_dns_support === true,
864
+ autofix: config => ({
865
+ ...config,
866
+ enable_dns_hostnames: true,
867
+ enable_dns_support: true,
868
+ }),
869
+ },
870
+ {
871
+ id: 'net-005',
872
+ category: 'networking',
873
+ severity: 'medium',
874
+ title: 'Use VPC Endpoints for AWS Service Access',
875
+ description: 'Use VPC endpoints to access AWS services without internet gateway',
876
+ recommendation: 'Create VPC endpoints for S3, DynamoDB, and other frequently accessed services',
877
+ applies_to: ['vpc'],
878
+ check: config => config.enable_vpc_endpoints === true,
879
+ autofix: config => ({
880
+ ...config,
881
+ enable_vpc_endpoints: true,
882
+ }),
883
+ },
884
+ ];
885
+
886
+ /**
887
+ * Compliance Best Practice Rules
888
+ */
889
+ export const complianceRules: BestPracticeRule[] = [
890
+ {
891
+ id: 'comp-001',
892
+ category: 'compliance',
893
+ severity: 'medium',
894
+ title: 'Enable Access Logging for S3',
895
+ description: 'S3 buckets should have access logging enabled',
896
+ recommendation: 'Enable server access logging for S3 buckets to track requests',
897
+ applies_to: ['s3'],
898
+ check: config => config.enable_access_logging === true,
899
+ autofix: config => ({
900
+ ...config,
901
+ enable_access_logging: true,
902
+ }),
903
+ },
904
+ {
905
+ id: 'comp-002',
906
+ category: 'compliance',
907
+ severity: 'medium',
908
+ title: 'Enable Audit Logging for RDS',
909
+ description: 'RDS instances should have audit logging enabled',
910
+ recommendation: 'Enable audit logging for database activity tracking',
911
+ applies_to: ['rds'],
912
+ check: config => config.enable_audit_logging === true,
913
+ autofix: config => ({
914
+ ...config,
915
+ enable_audit_logging: true,
916
+ }),
917
+ },
918
+ {
919
+ id: 'comp-003',
920
+ category: 'compliance',
921
+ severity: 'medium',
922
+ title: 'Retain CloudWatch Logs for 90+ Days',
923
+ description: 'CloudWatch log groups should retain logs for at least 90 days',
924
+ recommendation: 'Set CloudWatch log retention to at least 90 days for compliance',
925
+ applies_to: ['cloudwatch', 'eks', 'rds', 'vpc', 'ecs'],
926
+ check: config => {
927
+ const retention = Number(config.log_retention_days || 0);
928
+ return retention >= 90;
929
+ },
930
+ autofix: config => ({
931
+ ...config,
932
+ log_retention_days: Math.max(Number(config.log_retention_days || 0), 90),
933
+ }),
934
+ },
935
+ {
936
+ id: 'comp-004',
937
+ category: 'compliance',
938
+ severity: 'low',
939
+ title: 'Tag Resources with Compliance Framework',
940
+ description: 'All resources should be tagged with compliance framework identifier',
941
+ recommendation: 'Add a ComplianceFramework tag (e.g., SOC2, HIPAA, PCI-DSS) to all resources',
942
+ applies_to: ['vpc', 'eks', 'rds', 's3', 'ec2', 'ecs', 'kms'],
943
+ check: config => {
944
+ const tags = config.tags as Record<string, string> | undefined;
945
+ return tags !== undefined && 'ComplianceFramework' in tags;
946
+ },
947
+ },
948
+ {
949
+ id: 'comp-005',
950
+ category: 'compliance',
951
+ severity: 'medium',
952
+ title: 'Enable Config Recording for Drift Detection',
953
+ description: 'AWS Config should be enabled to record resource configurations',
954
+ recommendation: 'Enable AWS Config recording for drift detection and compliance auditing',
955
+ applies_to: ['account', 'config'],
956
+ check: config => config.enable_config_recording === true,
957
+ autofix: config => ({
958
+ ...config,
959
+ enable_config_recording: true,
960
+ }),
961
+ },
962
+ {
963
+ id: 'comp-006',
964
+ category: 'compliance',
965
+ severity: 'high',
966
+ title: 'Enable SSE-KMS for Sensitive Data Buckets',
967
+ description: 'S3 buckets containing sensitive data should use SSE-KMS encryption',
968
+ recommendation: 'Use SSE-KMS instead of SSE-S3 for buckets with sensitive or regulated data',
969
+ applies_to: ['s3'],
970
+ check: config => config.sse_algorithm === 'aws:kms' || config.kms_key_id !== undefined,
971
+ autofix: config => ({
972
+ ...config,
973
+ sse_algorithm: 'aws:kms',
974
+ }),
975
+ },
976
+ ];
977
+
978
+ /**
979
+ * ECS Best Practice Rules
980
+ */
981
+ export const ecsRules: BestPracticeRule[] = [
982
+ {
983
+ id: 'ecs-001',
984
+ category: 'security',
985
+ severity: 'high',
986
+ title: 'Use Fargate for Serverless Containers',
987
+ description: 'Prefer Fargate launch type for serverless container management',
988
+ recommendation:
989
+ 'Use Fargate to eliminate the need to manage EC2 instances for container workloads',
990
+ applies_to: ['ecs'],
991
+ check: config => config.launch_type === 'FARGATE' || config.launch_type === undefined,
992
+ },
993
+ {
994
+ id: 'ecs-002',
995
+ category: 'security',
996
+ severity: 'high',
997
+ title: 'Deploy ECS Tasks in Private Subnets',
998
+ description: 'ECS tasks should run in private subnets behind a load balancer',
999
+ recommendation:
1000
+ 'Configure ECS service networking to use private subnets with assign_public_ip = false',
1001
+ applies_to: ['ecs'],
1002
+ check: config => config.assign_public_ip === false || config.assign_public_ip === undefined,
1003
+ autofix: config => ({
1004
+ ...config,
1005
+ assign_public_ip: false,
1006
+ }),
1007
+ },
1008
+ {
1009
+ id: 'ecs-003',
1010
+ category: 'reliability',
1011
+ severity: 'high',
1012
+ title: 'Enable ECS Deployment Circuit Breaker',
1013
+ description: 'ECS services should have deployment circuit breaker enabled',
1014
+ recommendation:
1015
+ 'Enable deployment circuit breaker with rollback to prevent failed deployments from impacting availability',
1016
+ applies_to: ['ecs'],
1017
+ check: config => config.enable_circuit_breaker === true,
1018
+ autofix: config => ({
1019
+ ...config,
1020
+ enable_circuit_breaker: true,
1021
+ }),
1022
+ },
1023
+ {
1024
+ id: 'ecs-004',
1025
+ category: 'security',
1026
+ severity: 'medium',
1027
+ title: 'Enable Container Insights',
1028
+ description: 'ECS clusters should have Container Insights enabled for monitoring',
1029
+ recommendation: 'Enable CloudWatch Container Insights for detailed container-level metrics',
1030
+ applies_to: ['ecs'],
1031
+ check: config => config.enable_container_insights === true,
1032
+ autofix: config => ({
1033
+ ...config,
1034
+ enable_container_insights: true,
1035
+ }),
1036
+ },
1037
+ {
1038
+ id: 'ecs-005',
1039
+ category: 'reliability',
1040
+ severity: 'medium',
1041
+ title: 'Configure ECS Auto Scaling',
1042
+ description: 'ECS services should have auto scaling configured for production workloads',
1043
+ recommendation: 'Enable target tracking scaling on CPU and memory utilization',
1044
+ applies_to: ['ecs'],
1045
+ check: config => {
1046
+ if (config.environment === 'production') {
1047
+ return config.enable_autoscaling === true;
1048
+ }
1049
+ return true;
1050
+ },
1051
+ autofix: config => {
1052
+ if (config.environment === 'production') {
1053
+ return {
1054
+ ...config,
1055
+ enable_autoscaling: true,
1056
+ autoscaling_min_capacity: config.desired_count || 2,
1057
+ autoscaling_max_capacity: Math.max(Number(config.desired_count || 2) * 3, 6),
1058
+ cpu_scaling_target: 70,
1059
+ memory_scaling_target: 70,
1060
+ };
1061
+ }
1062
+ return config;
1063
+ },
1064
+ },
1065
+ {
1066
+ id: 'ecs-006',
1067
+ category: 'cost',
1068
+ severity: 'low',
1069
+ title: 'Use Fargate Spot for Non-Production ECS',
1070
+ description: 'Non-production ECS workloads can use Fargate Spot for cost savings',
1071
+ recommendation:
1072
+ 'Configure FARGATE_SPOT capacity provider for development and staging environments',
1073
+ applies_to: ['ecs'],
1074
+ check: config => {
1075
+ if (config.environment === 'production') {
1076
+ return true;
1077
+ }
1078
+ return config.use_fargate_spot === true;
1079
+ },
1080
+ },
1081
+ {
1082
+ id: 'ecs-007',
1083
+ category: 'security',
1084
+ severity: 'medium',
1085
+ title: 'Set Read-Only Root Filesystem',
1086
+ description: 'ECS task containers should use read-only root filesystems where possible',
1087
+ recommendation:
1088
+ 'Set readonlyRootFilesystem = true in container definitions to prevent filesystem modifications',
1089
+ applies_to: ['ecs'],
1090
+ check: config => config.readonly_root_filesystem === true,
1091
+ },
1092
+ ];
1093
+
1094
+ /**
1095
+ * KMS Best Practice Rules
1096
+ */
1097
+ export const kmsRules: BestPracticeRule[] = [
1098
+ {
1099
+ id: 'kms-001',
1100
+ category: 'security',
1101
+ severity: 'critical',
1102
+ title: 'Enable KMS Key Rotation',
1103
+ description: 'KMS keys should have automatic key rotation enabled',
1104
+ recommendation: 'Enable automatic annual key rotation for all customer-managed KMS keys',
1105
+ applies_to: ['kms'],
1106
+ check: config => config.enable_key_rotation === true,
1107
+ autofix: config => ({
1108
+ ...config,
1109
+ enable_key_rotation: true,
1110
+ }),
1111
+ },
1112
+ {
1113
+ id: 'kms-002',
1114
+ category: 'security',
1115
+ severity: 'high',
1116
+ title: 'Set Appropriate KMS Deletion Window',
1117
+ description: 'KMS keys should have a sufficient deletion waiting period',
1118
+ recommendation:
1119
+ 'Set deletion_window_in_days to at least 14 days (30 for production) to allow recovery from accidental deletion',
1120
+ applies_to: ['kms'],
1121
+ check: config => {
1122
+ const window = Number(config.deletion_window_in_days || 0);
1123
+ if (config.environment === 'production') {
1124
+ return window >= 30;
1125
+ }
1126
+ return window >= 7;
1127
+ },
1128
+ autofix: config => ({
1129
+ ...config,
1130
+ deletion_window_in_days: config.environment === 'production' ? 30 : 14,
1131
+ }),
1132
+ },
1133
+ {
1134
+ id: 'kms-003',
1135
+ category: 'security',
1136
+ severity: 'high',
1137
+ title: 'Restrict KMS Key Policy',
1138
+ description: 'KMS key policies should follow the principle of least privilege',
1139
+ recommendation: 'Define explicit key admins and key users instead of granting broad access',
1140
+ applies_to: ['kms'],
1141
+ check: config => {
1142
+ return (
1143
+ (Array.isArray(config.key_admins) && config.key_admins.length > 0) ||
1144
+ (Array.isArray(config.key_users) && config.key_users.length > 0)
1145
+ );
1146
+ },
1147
+ },
1148
+ {
1149
+ id: 'kms-004',
1150
+ category: 'compliance',
1151
+ severity: 'medium',
1152
+ title: 'Use KMS Key Aliases',
1153
+ description: 'KMS keys should have descriptive aliases for identification',
1154
+ recommendation: 'Create meaningful aliases for all KMS keys to improve manageability',
1155
+ applies_to: ['kms'],
1156
+ check: config => config.key_alias !== undefined && config.key_alias !== '',
1157
+ },
1158
+ {
1159
+ id: 'kms-005',
1160
+ category: 'reliability',
1161
+ severity: 'medium',
1162
+ title: 'Consider Multi-Region KMS Keys for DR',
1163
+ description: 'Production KMS keys should consider multi-region replication',
1164
+ recommendation:
1165
+ 'Enable multi-region for KMS keys used by services that need cross-region disaster recovery',
1166
+ applies_to: ['kms'],
1167
+ check: config => {
1168
+ if (config.environment === 'production') {
1169
+ return config.multi_region === true || config.cross_region_not_needed === true;
1170
+ }
1171
+ return true;
1172
+ },
1173
+ },
1174
+ ];
1175
+
1176
+ /**
1177
+ * All Rules Combined
1178
+ */
1179
+ export const allRules: BestPracticeRule[] = [
1180
+ ...securityRules,
1181
+ ...additionalSecurityRules,
1182
+ ...taggingRules,
1183
+ ...costRules,
1184
+ ...additionalCostRules,
1185
+ ...reliabilityRules,
1186
+ ...additionalReliabilityRules,
1187
+ ...performanceRules,
1188
+ ...networkingRules,
1189
+ ...complianceRules,
1190
+ ...ecsRules,
1191
+ ...kmsRules,
1192
+ ];
1193
+
1194
+ // ==========================================
1195
+ // Engine
1196
+ // ==========================================
1197
+
1198
+ export class BestPracticesEngine {
1199
+ private rules: Map<string, BestPracticeRule>;
1200
+
1201
+ constructor(customRules: BestPracticeRule[] = []) {
1202
+ this.rules = new Map();
1203
+
1204
+ // Load default rules
1205
+ [...allRules, ...customRules].forEach(rule => {
1206
+ this.rules.set(rule.id, rule);
1207
+ });
1208
+
1209
+ logger.info(`Initialized Best Practices Engine with ${this.rules.size} rules`);
1210
+ }
1211
+
1212
+ /**
1213
+ * Analyze configuration against all best practices
1214
+ */
1215
+ analyze(
1216
+ component: string,
1217
+ config: Record<string, unknown>,
1218
+ options?: {
1219
+ categories?: Array<
1220
+ | 'security'
1221
+ | 'tagging'
1222
+ | 'cost'
1223
+ | 'reliability'
1224
+ | 'performance'
1225
+ | 'networking'
1226
+ | 'compliance'
1227
+ >;
1228
+ severities?: Array<'critical' | 'high' | 'medium' | 'low' | 'info'>;
1229
+ includeInfo?: boolean;
1230
+ }
1231
+ ): BestPracticeReport {
1232
+ const violations: BestPracticeViolation[] = [];
1233
+ const recommendations: string[] = [];
1234
+
1235
+ // Filter rules based on component and options
1236
+ const applicableRules = this.getApplicableRules(component, options);
1237
+
1238
+ logger.debug(`Checking ${applicableRules.length} rules for component: ${component}`);
1239
+
1240
+ // Check each rule
1241
+ for (const rule of applicableRules) {
1242
+ try {
1243
+ const passed = rule.check(config);
1244
+
1245
+ if (!passed) {
1246
+ violations.push({
1247
+ rule_id: rule.id,
1248
+ category: rule.category,
1249
+ severity: rule.severity,
1250
+ title: rule.title,
1251
+ description: rule.description,
1252
+ recommendation: rule.recommendation,
1253
+ component,
1254
+ can_autofix: !!rule.autofix,
1255
+ });
1256
+
1257
+ recommendations.push(rule.recommendation);
1258
+ }
1259
+ } catch (error) {
1260
+ logger.error(`Error checking rule ${rule.id}`, error);
1261
+ }
1262
+ }
1263
+
1264
+ // Build summary
1265
+ const summary = this.buildSummary(violations, applicableRules.length);
1266
+
1267
+ logger.info(
1268
+ `Best practices analysis complete: ${violations.length} violations found out of ${applicableRules.length} rules checked`
1269
+ );
1270
+
1271
+ return {
1272
+ summary,
1273
+ violations,
1274
+ recommendations: [...new Set(recommendations)], // Deduplicate
1275
+ };
1276
+ }
1277
+
1278
+ /**
1279
+ * Analyze multiple components
1280
+ */
1281
+ analyzeAll(
1282
+ configs: Array<{ component: string; config: Record<string, unknown> }>,
1283
+ options?: {
1284
+ categories?: Array<
1285
+ | 'security'
1286
+ | 'tagging'
1287
+ | 'cost'
1288
+ | 'reliability'
1289
+ | 'performance'
1290
+ | 'networking'
1291
+ | 'compliance'
1292
+ >;
1293
+ severities?: Array<'critical' | 'high' | 'medium' | 'low' | 'info'>;
1294
+ }
1295
+ ): BestPracticeReport {
1296
+ const allViolations: BestPracticeViolation[] = [];
1297
+ const allRecommendations: string[] = [];
1298
+ let totalRulesChecked = 0;
1299
+
1300
+ for (const { component, config } of configs) {
1301
+ const report = this.analyze(component, config, options);
1302
+ allViolations.push(...report.violations);
1303
+ allRecommendations.push(...report.recommendations);
1304
+ totalRulesChecked += report.summary.total_rules_checked;
1305
+ }
1306
+
1307
+ const summary = this.buildSummary(allViolations, totalRulesChecked);
1308
+
1309
+ return {
1310
+ summary,
1311
+ violations: allViolations,
1312
+ recommendations: [...new Set(allRecommendations)],
1313
+ };
1314
+ }
1315
+
1316
+ /**
1317
+ * Apply autofixes to configuration
1318
+ */
1319
+ autofix(
1320
+ component: string,
1321
+ config: Record<string, unknown>,
1322
+ options?: {
1323
+ categories?: Array<
1324
+ | 'security'
1325
+ | 'tagging'
1326
+ | 'cost'
1327
+ | 'reliability'
1328
+ | 'performance'
1329
+ | 'networking'
1330
+ | 'compliance'
1331
+ >;
1332
+ severities?: Array<'critical' | 'high' | 'medium' | 'low' | 'info'>;
1333
+ ruleIds?: string[];
1334
+ }
1335
+ ): {
1336
+ fixed_config: Record<string, unknown>;
1337
+ applied_fixes: string[];
1338
+ violations_remaining: BestPracticeViolation[];
1339
+ } {
1340
+ let fixedConfig = { ...config };
1341
+ const appliedFixes: string[] = [];
1342
+
1343
+ // Get applicable rules
1344
+ let applicableRules = this.getApplicableRules(component, options);
1345
+
1346
+ // Filter by rule IDs if specified
1347
+ if (options?.ruleIds) {
1348
+ applicableRules = applicableRules.filter(rule => options.ruleIds!.includes(rule.id));
1349
+ }
1350
+
1351
+ // Apply fixes
1352
+ for (const rule of applicableRules) {
1353
+ if (rule.autofix) {
1354
+ try {
1355
+ const passed = rule.check(fixedConfig);
1356
+ if (!passed) {
1357
+ fixedConfig = rule.autofix(fixedConfig);
1358
+ appliedFixes.push(rule.id);
1359
+ logger.debug(`Applied autofix for rule: ${rule.id}`);
1360
+ }
1361
+ } catch (error) {
1362
+ logger.error(`Error applying autofix for rule ${rule.id}`, error);
1363
+ }
1364
+ }
1365
+ }
1366
+
1367
+ // Re-analyze to find remaining violations
1368
+ const report = this.analyze(component, fixedConfig, options);
1369
+
1370
+ logger.info(
1371
+ `Applied ${appliedFixes.length} autofixes, ${report.violations.length} violations remaining`
1372
+ );
1373
+
1374
+ return {
1375
+ fixed_config: fixedConfig,
1376
+ applied_fixes: appliedFixes,
1377
+ violations_remaining: report.violations,
1378
+ };
1379
+ }
1380
+
1381
+ /**
1382
+ * Get rules by category
1383
+ */
1384
+ getRulesByCategory(
1385
+ category: 'security' | 'tagging' | 'cost' | 'reliability' | 'performance'
1386
+ ): BestPracticeRule[] {
1387
+ const ruleMap = {
1388
+ security: securityRules,
1389
+ tagging: taggingRules,
1390
+ cost: costRules,
1391
+ reliability: reliabilityRules,
1392
+ performance: performanceRules,
1393
+ };
1394
+
1395
+ return ruleMap[category] || [];
1396
+ }
1397
+
1398
+ /**
1399
+ * Get a specific rule by ID
1400
+ */
1401
+ getRule(ruleId: string): BestPracticeRule | undefined {
1402
+ return this.rules.get(ruleId);
1403
+ }
1404
+
1405
+ /**
1406
+ * List all rules
1407
+ */
1408
+ listRules(): BestPracticeRule[] {
1409
+ return Array.from(this.rules.values());
1410
+ }
1411
+
1412
+ /**
1413
+ * Add custom rule
1414
+ */
1415
+ addRule(rule: BestPracticeRule): void {
1416
+ this.rules.set(rule.id, rule);
1417
+ logger.debug(`Added custom rule: ${rule.id}`);
1418
+ }
1419
+
1420
+ /**
1421
+ * Remove rule
1422
+ */
1423
+ removeRule(ruleId: string): void {
1424
+ this.rules.delete(ruleId);
1425
+ logger.debug(`Removed rule: ${ruleId}`);
1426
+ }
1427
+
1428
+ /**
1429
+ * Get applicable rules for a component
1430
+ */
1431
+ private getApplicableRules(
1432
+ component: string,
1433
+ options?: {
1434
+ categories?: Array<
1435
+ | 'security'
1436
+ | 'tagging'
1437
+ | 'cost'
1438
+ | 'reliability'
1439
+ | 'performance'
1440
+ | 'networking'
1441
+ | 'compliance'
1442
+ >;
1443
+ severities?: Array<'critical' | 'high' | 'medium' | 'low' | 'info'>;
1444
+ includeInfo?: boolean;
1445
+ }
1446
+ ): BestPracticeRule[] {
1447
+ let rules = Array.from(this.rules.values()).filter(rule => rule.applies_to.includes(component));
1448
+
1449
+ // Filter by categories
1450
+ if (options?.categories && options.categories.length > 0) {
1451
+ rules = rules.filter(rule => options.categories!.includes(rule.category));
1452
+ }
1453
+
1454
+ // Filter by severities
1455
+ if (options?.severities && options.severities.length > 0) {
1456
+ rules = rules.filter(rule => options.severities!.includes(rule.severity));
1457
+ }
1458
+
1459
+ // Exclude info severity unless explicitly included
1460
+ if (!options?.includeInfo) {
1461
+ rules = rules.filter(rule => rule.severity !== 'info');
1462
+ }
1463
+
1464
+ return rules;
1465
+ }
1466
+
1467
+ /**
1468
+ * Build summary from violations
1469
+ */
1470
+ private buildSummary(violations: BestPracticeViolation[], totalRulesChecked: number) {
1471
+ const violationsBySeverity: Record<string, number> = {
1472
+ critical: 0,
1473
+ high: 0,
1474
+ medium: 0,
1475
+ low: 0,
1476
+ info: 0,
1477
+ };
1478
+
1479
+ const violationsByCategory: Record<string, number> = {
1480
+ security: 0,
1481
+ tagging: 0,
1482
+ cost: 0,
1483
+ reliability: 0,
1484
+ performance: 0,
1485
+ };
1486
+
1487
+ let autofixableViolations = 0;
1488
+
1489
+ for (const violation of violations) {
1490
+ violationsBySeverity[violation.severity] =
1491
+ (violationsBySeverity[violation.severity] || 0) + 1;
1492
+ violationsByCategory[violation.category] =
1493
+ (violationsByCategory[violation.category] || 0) + 1;
1494
+
1495
+ if (violation.can_autofix) {
1496
+ autofixableViolations++;
1497
+ }
1498
+ }
1499
+
1500
+ return {
1501
+ total_rules_checked: totalRulesChecked,
1502
+ violations_found: violations.length,
1503
+ violations_by_severity: violationsBySeverity,
1504
+ violations_by_category: violationsByCategory,
1505
+ autofixable_violations: autofixableViolations,
1506
+ };
1507
+ }
1508
+
1509
+ /**
1510
+ * Get compliance score (percentage of passed rules)
1511
+ */
1512
+ getComplianceScore(report: BestPracticeReport): number {
1513
+ if (report.summary.total_rules_checked === 0) {
1514
+ return 100;
1515
+ }
1516
+
1517
+ const passed = report.summary.total_rules_checked - report.summary.violations_found;
1518
+ return Math.round((passed / report.summary.total_rules_checked) * 100);
1519
+ }
1520
+
1521
+ /**
1522
+ * Get security score (based on security violations)
1523
+ */
1524
+ getSecurityScore(report: BestPracticeReport): number {
1525
+ const securityViolations = report.violations.filter(v => v.category === 'security');
1526
+ const totalSecurityRules = this.getRulesByCategory('security').length;
1527
+
1528
+ if (totalSecurityRules === 0) {
1529
+ return 100;
1530
+ }
1531
+
1532
+ const passed = totalSecurityRules - securityViolations.length;
1533
+ return Math.round((passed / totalSecurityRules) * 100);
1534
+ }
1535
+
1536
+ /**
1537
+ * Format report as markdown
1538
+ */
1539
+ formatReportAsMarkdown(report: BestPracticeReport): string {
1540
+ let markdown = '# Best Practices Report\n\n';
1541
+
1542
+ // Summary
1543
+ markdown += '## Summary\n\n';
1544
+ markdown += `- **Total Rules Checked**: ${report.summary.total_rules_checked}\n`;
1545
+ markdown += `- **Violations Found**: ${report.summary.violations_found}\n`;
1546
+ markdown += `- **Compliance Score**: ${this.getComplianceScore(report)}%\n`;
1547
+ markdown += `- **Security Score**: ${this.getSecurityScore(report)}%\n`;
1548
+ markdown += `- **Autofixable Violations**: ${report.summary.autofixable_violations}\n\n`;
1549
+
1550
+ // Violations by Severity
1551
+ markdown += '### Violations by Severity\n\n';
1552
+ Object.entries(report.summary.violations_by_severity).forEach(([severity, count]) => {
1553
+ if (count > 0) {
1554
+ markdown += `- **${severity}**: ${count}\n`;
1555
+ }
1556
+ });
1557
+ markdown += '\n';
1558
+
1559
+ // Violations by Category
1560
+ markdown += '### Violations by Category\n\n';
1561
+ Object.entries(report.summary.violations_by_category).forEach(([category, count]) => {
1562
+ if (count > 0) {
1563
+ markdown += `- **${category}**: ${count}\n`;
1564
+ }
1565
+ });
1566
+ markdown += '\n';
1567
+
1568
+ // Violations Detail
1569
+ if (report.violations.length > 0) {
1570
+ markdown += '## Violations\n\n';
1571
+
1572
+ // Group by severity
1573
+ const groupedBySeverity = report.violations.reduce(
1574
+ (acc, v) => {
1575
+ if (!acc[v.severity]) {
1576
+ acc[v.severity] = [];
1577
+ }
1578
+ acc[v.severity].push(v);
1579
+ return acc;
1580
+ },
1581
+ {} as Record<string, BestPracticeViolation[]>
1582
+ );
1583
+
1584
+ const severityOrder = ['critical', 'high', 'medium', 'low', 'info'];
1585
+
1586
+ for (const severity of severityOrder) {
1587
+ const violations = groupedBySeverity[severity];
1588
+ if (!violations || violations.length === 0) {
1589
+ continue;
1590
+ }
1591
+
1592
+ markdown += `### ${severity.toUpperCase()} Severity\n\n`;
1593
+
1594
+ for (const violation of violations) {
1595
+ markdown += `#### ${violation.title}\n\n`;
1596
+ markdown += `- **Rule ID**: ${violation.rule_id}\n`;
1597
+ markdown += `- **Category**: ${violation.category}\n`;
1598
+ markdown += `- **Component**: ${violation.component}\n`;
1599
+ markdown += `- **Description**: ${violation.description}\n`;
1600
+ markdown += `- **Recommendation**: ${violation.recommendation}\n`;
1601
+ markdown += `- **Can Autofix**: ${violation.can_autofix ? 'Yes' : 'No'}\n\n`;
1602
+ }
1603
+ }
1604
+ }
1605
+
1606
+ return markdown;
1607
+ }
1608
+ }