@deniscuciuc/compose-analyzer 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 (72) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +164 -0
  4. package/analyzerrc.example.json +3 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +35 -0
  8. package/dist/package.json +81 -0
  9. package/dist/src/analyzers/image-analyzer.d.ts +8 -0
  10. package/dist/src/analyzers/image-analyzer.d.ts.map +1 -0
  11. package/dist/src/analyzers/image-analyzer.js +61 -0
  12. package/dist/src/analyzers/network-analyzer.d.ts +8 -0
  13. package/dist/src/analyzers/network-analyzer.d.ts.map +1 -0
  14. package/dist/src/analyzers/network-analyzer.js +48 -0
  15. package/dist/src/analyzers/reliability-analyzer.d.ts +8 -0
  16. package/dist/src/analyzers/reliability-analyzer.d.ts.map +1 -0
  17. package/dist/src/analyzers/reliability-analyzer.js +84 -0
  18. package/dist/src/analyzers/resource-analyzer.d.ts +8 -0
  19. package/dist/src/analyzers/resource-analyzer.d.ts.map +1 -0
  20. package/dist/src/analyzers/resource-analyzer.js +34 -0
  21. package/dist/src/analyzers/security-analyzer.d.ts +9 -0
  22. package/dist/src/analyzers/security-analyzer.d.ts.map +1 -0
  23. package/dist/src/analyzers/security-analyzer.js +82 -0
  24. package/dist/src/cli/options.d.ts +16 -0
  25. package/dist/src/cli/options.d.ts.map +1 -0
  26. package/dist/src/cli/options.js +129 -0
  27. package/dist/src/cli/runner.d.ts +6 -0
  28. package/dist/src/cli/runner.d.ts.map +1 -0
  29. package/dist/src/cli/runner.js +234 -0
  30. package/dist/src/collectors/compose-collector.d.ts +19 -0
  31. package/dist/src/collectors/compose-collector.d.ts.map +1 -0
  32. package/dist/src/collectors/compose-collector.js +140 -0
  33. package/dist/src/collectors/docker-collector.d.ts +8 -0
  34. package/dist/src/collectors/docker-collector.d.ts.map +1 -0
  35. package/dist/src/collectors/docker-collector.js +96 -0
  36. package/dist/src/config/loader.d.ts +3 -0
  37. package/dist/src/config/loader.d.ts.map +1 -0
  38. package/dist/src/config/loader.js +41 -0
  39. package/dist/src/constants.d.ts +23 -0
  40. package/dist/src/constants.d.ts.map +1 -0
  41. package/dist/src/constants.js +70 -0
  42. package/dist/src/health-score.d.ts +3 -0
  43. package/dist/src/health-score.d.ts.map +1 -0
  44. package/dist/src/health-score.js +14 -0
  45. package/dist/src/interactive/display.d.ts +11 -0
  46. package/dist/src/interactive/display.d.ts.map +1 -0
  47. package/dist/src/interactive/display.js +117 -0
  48. package/dist/src/interactive/index.d.ts +15 -0
  49. package/dist/src/interactive/index.d.ts.map +1 -0
  50. package/dist/src/interactive/index.js +218 -0
  51. package/dist/src/interactive/menus.d.ts +68 -0
  52. package/dist/src/interactive/menus.d.ts.map +1 -0
  53. package/dist/src/interactive/menus.js +32 -0
  54. package/dist/src/reporters/diff-reporter.d.ts +21 -0
  55. package/dist/src/reporters/diff-reporter.d.ts.map +1 -0
  56. package/dist/src/reporters/diff-reporter.js +87 -0
  57. package/dist/src/reporters/html-reporter.d.ts +12 -0
  58. package/dist/src/reporters/html-reporter.d.ts.map +1 -0
  59. package/dist/src/reporters/html-reporter.js +226 -0
  60. package/dist/src/reporters/report-generator.d.ts +23 -0
  61. package/dist/src/reporters/report-generator.d.ts.map +1 -0
  62. package/dist/src/reporters/report-generator.js +326 -0
  63. package/dist/src/types.d.ts +198 -0
  64. package/dist/src/types.d.ts.map +1 -0
  65. package/dist/src/types.js +2 -0
  66. package/dist/src/utils/format.d.ts +6 -0
  67. package/dist/src/utils/format.d.ts.map +1 -0
  68. package/dist/src/utils/format.js +32 -0
  69. package/dist/src/utils/print.d.ts +8 -0
  70. package/dist/src/utils/print.d.ts.map +1 -0
  71. package/dist/src/utils/print.js +42 -0
  72. package/package.json +80 -0
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ReportGenerator = void 0;
37
+ const fs = __importStar(require("node:fs"));
38
+ const path = __importStar(require("node:path"));
39
+ const print_1 = require("../utils/print");
40
+ const html_reporter_1 = require("./html-reporter");
41
+ class ReportGenerator {
42
+ outputDir;
43
+ constructor(outputDir = "./reports", _options = {}) {
44
+ this.outputDir = outputDir;
45
+ }
46
+ async generateFullReport(report, timestamp) {
47
+ const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
48
+ const filename = `compose-analysis-${ts}.md`;
49
+ const filepath = path.join(this.outputDir, filename);
50
+ await this.ensureOutputDir();
51
+ fs.writeFileSync(filepath, this.buildMarkdownReport(report));
52
+ return filepath;
53
+ }
54
+ async generateJsonReport(report, timestamp) {
55
+ const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
56
+ const filename = `compose-analysis-${ts}.json`;
57
+ const filepath = path.join(this.outputDir, filename);
58
+ await this.ensureOutputDir();
59
+ fs.writeFileSync(filepath, JSON.stringify(report, null, 2));
60
+ return filepath;
61
+ }
62
+ async generateHtmlReport(report, timestamp) {
63
+ const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
64
+ const filename = `compose-analysis-${ts}.html`;
65
+ const filepath = path.join(this.outputDir, filename);
66
+ await this.ensureOutputDir();
67
+ fs.writeFileSync(filepath, html_reporter_1.HtmlReporter.generate(report));
68
+ return filepath;
69
+ }
70
+ printSummary(report) {
71
+ (0, print_1.printSection)("Compose Analysis Summary");
72
+ (0, print_1.printRow)("Health score", `${report.healthScore}/100 ${(0, print_1.healthEmoji)(report.healthScore)} ${(0, print_1.healthLabel)(report.healthScore)}`);
73
+ (0, print_1.printRow)("Compose file", report.composeFile);
74
+ (0, print_1.printRow)("Compose version", report.version ?? "not declared");
75
+ (0, print_1.printRow)("Services", report.metrics.totalServices);
76
+ (0, print_1.printRow)("Issues", report.metrics.totalIssues);
77
+ (0, print_1.printRow)("Critical / high", `${report.metrics.criticalIssues} / ${report.metrics.highIssues}`);
78
+ if (report.allIssues.length === 0) {
79
+ (0, print_1.printBullet)("No issues detected.");
80
+ return;
81
+ }
82
+ (0, print_1.printBullet)(`Top issues (${Math.min(report.allIssues.length, 5)} shown):`);
83
+ for (const issue of report.allIssues.slice(0, 5)) {
84
+ (0, print_1.printSubBullet)(`[${issue.severity}] ${issue.service} — ${issue.title}`);
85
+ }
86
+ }
87
+ async ensureOutputDir() {
88
+ if (!fs.existsSync(this.outputDir)) {
89
+ fs.mkdirSync(this.outputDir, { recursive: true });
90
+ }
91
+ }
92
+ buildMarkdownReport(report) {
93
+ return [
94
+ this.buildHeader(report),
95
+ this.buildExecutiveSummary(report),
96
+ this.buildMetricsSection(report),
97
+ this.buildIssuesSection(report),
98
+ this.buildImageSection(report),
99
+ this.buildSecuritySection(report),
100
+ this.buildReliabilitySection(report),
101
+ this.buildResourceSection(report),
102
+ this.buildNetworkSection(report),
103
+ this.buildDockerRuntimeSection(report),
104
+ this.buildRecommendationsSection(report),
105
+ ]
106
+ .filter(Boolean)
107
+ .join("\n\n");
108
+ }
109
+ buildHeader(report) {
110
+ return `# Compose Analysis Report
111
+
112
+ **Compose file:** ${report.composeFile}
113
+ **Generated:** ${new Date(report.generatedAt).toISOString()}
114
+ **Tool:** Compose Analyzer
115
+
116
+ ---`;
117
+ }
118
+ buildExecutiveSummary(report) {
119
+ const findings = [
120
+ report.metrics.criticalIssues > 0
121
+ ? `- **${report.metrics.criticalIssues}** critical issues need immediate attention`
122
+ : "- No critical issues detected",
123
+ report.metrics.highIssues > 0
124
+ ? `- **${report.metrics.highIssues}** high-severity issues should be prioritized`
125
+ : "- No high-severity issues detected",
126
+ report.images.unpinnedImages.length > 0
127
+ ? `- **${report.images.unpinnedImages.length}** services use unpinned images`
128
+ : "- All image references are pinned or build-only",
129
+ report.resources.missingLimits.length > 0
130
+ ? `- **${report.resources.missingLimits.length}** services are missing resource limits`
131
+ : "- All services define CPU and memory limits",
132
+ ];
133
+ return `## Executive Summary
134
+
135
+ ### Health Score: ${report.healthScore}/100 ${(0, print_1.healthEmoji)(report.healthScore)}
136
+
137
+ ### Key Findings
138
+ ${findings.join("\n")}
139
+
140
+ ### Quick Stats
141
+ | Metric | Value |
142
+ | --- | --- |
143
+ | Compose version | ${report.version ?? "not declared"} |
144
+ | Services | ${report.metrics.totalServices} |
145
+ | Services with build | ${report.metrics.servicesWithBuild} |
146
+ | Named volumes | ${report.metrics.namedVolumes} |
147
+ | Total issues | ${report.metrics.totalIssues} |
148
+ | Critical issues | ${report.metrics.criticalIssues} |
149
+ | High issues | ${report.metrics.highIssues} |`;
150
+ }
151
+ buildMetricsSection(report) {
152
+ return `## Metrics
153
+
154
+ | Metric | Value |
155
+ | --- | --- |
156
+ | Total services | ${report.metrics.totalServices} |
157
+ | Services with build contexts | ${report.metrics.servicesWithBuild} |
158
+ | Named volumes | ${report.metrics.namedVolumes} |
159
+ | Total issues | ${report.metrics.totalIssues} |
160
+ | Critical issues | ${report.metrics.criticalIssues} |
161
+ | High issues | ${report.metrics.highIssues} |`;
162
+ }
163
+ buildIssuesSection(report) {
164
+ if (report.allIssues.length === 0) {
165
+ return "## Issues\n\nNo issues detected.";
166
+ }
167
+ const rows = report.allIssues
168
+ .map((issue) => {
169
+ const fix = issue.fix ? issue.fix.replaceAll("\n", "<br />") : "—";
170
+ return `| ${issue.severity} | ${issue.service} | ${issue.category} | ${issue.title} | ${issue.detail} | ${fix} |`;
171
+ })
172
+ .join("\n");
173
+ return `## Issues\n\n| Severity | Service | Category | Title | Detail | Fix |
174
+ | --- | --- | --- | --- | --- | --- |
175
+ ${rows}`;
176
+ }
177
+ buildImageSection(report) {
178
+ const lines = [
179
+ `## Image Analysis`,
180
+ ``,
181
+ `### Unpinned Images (${report.images.unpinnedImages.length})`,
182
+ ];
183
+ if (report.images.unpinnedImages.length === 0) {
184
+ lines.push("No unpinned images found.");
185
+ }
186
+ else {
187
+ lines.push(`| Service | Image | Recommendation |`, `| --- | --- | --- |`);
188
+ for (const entry of report.images.unpinnedImages) {
189
+ lines.push(`| ${entry.service} | ${entry.image} | ${entry.recommendation} |`);
190
+ }
191
+ }
192
+ lines.push("", `### :latest Tags (${report.images.latestTagImages.length})`);
193
+ if (report.images.latestTagImages.length === 0) {
194
+ lines.push("No :latest tags found.");
195
+ }
196
+ else {
197
+ lines.push(`| Service | Image |`, `| --- | --- |`);
198
+ for (const entry of report.images.latestTagImages) {
199
+ lines.push(`| ${entry.service} | ${entry.image} |`);
200
+ }
201
+ }
202
+ return lines.join("\n");
203
+ }
204
+ buildSecuritySection(report) {
205
+ return [
206
+ `## Security Analysis`,
207
+ ``,
208
+ `### Secrets in Environment (${report.security.secretsInEnv.length})`,
209
+ report.security.secretsInEnv.length === 0
210
+ ? "No suspicious plain-text environment secrets found."
211
+ : [
212
+ `| Service | Variable | Pattern |`,
213
+ `| --- | --- | --- |`,
214
+ ...report.security.secretsInEnv.map((entry) => `| ${entry.service} | ${entry.variable} | ${entry.pattern} |`),
215
+ ].join("\n"),
216
+ ``,
217
+ `### Privileged Services (${report.security.privilegedServices.length})`,
218
+ report.security.privilegedServices.length === 0
219
+ ? "No privileged containers found."
220
+ : report.security.privilegedServices
221
+ .map((entry) => `- ${entry.service}`)
222
+ .join("\n"),
223
+ ``,
224
+ `### Exposed Sensitive Ports (${report.security.exposedDatabases.length})`,
225
+ report.security.exposedDatabases.length === 0
226
+ ? "No exposed database or broker ports found."
227
+ : [
228
+ `| Service | Image | Port | Bind Address |`,
229
+ `| --- | --- | --- | --- |`,
230
+ ...report.security.exposedDatabases.map((entry) => `| ${entry.service} | ${entry.image} | ${entry.port} | ${entry.bindAddr} |`),
231
+ ].join("\n"),
232
+ ].join("\n");
233
+ }
234
+ buildReliabilitySection(report) {
235
+ return [
236
+ `## Reliability Analysis`,
237
+ ``,
238
+ `### Missing Healthchecks (${report.reliability.missingHealthcheck.length})`,
239
+ report.reliability.missingHealthcheck.length === 0
240
+ ? "All services define healthchecks."
241
+ : report.reliability.missingHealthcheck
242
+ .map((entry) => `- ${entry.service}`)
243
+ .join("\n"),
244
+ ``,
245
+ `### Unsafe depends_on (${report.reliability.unsafeDepends.length})`,
246
+ report.reliability.unsafeDepends.length === 0
247
+ ? "No unsafe depends_on conditions found."
248
+ : [
249
+ `| Service | Dependency | Condition |`,
250
+ `| --- | --- | --- |`,
251
+ ...report.reliability.unsafeDepends.map((entry) => `| ${entry.service} | ${entry.dependency} | ${entry.condition} |`),
252
+ ].join("\n"),
253
+ ``,
254
+ `### Missing Restart Policies (${report.reliability.missingRestart.length})`,
255
+ report.reliability.missingRestart.length === 0
256
+ ? "All services define a restart policy."
257
+ : report.reliability.missingRestart
258
+ .map((entry) => `- ${entry.service}`)
259
+ .join("\n"),
260
+ ].join("\n");
261
+ }
262
+ buildResourceSection(report) {
263
+ return [
264
+ `## Resource Analysis`,
265
+ ``,
266
+ `### Services Missing Limits (${report.resources.missingLimits.length})`,
267
+ report.resources.missingLimits.length === 0
268
+ ? "All services define CPU and memory limits."
269
+ : [
270
+ `| Service | Missing |`,
271
+ `| --- | --- |`,
272
+ ...report.resources.missingLimits.map((entry) => `| ${entry.service} | ${entry.missing.join(", ")} |`),
273
+ ].join("\n"),
274
+ ].join("\n");
275
+ }
276
+ buildNetworkSection(report) {
277
+ return [
278
+ `## Network Analysis`,
279
+ ``,
280
+ `### Services on Default Network (${report.networks.servicesOnDefaultNetwork.length})`,
281
+ report.networks.servicesOnDefaultNetwork.length === 0
282
+ ? "No default-network usage detected."
283
+ : report.networks.servicesOnDefaultNetwork
284
+ .map((entry) => `- ${entry.service}`)
285
+ .join("\n"),
286
+ ``,
287
+ `### Random Host Bindings (${report.networks.exposedInternalPorts.length})`,
288
+ report.networks.exposedInternalPorts.length === 0
289
+ ? "No random host bindings detected."
290
+ : [
291
+ `| Service | Port | Note |`,
292
+ `| --- | --- | --- |`,
293
+ ...report.networks.exposedInternalPorts.map((entry) => `| ${entry.service} | ${entry.port} | ${entry.note} |`),
294
+ ].join("\n"),
295
+ ].join("\n");
296
+ }
297
+ buildDockerRuntimeSection(report) {
298
+ if (!report.dockerRuntime) {
299
+ return "";
300
+ }
301
+ if (!report.dockerRuntime.available) {
302
+ return `## Docker Runtime\n\n${report.dockerRuntime.error ?? "Docker runtime unavailable."}`;
303
+ }
304
+ return [
305
+ `## Docker Runtime`,
306
+ ``,
307
+ `| Service | Container | State | Status | Health | Image |`,
308
+ `| --- | --- | --- | --- | --- | --- |`,
309
+ ...report.dockerRuntime.services.map((service) => `| ${service.service} | ${service.containerName ?? "—"} | ${service.state ?? "—"} | ${service.status ?? "—"} | ${service.health ?? "—"} | ${service.image ?? "—"} |`),
310
+ ].join("\n");
311
+ }
312
+ buildRecommendationsSection(report) {
313
+ if (report.recommendations.length === 0) {
314
+ return "## Recommendations\n\nNo recommendations.";
315
+ }
316
+ return `## Recommendations\n\n${report.recommendations
317
+ .map((recommendation) => {
318
+ const fix = recommendation.fix
319
+ ? `\n - Fix: ${recommendation.fix}`
320
+ : "";
321
+ return `- **${recommendation.priority.toUpperCase()}** ${recommendation.service}: ${recommendation.message}${fix}`;
322
+ })
323
+ .join("\n")}`;
324
+ }
325
+ }
326
+ exports.ReportGenerator = ReportGenerator;
@@ -0,0 +1,198 @@
1
+ export interface ComposeService {
2
+ name?: string;
3
+ image?: string;
4
+ build?: string | {
5
+ context?: string;
6
+ dockerfile?: string;
7
+ };
8
+ ports?: Array<string | {
9
+ target: number;
10
+ published?: number | string;
11
+ host_ip?: string;
12
+ protocol?: string;
13
+ }>;
14
+ environment?: Record<string, string | null> | string[];
15
+ env_file?: string | string[];
16
+ volumes?: Array<string | {
17
+ type?: string;
18
+ source?: string;
19
+ target?: string;
20
+ }>;
21
+ networks?: string[] | Record<string, unknown>;
22
+ depends_on?: string[] | Record<string, {
23
+ condition?: string;
24
+ }>;
25
+ healthcheck?: {
26
+ test: string | string[];
27
+ interval?: string;
28
+ timeout?: string;
29
+ retries?: number;
30
+ start_period?: string;
31
+ disable?: boolean;
32
+ };
33
+ restart?: string;
34
+ deploy?: {
35
+ resources?: {
36
+ limits?: {
37
+ cpus?: string;
38
+ memory?: string;
39
+ };
40
+ reservations?: {
41
+ cpus?: string;
42
+ memory?: string;
43
+ };
44
+ };
45
+ };
46
+ mem_limit?: string;
47
+ mem_reservation?: string;
48
+ cpus?: number | string;
49
+ user?: string;
50
+ privileged?: boolean;
51
+ cap_add?: string[];
52
+ cap_drop?: string[];
53
+ secrets?: string[];
54
+ labels?: Record<string, string> | string[];
55
+ }
56
+ export interface ComposeFile {
57
+ version?: string;
58
+ services: Record<string, ComposeService>;
59
+ networks?: Record<string, unknown>;
60
+ volumes?: Record<string, unknown>;
61
+ secrets?: Record<string, unknown>;
62
+ }
63
+ export type ComposeSeverity = "critical" | "high" | "medium" | "low";
64
+ export type ComposeCategory = "image" | "security" | "reliability" | "resources" | "network";
65
+ export interface ComposeIssue {
66
+ service: string;
67
+ severity: ComposeSeverity;
68
+ category: ComposeCategory;
69
+ title: string;
70
+ detail: string;
71
+ fix?: string;
72
+ }
73
+ export interface ImageAnalysis {
74
+ unpinnedImages: {
75
+ service: string;
76
+ image: string;
77
+ recommendation: string;
78
+ }[];
79
+ latestTagImages: {
80
+ service: string;
81
+ image: string;
82
+ }[];
83
+ issues: ComposeIssue[];
84
+ }
85
+ export interface SecurityAnalysis {
86
+ secretsInEnv: {
87
+ service: string;
88
+ variable: string;
89
+ pattern: string;
90
+ }[];
91
+ privilegedServices: {
92
+ service: string;
93
+ }[];
94
+ exposedDatabases: {
95
+ service: string;
96
+ image: string;
97
+ port: string;
98
+ bindAddr: string;
99
+ }[];
100
+ issues: ComposeIssue[];
101
+ }
102
+ export interface ReliabilityAnalysis {
103
+ missingHealthcheck: {
104
+ service: string;
105
+ }[];
106
+ unsafeDepends: {
107
+ service: string;
108
+ dependency: string;
109
+ condition: string;
110
+ }[];
111
+ missingRestart: {
112
+ service: string;
113
+ }[];
114
+ insecureRestartPolicy: {
115
+ service: string;
116
+ policy: string;
117
+ }[];
118
+ issues: ComposeIssue[];
119
+ }
120
+ export interface ResourceAnalysis {
121
+ missingLimits: {
122
+ service: string;
123
+ missing: string[];
124
+ }[];
125
+ issues: ComposeIssue[];
126
+ }
127
+ export interface NetworkAnalysis {
128
+ servicesOnDefaultNetwork: {
129
+ service: string;
130
+ }[];
131
+ exposedInternalPorts: {
132
+ service: string;
133
+ port: string;
134
+ note: string;
135
+ }[];
136
+ issues: ComposeIssue[];
137
+ }
138
+ export interface ComposeMetrics {
139
+ totalServices: number;
140
+ servicesWithBuild: number;
141
+ namedVolumes: number;
142
+ totalIssues: number;
143
+ criticalIssues: number;
144
+ highIssues: number;
145
+ }
146
+ export interface Recommendation {
147
+ priority: ComposeSeverity;
148
+ service: string;
149
+ message: string;
150
+ fix?: string;
151
+ }
152
+ export interface DockerRuntimeService {
153
+ service: string;
154
+ containerName?: string;
155
+ image?: string;
156
+ state?: string;
157
+ status?: string;
158
+ health?: string;
159
+ }
160
+ export interface DockerRuntimeSummary {
161
+ available: boolean;
162
+ error?: string;
163
+ services: DockerRuntimeService[];
164
+ }
165
+ export interface FullComposeReport {
166
+ generatedAt: Date;
167
+ composeFile: string;
168
+ version?: string;
169
+ healthScore: number;
170
+ metrics: ComposeMetrics;
171
+ images: ImageAnalysis;
172
+ security: SecurityAnalysis;
173
+ reliability: ReliabilityAnalysis;
174
+ resources: ResourceAnalysis;
175
+ networks: NetworkAnalysis;
176
+ allIssues: ComposeIssue[];
177
+ recommendations: Recommendation[];
178
+ dockerRuntime?: DockerRuntimeSummary;
179
+ }
180
+ export interface NormalizedPort {
181
+ raw: string;
182
+ hostIp?: string;
183
+ published?: string;
184
+ target: string;
185
+ protocol: string;
186
+ exposesToAllInterfaces: boolean;
187
+ randomHostBinding: boolean;
188
+ }
189
+ export interface AnalyzerOptions {
190
+ outputDir?: string;
191
+ withDocker?: boolean;
192
+ quiet?: boolean;
193
+ html?: boolean;
194
+ }
195
+ export interface AnalyzerConfig {
196
+ output?: string;
197
+ }
198
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,KAAK,CAAC,EAAE,KAAK,CACV,MAAM,GACN;QACA,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACjB,CACH,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC;IACvD,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,KAAK,CACZ,MAAM,GACN;QACA,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACf,CACH,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,WAAW,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,OAAO,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE;QACR,SAAS,CAAC,EAAE;YACX,MAAM,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YAC5C,YAAY,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAClD,CAAC;KACF,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AACrE,MAAM,MAAM,eAAe,GACxB,OAAO,GACP,UAAU,GACV,aAAa,GACb,WAAW,GACX,SAAS,CAAC;AAEb,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,cAAc,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;KACvB,EAAE,CAAC;IACJ,eAAe,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvE,kBAAkB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,gBAAgB,EAAE;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;IACJ,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IACnC,kBAAkB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,aAAa,EAAE;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KAClB,EAAE,CAAC;IACJ,cAAc,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtC,qBAAqB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7D,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACxD,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC/B,wBAAwB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChD,oBAAoB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,mBAAmB,CAAC;IACjC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB,EAAE,OAAO,CAAC;IAChC,iBAAiB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ export declare function formatBytes(bytes: number): string;
2
+ export declare function formatDuration(ms: number): string;
3
+ export declare function formatMs(ms: number): string;
4
+ export declare function formatNumber(value: number): string;
5
+ export declare function formatPercent(value: number, decimals?: number): string;
6
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/utils/format.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAEjE"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatBytes = formatBytes;
4
+ exports.formatDuration = formatDuration;
5
+ exports.formatMs = formatMs;
6
+ exports.formatNumber = formatNumber;
7
+ exports.formatPercent = formatPercent;
8
+ function formatBytes(bytes) {
9
+ if (bytes < 1024)
10
+ return `${bytes} B`;
11
+ if (bytes < 1_048_576)
12
+ return `${(bytes / 1024).toFixed(2)} KB`;
13
+ if (bytes < 1_073_741_824)
14
+ return `${(bytes / 1_048_576).toFixed(2)} MB`;
15
+ return `${(bytes / 1_073_741_824).toFixed(2)} GB`;
16
+ }
17
+ function formatDuration(ms) {
18
+ if (ms < 1000)
19
+ return `${ms.toFixed(0)}ms`;
20
+ if (ms < 60_000)
21
+ return `${(ms / 1000).toFixed(2)}s`;
22
+ return `${(ms / 60_000).toFixed(2)}m`;
23
+ }
24
+ function formatMs(ms) {
25
+ return formatDuration(ms);
26
+ }
27
+ function formatNumber(value) {
28
+ return value.toLocaleString("en-US");
29
+ }
30
+ function formatPercent(value, decimals = 2) {
31
+ return `${value.toFixed(decimals)}%`;
32
+ }
@@ -0,0 +1,8 @@
1
+ export declare function printSection(title: string): void;
2
+ export declare function printRow(label: string, value: string | number): void;
3
+ export declare function printBullet(line: string): void;
4
+ export declare function printSubBullet(line: string): void;
5
+ export declare function printSeparator(): void;
6
+ export declare function healthEmoji(score: number): string;
7
+ export declare function healthLabel(score: number): string;
8
+ //# sourceMappingURL=print.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print.d.ts","sourceRoot":"","sources":["../../../src/utils/print.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printSection = printSection;
4
+ exports.printRow = printRow;
5
+ exports.printBullet = printBullet;
6
+ exports.printSubBullet = printSubBullet;
7
+ exports.printSeparator = printSeparator;
8
+ exports.healthEmoji = healthEmoji;
9
+ exports.healthLabel = healthLabel;
10
+ function printSection(title) {
11
+ console.log(`\n━━━ ${title} ━━━`);
12
+ }
13
+ function printRow(label, value) {
14
+ console.log(` ${label}: ${value}`);
15
+ }
16
+ function printBullet(line) {
17
+ console.log(` • ${line}`);
18
+ }
19
+ function printSubBullet(line) {
20
+ console.log(` ${line}`);
21
+ }
22
+ function printSeparator() {
23
+ console.log("");
24
+ }
25
+ function healthEmoji(score) {
26
+ if (score >= 90)
27
+ return "🟢";
28
+ if (score >= 70)
29
+ return "🟡";
30
+ if (score >= 50)
31
+ return "🟠";
32
+ return "🔴";
33
+ }
34
+ function healthLabel(score) {
35
+ if (score >= 90)
36
+ return "Excellent";
37
+ if (score >= 70)
38
+ return "Good";
39
+ if (score >= 50)
40
+ return "Warning";
41
+ return "Critical";
42
+ }