@iacmp/cli 1.1.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.
- package/LICENSE +21 -0
- package/bin/run.js +73 -0
- package/dist/chat.js +4068 -0
- package/dist/commands/ai.js +3985 -0
- package/dist/commands/audit-all.js +1039 -0
- package/dist/commands/audit-dr.js +351 -0
- package/dist/commands/audit-ha.js +375 -0
- package/dist/commands/audit-improvements.js +373 -0
- package/dist/commands/audit-security.js +351 -0
- package/dist/commands/dashboard.js +417 -0
- package/dist/commands/deploy.js +188 -0
- package/dist/commands/destroy.js +194 -0
- package/dist/commands/diagram.js +896 -0
- package/dist/commands/diff.js +4420 -0
- package/dist/commands/doctor.js +191 -0
- package/dist/commands/init.js +507 -0
- package/dist/commands/ls.js +75 -0
- package/dist/commands/registry.js +170 -0
- package/dist/commands/registry.json +29 -0
- package/dist/commands/synth.js +4458 -0
- package/dist/commands/watch.js +133 -0
- package/dist/index.js +30 -0
- package/oclif.manifest.json +727 -0
- package/package.json +95 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/commands/audit-security.ts
|
|
31
|
+
var audit_security_exports = {};
|
|
32
|
+
__export(audit_security_exports, {
|
|
33
|
+
default: () => AuditSecurity
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(audit_security_exports);
|
|
36
|
+
var import_core = require("@oclif/core");
|
|
37
|
+
var import_chalk = __toESM(require("chalk"));
|
|
38
|
+
|
|
39
|
+
// src/audit.ts
|
|
40
|
+
var fs2 = __toESM(require("fs"));
|
|
41
|
+
var path = __toESM(require("path"));
|
|
42
|
+
|
|
43
|
+
// src/utils.ts
|
|
44
|
+
var fs = __toESM(require("fs"));
|
|
45
|
+
function readJsonFile(filePath) {
|
|
46
|
+
let content;
|
|
47
|
+
try {
|
|
48
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
49
|
+
} catch (e) {
|
|
50
|
+
throw new Error(`Falha ao ler '${filePath}': ${errMessage(e)}`);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(content);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
throw new Error(`JSON inv\xE1lido em '${filePath}': ${errMessage(e)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function errMessage(e) {
|
|
59
|
+
if (e instanceof Error) return e.message;
|
|
60
|
+
if (typeof e === "string") return e;
|
|
61
|
+
try {
|
|
62
|
+
return JSON.stringify(e);
|
|
63
|
+
} catch {
|
|
64
|
+
return String(e);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/audit.ts
|
|
69
|
+
function readConfig(cwd) {
|
|
70
|
+
const configPath = path.join(cwd, "iacmp.json");
|
|
71
|
+
if (!fs2.existsSync(configPath)) throw new Error("iacmp.json n\xE3o encontrado. Rode: iacmp init");
|
|
72
|
+
const config = readJsonFile(configPath);
|
|
73
|
+
return {
|
|
74
|
+
name: config.name ?? path.basename(cwd),
|
|
75
|
+
provider: config.provider ?? "aws"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function resolveTsNode(projectDir) {
|
|
79
|
+
const dirs = [];
|
|
80
|
+
let dir = projectDir;
|
|
81
|
+
for (let i = 0; i < 5; i++) {
|
|
82
|
+
dirs.push(dir);
|
|
83
|
+
const parent = path.dirname(dir);
|
|
84
|
+
if (parent === dir) break;
|
|
85
|
+
dir = parent;
|
|
86
|
+
}
|
|
87
|
+
for (const d of dirs) {
|
|
88
|
+
const tsNodePath = path.join(d, "node_modules", "ts-node");
|
|
89
|
+
if (fs2.existsSync(tsNodePath)) return tsNodePath;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function findStackFiles(dir) {
|
|
94
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
95
|
+
const files = [];
|
|
96
|
+
for (const entry of entries) {
|
|
97
|
+
const full = path.join(dir, entry.name);
|
|
98
|
+
if (entry.isDirectory()) {
|
|
99
|
+
files.push(...findStackFiles(full));
|
|
100
|
+
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) {
|
|
101
|
+
files.push(full);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return files;
|
|
105
|
+
}
|
|
106
|
+
function loadStacks(cwd) {
|
|
107
|
+
const stacksDir = path.join(cwd, "stacks");
|
|
108
|
+
if (!fs2.existsSync(stacksDir)) throw new Error("Diret\xF3rio stacks/ n\xE3o encontrado.");
|
|
109
|
+
const stackFiles = findStackFiles(stacksDir);
|
|
110
|
+
if (stackFiles.length === 0) throw new Error("Nenhuma stack encontrada em stacks/");
|
|
111
|
+
const tsNodePath = resolveTsNode(cwd);
|
|
112
|
+
if (tsNodePath) {
|
|
113
|
+
require(tsNodePath).register({
|
|
114
|
+
transpileOnly: true,
|
|
115
|
+
skipProject: true,
|
|
116
|
+
compilerOptions: {
|
|
117
|
+
target: "ES2022",
|
|
118
|
+
module: "commonjs",
|
|
119
|
+
moduleResolution: "node",
|
|
120
|
+
esModuleInterop: true,
|
|
121
|
+
strict: false,
|
|
122
|
+
skipLibCheck: true
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const result = [];
|
|
127
|
+
for (const stackPath of stackFiles) {
|
|
128
|
+
const stackName = path.basename(stackPath).replace(/\.(ts|js)$/, "");
|
|
129
|
+
try {
|
|
130
|
+
const mod = require(stackPath);
|
|
131
|
+
const raw = mod.default ?? mod.stack ?? mod;
|
|
132
|
+
if (!raw || typeof raw !== "object" || !("constructs" in raw)) {
|
|
133
|
+
console.warn(`[audit] ${path.basename(stackPath)} n\xE3o exporta uma Stack v\xE1lida \u2014 ignorado.`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
result.push({ name: stackName, stack: raw });
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.warn(`[audit] falha ao carregar ${path.basename(stackPath)}: ${errMessage(err)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
function saveReport(cwd, commandName, content) {
|
|
144
|
+
const auditDir = path.join(cwd, "audit");
|
|
145
|
+
if (!fs2.existsSync(auditDir)) fs2.mkdirSync(auditDir, { recursive: true });
|
|
146
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
147
|
+
const fileName = `${commandName}-${date}.md`;
|
|
148
|
+
const filePath = path.join(auditDir, fileName);
|
|
149
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
150
|
+
return path.join("audit", fileName);
|
|
151
|
+
}
|
|
152
|
+
function today() {
|
|
153
|
+
return (/* @__PURE__ */ new Date()).toLocaleDateString("pt-BR");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/commands/audit-security.ts
|
|
157
|
+
function shouldFail(failOn, critical, warnings) {
|
|
158
|
+
if (failOn === "critical") return critical > 0;
|
|
159
|
+
if (failOn === "warning") return critical > 0 || warnings > 0;
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
function analyzeStack(stackName, stack) {
|
|
163
|
+
const findings = [];
|
|
164
|
+
const ok = [];
|
|
165
|
+
for (const c of stack.constructs) {
|
|
166
|
+
const p = c.props;
|
|
167
|
+
let hasIssue = false;
|
|
168
|
+
if (c.type === "Storage.Bucket") {
|
|
169
|
+
if (p.publicAccess === true) {
|
|
170
|
+
findings.push({
|
|
171
|
+
level: "critical",
|
|
172
|
+
stackName,
|
|
173
|
+
construct: c,
|
|
174
|
+
title: `Storage.Bucket '${c.id}' \u2014 public access enabled`,
|
|
175
|
+
problem: "publicAccess is enabled. Anyone can read/list objects in this bucket.",
|
|
176
|
+
recommendation: "Set `publicAccess: false` unless this is an intentional static website bucket."
|
|
177
|
+
});
|
|
178
|
+
hasIssue = true;
|
|
179
|
+
}
|
|
180
|
+
if (p.versioning !== true) {
|
|
181
|
+
findings.push({
|
|
182
|
+
level: "warning",
|
|
183
|
+
stackName,
|
|
184
|
+
construct: c,
|
|
185
|
+
title: `Storage.Bucket '${c.id}' \u2014 versioning disabled`,
|
|
186
|
+
problem: "Versioning is not enabled. Deleted or overwritten objects cannot be recovered.",
|
|
187
|
+
recommendation: "Set `versioning: true` to enable object rollback."
|
|
188
|
+
});
|
|
189
|
+
hasIssue = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (c.type === "Database.SQL") {
|
|
193
|
+
if (p.multiAz !== true) {
|
|
194
|
+
findings.push({
|
|
195
|
+
level: "warning",
|
|
196
|
+
stackName,
|
|
197
|
+
construct: c,
|
|
198
|
+
title: `Database.SQL '${c.id}' \u2014 no Multi-AZ`,
|
|
199
|
+
problem: "multiAz is not enabled. A failure in the availability zone will make the database unavailable.",
|
|
200
|
+
recommendation: "Set `multiAz: true` for high availability."
|
|
201
|
+
});
|
|
202
|
+
hasIssue = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (c.type === "Function.Lambda") {
|
|
206
|
+
if (p.memory === void 0 || p.memory === null) {
|
|
207
|
+
findings.push({
|
|
208
|
+
level: "warning",
|
|
209
|
+
stackName,
|
|
210
|
+
construct: c,
|
|
211
|
+
title: `Function.Lambda '${c.id}' \u2014 memory not defined`,
|
|
212
|
+
problem: "memory is not set. The function will use the provider default, which may be insufficient.",
|
|
213
|
+
recommendation: "Set `memory` explicitly (e.g. 256 or 512 MB)."
|
|
214
|
+
});
|
|
215
|
+
hasIssue = true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (c.type === "Network.VPC") {
|
|
219
|
+
if (p.cidr === void 0 || p.cidr === null) {
|
|
220
|
+
findings.push({
|
|
221
|
+
level: "warning",
|
|
222
|
+
stackName,
|
|
223
|
+
construct: c,
|
|
224
|
+
title: `Network.VPC '${c.id}' \u2014 default CIDR`,
|
|
225
|
+
problem: "cidr is not defined. The provider default CIDR may conflict with existing networks.",
|
|
226
|
+
recommendation: 'Set `cidr` explicitly (e.g. "10.0.0.0/16").'
|
|
227
|
+
});
|
|
228
|
+
hasIssue = true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (c.type === "Compute.Instance") {
|
|
232
|
+
if (p.publicAccess === true) {
|
|
233
|
+
findings.push({
|
|
234
|
+
level: "critical",
|
|
235
|
+
stackName,
|
|
236
|
+
construct: c,
|
|
237
|
+
title: `Compute.Instance '${c.id}' \u2014 public access enabled`,
|
|
238
|
+
problem: "publicAccess is enabled. The instance is directly exposed to the internet.",
|
|
239
|
+
recommendation: "Disable public access and use a load balancer or bastion host."
|
|
240
|
+
});
|
|
241
|
+
hasIssue = true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (!hasIssue) ok.push(c);
|
|
245
|
+
}
|
|
246
|
+
return { findings, ok };
|
|
247
|
+
}
|
|
248
|
+
var AuditSecurity = class _AuditSecurity extends import_core.Command {
|
|
249
|
+
static description = "Audit stacks for security issues";
|
|
250
|
+
static examples = [
|
|
251
|
+
"$ iacmp audit-security",
|
|
252
|
+
"$ iacmp audit-security --fail-on=critical",
|
|
253
|
+
"$ iacmp audit-security --fail-on=warning"
|
|
254
|
+
];
|
|
255
|
+
static flags = {
|
|
256
|
+
"fail-on": import_core.Flags.string({
|
|
257
|
+
description: "Sai com exit 1 quando h\xE1 achados no n\xEDvel indicado",
|
|
258
|
+
options: ["critical", "warning", "none"],
|
|
259
|
+
default: "none"
|
|
260
|
+
})
|
|
261
|
+
};
|
|
262
|
+
async run() {
|
|
263
|
+
const { flags } = await this.parse(_AuditSecurity);
|
|
264
|
+
const failOn = flags["fail-on"];
|
|
265
|
+
const cwd = process.cwd();
|
|
266
|
+
let config;
|
|
267
|
+
try {
|
|
268
|
+
config = readConfig(cwd);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
this.error(err.message);
|
|
271
|
+
}
|
|
272
|
+
let stacks;
|
|
273
|
+
try {
|
|
274
|
+
stacks = loadStacks(cwd);
|
|
275
|
+
} catch (err) {
|
|
276
|
+
this.error(err.message);
|
|
277
|
+
}
|
|
278
|
+
const allFindings = [];
|
|
279
|
+
const allOk = [];
|
|
280
|
+
for (const { name, stack } of stacks) {
|
|
281
|
+
const { findings, ok } = analyzeStack(name, stack);
|
|
282
|
+
allFindings.push(...findings);
|
|
283
|
+
allOk.push(...ok);
|
|
284
|
+
}
|
|
285
|
+
const critical = allFindings.filter((f) => f.level === "critical");
|
|
286
|
+
const warnings = allFindings.filter((f) => f.level === "warning");
|
|
287
|
+
this.log(import_chalk.default.bold("\nSecurity Audit"));
|
|
288
|
+
this.log("\u2500".repeat(40));
|
|
289
|
+
this.log(`Critical issues: ${critical.length > 0 ? import_chalk.default.red(critical.length) : import_chalk.default.green(0)}`);
|
|
290
|
+
this.log(`Warnings: ${warnings.length > 0 ? import_chalk.default.yellow(warnings.length) : import_chalk.default.green(0)}`);
|
|
291
|
+
this.log(`OK: ${import_chalk.default.green(allOk.length)}`);
|
|
292
|
+
this.log("");
|
|
293
|
+
for (const f of critical) {
|
|
294
|
+
this.log(`${import_chalk.default.red("\u2717 [CRITICAL]")} ${f.title}`);
|
|
295
|
+
}
|
|
296
|
+
for (const f of warnings) {
|
|
297
|
+
this.log(`${import_chalk.default.yellow("\u26A0 [WARNING]")} ${f.title}`);
|
|
298
|
+
}
|
|
299
|
+
for (const c of allOk) {
|
|
300
|
+
this.log(`${import_chalk.default.green("\u2713")} ${c.type} '${c.id}' \u2014 OK`);
|
|
301
|
+
}
|
|
302
|
+
let md = `# Security Audit Report \u2014 ${config.name}
|
|
303
|
+
`;
|
|
304
|
+
md += `Date: ${today()}
|
|
305
|
+
`;
|
|
306
|
+
md += `Provider: ${config.provider}
|
|
307
|
+
|
|
308
|
+
`;
|
|
309
|
+
md += `## Summary
|
|
310
|
+
`;
|
|
311
|
+
md += `- Critical issues: ${critical.length}
|
|
312
|
+
`;
|
|
313
|
+
md += `- Warnings: ${warnings.length}
|
|
314
|
+
`;
|
|
315
|
+
md += `- OK: ${allOk.length}
|
|
316
|
+
|
|
317
|
+
`;
|
|
318
|
+
md += `## Findings
|
|
319
|
+
|
|
320
|
+
`;
|
|
321
|
+
for (const f of allFindings) {
|
|
322
|
+
const label = f.level === "critical" ? "CRITICAL" : "WARNING";
|
|
323
|
+
md += `### [${label}] ${f.title}
|
|
324
|
+
`;
|
|
325
|
+
md += `Stack: ${f.stackName}
|
|
326
|
+
`;
|
|
327
|
+
md += `Resource: ${f.construct.id} (${f.construct.type})
|
|
328
|
+
`;
|
|
329
|
+
md += `Problem: ${f.problem}
|
|
330
|
+
`;
|
|
331
|
+
md += `Recommendation: ${f.recommendation}
|
|
332
|
+
|
|
333
|
+
`;
|
|
334
|
+
}
|
|
335
|
+
if (allOk.length > 0) {
|
|
336
|
+
md += `## Resources with no issues
|
|
337
|
+
`;
|
|
338
|
+
for (const c of allOk) {
|
|
339
|
+
md += `- ${c.type} '${c.id}' \u2014 OK
|
|
340
|
+
`;
|
|
341
|
+
}
|
|
342
|
+
md += "\n";
|
|
343
|
+
}
|
|
344
|
+
const relPath = saveReport(cwd, "security", md);
|
|
345
|
+
this.log(`
|
|
346
|
+
Report saved to ${relPath}`);
|
|
347
|
+
if (shouldFail(failOn, critical.length, warnings.length)) {
|
|
348
|
+
this.exit(1);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|