infrawise 0.8.2 โ 0.8.7
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/README.md +1 -47
- package/dist/adapters/aws/logs.js +3 -1
- package/dist/adapters/aws/s3.js +37 -7
- package/dist/adapters/aws/services.js +28 -9
- package/dist/adapters/db/mongodb.js +5 -4
- package/dist/adapters/db/mysql.js +4 -1
- package/dist/adapters/iac/terraform.js +52 -25
- package/dist/analyzers/aws-services.js +5 -1
- package/dist/analyzers/rds.js +10 -2
- package/dist/analyzers/terraform.js +18 -3
- package/dist/cli/commands/analyze.js +161 -107
- package/dist/cli/commands/auth.js +4 -1
- package/dist/cli/commands/dev.js +6 -3
- package/dist/cli/commands/doctor.js +64 -24
- package/dist/cli/commands/init.js +25 -7
- package/dist/cli/utils.js +23 -9
- package/dist/context/index.js +36 -9
- package/dist/core/cache.js +12 -9
- package/dist/core/config.js +38 -16
- package/dist/core/index.js +2 -2
- package/dist/graph/index.js +63 -9
- package/dist/server/index.js +170 -51
- package/package.json +18 -5
|
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import ora from 'ora';
|
|
5
|
-
import { loadConfig, formatError, writeCache, readCache } from '../../core/index.js';
|
|
5
|
+
import { loadConfig, formatError, writeCache, readCache, setCacheDir } from '../../core/index.js';
|
|
6
6
|
import { extractDynamoMetadata } from '../../adapters/aws/dynamodb.js';
|
|
7
7
|
import { extractPostgresMetadata } from '../../adapters/db/postgres.js';
|
|
8
8
|
import { extractMySQLMetadata } from '../../adapters/db/mysql.js';
|
|
@@ -39,7 +39,9 @@ function buildMarkdownReport(findings, projectName) {
|
|
|
39
39
|
renderGroup('Verify (check intent)', '๐ต', verify),
|
|
40
40
|
'',
|
|
41
41
|
'_Generated by [infrawise](https://github.com/Sidd27/infrawise) โ MCP server for AWS infrastructure analysis_',
|
|
42
|
-
]
|
|
42
|
+
]
|
|
43
|
+
.filter((l) => l !== undefined)
|
|
44
|
+
.join('\n');
|
|
43
45
|
}
|
|
44
46
|
function mkSpinner(text) {
|
|
45
47
|
return ora({ text: chalk.dim(text), color: 'cyan' }).start();
|
|
@@ -49,6 +51,7 @@ export async function runAnalyze(options = {}) {
|
|
|
49
51
|
let config;
|
|
50
52
|
try {
|
|
51
53
|
config = loadConfig(options.config);
|
|
54
|
+
setCacheDir(path.dirname(path.resolve(options.config ?? 'infrawise.yaml')));
|
|
52
55
|
log.success('Config loaded', options.config ?? 'infrawise.yaml');
|
|
53
56
|
}
|
|
54
57
|
catch (err) {
|
|
@@ -56,8 +59,12 @@ export async function runAnalyze(options = {}) {
|
|
|
56
59
|
process.exit(1);
|
|
57
60
|
}
|
|
58
61
|
const repoPath = options.repo ?? process.cwd();
|
|
59
|
-
const minSeverity = options.severity ? SEVERITY_ORDER[options.severity] ?? 1 : 0;
|
|
60
|
-
const awsCfg = {
|
|
62
|
+
const minSeverity = options.severity ? (SEVERITY_ORDER[options.severity] ?? 1) : 0;
|
|
63
|
+
const awsCfg = {
|
|
64
|
+
region: config.aws?.region,
|
|
65
|
+
profile: config.aws?.profile,
|
|
66
|
+
endpoint: config.aws?.endpoint,
|
|
67
|
+
};
|
|
61
68
|
const dynamoMeta = [];
|
|
62
69
|
const postgresMeta = [];
|
|
63
70
|
const mysqlMeta = [];
|
|
@@ -72,7 +79,8 @@ export async function runAnalyze(options = {}) {
|
|
|
72
79
|
s.succeed(chalk.green('DynamoDB') + chalk.dim(` ${result.length} table(s)`));
|
|
73
80
|
}
|
|
74
81
|
catch (err) {
|
|
75
|
-
s.warn(chalk.yellow('DynamoDB skipped') +
|
|
82
|
+
s.warn(chalk.yellow('DynamoDB skipped') +
|
|
83
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
76
84
|
}
|
|
77
85
|
}
|
|
78
86
|
// โโ PostgreSQL โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -84,7 +92,8 @@ export async function runAnalyze(options = {}) {
|
|
|
84
92
|
s.succeed(chalk.green('PostgreSQL') + chalk.dim(` ${result.length} table(s)`));
|
|
85
93
|
}
|
|
86
94
|
catch (err) {
|
|
87
|
-
s.warn(chalk.yellow('PostgreSQL skipped') +
|
|
95
|
+
s.warn(chalk.yellow('PostgreSQL skipped') +
|
|
96
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
88
97
|
}
|
|
89
98
|
}
|
|
90
99
|
// โโ MySQL โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -96,7 +105,8 @@ export async function runAnalyze(options = {}) {
|
|
|
96
105
|
s.succeed(chalk.green('MySQL') + chalk.dim(` ${result.length} table(s)`));
|
|
97
106
|
}
|
|
98
107
|
catch (err) {
|
|
99
|
-
s.warn(chalk.yellow('MySQL skipped') +
|
|
108
|
+
s.warn(chalk.yellow('MySQL skipped') +
|
|
109
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
112
|
// โโ MongoDB โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -108,7 +118,8 @@ export async function runAnalyze(options = {}) {
|
|
|
108
118
|
s.succeed(chalk.green('MongoDB') + chalk.dim(` ${result.length} collection(s)`));
|
|
109
119
|
}
|
|
110
120
|
catch (err) {
|
|
111
|
-
s.warn(chalk.yellow('MongoDB skipped') +
|
|
121
|
+
s.warn(chalk.yellow('MongoDB skipped') +
|
|
122
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
112
123
|
}
|
|
113
124
|
}
|
|
114
125
|
// โโ SQS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -120,7 +131,8 @@ export async function runAnalyze(options = {}) {
|
|
|
120
131
|
s.succeed(chalk.green('SQS') + chalk.dim(` ${result.length} queue(s)`));
|
|
121
132
|
}
|
|
122
133
|
catch (err) {
|
|
123
|
-
s.warn(chalk.yellow('SQS skipped') +
|
|
134
|
+
s.warn(chalk.yellow('SQS skipped') +
|
|
135
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
138
|
// โโ SNS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -132,7 +144,8 @@ export async function runAnalyze(options = {}) {
|
|
|
132
144
|
s.succeed(chalk.green('SNS') + chalk.dim(` ${result.length} topic(s)`));
|
|
133
145
|
}
|
|
134
146
|
catch (err) {
|
|
135
|
-
s.warn(chalk.yellow('SNS skipped') +
|
|
147
|
+
s.warn(chalk.yellow('SNS skipped') +
|
|
148
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
136
149
|
}
|
|
137
150
|
}
|
|
138
151
|
// โโ SSM Parameter Store โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -141,10 +154,13 @@ export async function runAnalyze(options = {}) {
|
|
|
141
154
|
try {
|
|
142
155
|
const result = await extractSSMMetadata({ ...awsCfg, paths: config.ssm?.paths });
|
|
143
156
|
servicesMeta.ssm = result;
|
|
144
|
-
s.succeed(chalk.green('SSM') +
|
|
157
|
+
s.succeed(chalk.green('SSM') +
|
|
158
|
+
chalk.dim(` ${result.length} parameter(s) `) +
|
|
159
|
+
chalk.dim('(metadata only, no values)'));
|
|
145
160
|
}
|
|
146
161
|
catch (err) {
|
|
147
|
-
s.warn(chalk.yellow('SSM skipped') +
|
|
162
|
+
s.warn(chalk.yellow('SSM skipped') +
|
|
163
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
148
164
|
}
|
|
149
165
|
}
|
|
150
166
|
// โโ Secrets Manager โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -153,10 +169,13 @@ export async function runAnalyze(options = {}) {
|
|
|
153
169
|
try {
|
|
154
170
|
const result = await extractSecretsMetadata(awsCfg);
|
|
155
171
|
servicesMeta.secrets = result;
|
|
156
|
-
s.succeed(chalk.green('Secrets Manager') +
|
|
172
|
+
s.succeed(chalk.green('Secrets Manager') +
|
|
173
|
+
chalk.dim(` ${result.length} secret(s) `) +
|
|
174
|
+
chalk.dim('(names/rotation only, no values)'));
|
|
157
175
|
}
|
|
158
176
|
catch (err) {
|
|
159
|
-
s.warn(chalk.yellow('Secrets Manager skipped') +
|
|
177
|
+
s.warn(chalk.yellow('Secrets Manager skipped') +
|
|
178
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
181
|
// โโ Lambda โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -168,7 +187,8 @@ export async function runAnalyze(options = {}) {
|
|
|
168
187
|
s.succeed(chalk.green('Lambda') + chalk.dim(` ${result.length} function(s)`));
|
|
169
188
|
}
|
|
170
189
|
catch (err) {
|
|
171
|
-
s.warn(chalk.yellow('Lambda skipped') +
|
|
190
|
+
s.warn(chalk.yellow('Lambda skipped') +
|
|
191
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
172
192
|
}
|
|
173
193
|
}
|
|
174
194
|
// โโ EventBridge โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -180,7 +200,8 @@ export async function runAnalyze(options = {}) {
|
|
|
180
200
|
s.succeed(chalk.green('EventBridge') + chalk.dim(` ${result.length} rule(s)`));
|
|
181
201
|
}
|
|
182
202
|
catch (err) {
|
|
183
|
-
s.warn(chalk.yellow('EventBridge skipped') +
|
|
203
|
+
s.warn(chalk.yellow('EventBridge skipped') +
|
|
204
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
184
205
|
}
|
|
185
206
|
}
|
|
186
207
|
// โโ RDS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -192,7 +213,8 @@ export async function runAnalyze(options = {}) {
|
|
|
192
213
|
s.succeed(chalk.green('RDS') + chalk.dim(` ${result.length} instance(s)`));
|
|
193
214
|
}
|
|
194
215
|
catch (err) {
|
|
195
|
-
s.warn(chalk.yellow('RDS skipped') +
|
|
216
|
+
s.warn(chalk.yellow('RDS skipped') +
|
|
217
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
196
218
|
}
|
|
197
219
|
}
|
|
198
220
|
// โโ S3 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -204,7 +226,8 @@ export async function runAnalyze(options = {}) {
|
|
|
204
226
|
s.succeed(chalk.green('S3') + chalk.dim(` ${result.length} bucket(s)`));
|
|
205
227
|
}
|
|
206
228
|
catch (err) {
|
|
207
|
-
s.warn(chalk.yellow('S3 skipped') +
|
|
229
|
+
s.warn(chalk.yellow('S3 skipped') +
|
|
230
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
208
231
|
}
|
|
209
232
|
}
|
|
210
233
|
// โโ CloudWatch Logs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -218,10 +241,12 @@ export async function runAnalyze(options = {}) {
|
|
|
218
241
|
});
|
|
219
242
|
servicesMeta.logs = result;
|
|
220
243
|
const errorGroups = result.filter((lg) => lg.errorCount > 0).length;
|
|
221
|
-
s.succeed(chalk.green('CloudWatch Logs') +
|
|
244
|
+
s.succeed(chalk.green('CloudWatch Logs') +
|
|
245
|
+
chalk.dim(` ${result.length} group(s), ${errorGroups} with errors`));
|
|
222
246
|
}
|
|
223
247
|
catch (err) {
|
|
224
|
-
s.warn(chalk.yellow('CloudWatch Logs skipped') +
|
|
248
|
+
s.warn(chalk.yellow('CloudWatch Logs skipped') +
|
|
249
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
225
250
|
}
|
|
226
251
|
}
|
|
227
252
|
// โโ IaC schema (Terraform / CloudFormation / CDK) โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -230,16 +255,23 @@ export async function runAnalyze(options = {}) {
|
|
|
230
255
|
const s = mkSpinner('Extracting IaC schema (Terraform / CloudFormation / CDK)...');
|
|
231
256
|
try {
|
|
232
257
|
const iacSchema = await extractIaCSchema(repoPath);
|
|
233
|
-
const total = iacSchema.dynamoTables.length +
|
|
234
|
-
iacSchema.
|
|
235
|
-
iacSchema.
|
|
236
|
-
iacSchema.
|
|
258
|
+
const total = iacSchema.dynamoTables.length +
|
|
259
|
+
iacSchema.rdsInstances.length +
|
|
260
|
+
iacSchema.mongoClusters.length +
|
|
261
|
+
iacSchema.queues.length +
|
|
262
|
+
iacSchema.topics.length +
|
|
263
|
+
iacSchema.lambdas.length +
|
|
264
|
+
iacSchema.buckets.length +
|
|
265
|
+
iacSchema.parameters.length +
|
|
266
|
+
iacSchema.secrets.length +
|
|
267
|
+
iacSchema.apiGateways.length;
|
|
237
268
|
iacDriftAnalyzer = new IaCDriftAnalyzer();
|
|
238
269
|
iacDriftAnalyzer.setIaCSchema(iacSchema);
|
|
239
270
|
s.succeed(chalk.green('IaC schema') + chalk.dim(` ${total} resource(s) across TF/CFN/CDK`));
|
|
240
271
|
}
|
|
241
272
|
catch (err) {
|
|
242
|
-
s.warn(chalk.yellow('IaC scan skipped') +
|
|
273
|
+
s.warn(chalk.yellow('IaC scan skipped') +
|
|
274
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
243
275
|
}
|
|
244
276
|
}
|
|
245
277
|
// โโ Repository scan โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -248,10 +280,12 @@ export async function runAnalyze(options = {}) {
|
|
|
248
280
|
const s = mkSpinner(`Scanning ${path.basename(repoPath)} for service usage...`);
|
|
249
281
|
try {
|
|
250
282
|
operations = await scanRepository(repoPath);
|
|
251
|
-
s.succeed(chalk.green('Repository scanned') +
|
|
283
|
+
s.succeed(chalk.green('Repository scanned') +
|
|
284
|
+
chalk.dim(` ${operations.length} service operation(s) found`));
|
|
252
285
|
}
|
|
253
286
|
catch (err) {
|
|
254
|
-
s.warn(chalk.yellow('Repository scan failed') +
|
|
287
|
+
s.warn(chalk.yellow('Repository scan failed') +
|
|
288
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
255
289
|
operations = [];
|
|
256
290
|
}
|
|
257
291
|
}
|
|
@@ -260,76 +294,85 @@ export async function runAnalyze(options = {}) {
|
|
|
260
294
|
{
|
|
261
295
|
const s = mkSpinner('Building infrastructure graph...');
|
|
262
296
|
graph = buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta, mongoMeta, servicesMeta);
|
|
263
|
-
s.succeed(chalk.green('Graph built') +
|
|
297
|
+
s.succeed(chalk.green('Graph built') +
|
|
298
|
+
chalk.dim(` ${graph.nodes.length} nodes, ${graph.edges.length} edges`));
|
|
264
299
|
}
|
|
265
300
|
// โโ Run analyzers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
266
301
|
let findings;
|
|
267
302
|
{
|
|
268
303
|
const s = mkSpinner('Running analyzers...');
|
|
269
304
|
const analyzers = [
|
|
270
|
-
...(config.dynamodb?.enabled === true
|
|
271
|
-
new FullTableScanAnalyzer(),
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
new
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
new S3UnencryptedAnalyzer(),
|
|
315
|
-
] : []),
|
|
305
|
+
...(config.dynamodb?.enabled === true
|
|
306
|
+
? [new FullTableScanAnalyzer(), new MissingGSIAnalyzer(), new HotPartitionAnalyzer()]
|
|
307
|
+
: []),
|
|
308
|
+
...(config.postgres?.enabled
|
|
309
|
+
? [new MissingIndexAnalyzer(), new NplusOneAnalyzer(), new LargeSelectAnalyzer()]
|
|
310
|
+
: []),
|
|
311
|
+
...(config.mysql?.enabled
|
|
312
|
+
? [new MissingMySQLIndexAnalyzer(), new MySQLFullTableScanAnalyzer()]
|
|
313
|
+
: []),
|
|
314
|
+
...(config.mongodb?.enabled
|
|
315
|
+
? [new MissingMongoIndexAnalyzer(), new MongoCollectionScanAnalyzer()]
|
|
316
|
+
: []),
|
|
317
|
+
...(config.sqs?.enabled === true
|
|
318
|
+
? [
|
|
319
|
+
new MissingDLQAnalyzer(),
|
|
320
|
+
new UnencryptedQueueAnalyzer(),
|
|
321
|
+
new LargeQueueBacklogAnalyzer(),
|
|
322
|
+
]
|
|
323
|
+
: []),
|
|
324
|
+
...(config.secretsManager?.enabled === true ? [new MissingSecretRotationAnalyzer()] : []),
|
|
325
|
+
...(config.cloudwatchLogs?.enabled ? [new MissingLogRetentionAnalyzer()] : []),
|
|
326
|
+
...(config.lambda?.enabled === true
|
|
327
|
+
? [
|
|
328
|
+
new LambdaDefaultMemoryAnalyzer(),
|
|
329
|
+
new LambdaHighTimeoutAnalyzer(),
|
|
330
|
+
new LambdaMissingTriggerDLQAnalyzer(),
|
|
331
|
+
]
|
|
332
|
+
: []),
|
|
333
|
+
...(config.rds?.enabled === true
|
|
334
|
+
? [
|
|
335
|
+
new RDSPubliclyAccessibleAnalyzer(),
|
|
336
|
+
new RDSNoBackupAnalyzer(),
|
|
337
|
+
new RDSUnencryptedAnalyzer(),
|
|
338
|
+
new RDSNoDeletionProtectionAnalyzer(),
|
|
339
|
+
new RDSNoMultiAZAnalyzer(),
|
|
340
|
+
]
|
|
341
|
+
: []),
|
|
342
|
+
...(config.s3?.enabled === true
|
|
343
|
+
? [
|
|
344
|
+
new S3PublicAccessAnalyzer(),
|
|
345
|
+
new S3MissingVersioningAnalyzer(),
|
|
346
|
+
new S3UnencryptedAnalyzer(),
|
|
347
|
+
]
|
|
348
|
+
: []),
|
|
316
349
|
...(iacDriftAnalyzer ? [iacDriftAnalyzer] : []),
|
|
317
350
|
];
|
|
318
351
|
findings = await runAllAnalyzers(graph, analyzers);
|
|
319
352
|
s.succeed(chalk.green('Analysis complete') + chalk.dim(` ${findings.length} finding(s)`));
|
|
320
353
|
}
|
|
321
354
|
// โโ Cache โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
355
|
+
if (!options.noCache) {
|
|
356
|
+
writeCache('graph', graph);
|
|
357
|
+
writeCache('findings', findings);
|
|
358
|
+
writeCache('operations', operations);
|
|
359
|
+
writeCache('meta', {
|
|
360
|
+
dynamoMeta,
|
|
361
|
+
postgresMeta,
|
|
362
|
+
mysqlMeta,
|
|
363
|
+
mongoMeta,
|
|
364
|
+
servicesMeta,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
326
367
|
// โโ Output โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
327
368
|
const displayFindings = minSeverity > 0
|
|
328
369
|
? findings.filter((f) => (SEVERITY_ORDER[f.severity] ?? 0) >= minSeverity)
|
|
329
370
|
: findings;
|
|
330
371
|
console.log('');
|
|
331
372
|
if (displayFindings.length === 0) {
|
|
332
|
-
const msg = minSeverity > 0
|
|
373
|
+
const msg = minSeverity > 0
|
|
374
|
+
? `No ${options.severity} (or higher) severity issues found.`
|
|
375
|
+
: 'Your infrastructure looks clean.';
|
|
333
376
|
console.log(` ${chalk.green.bold('โ No issues found!')} ${chalk.dim(msg)}`);
|
|
334
377
|
}
|
|
335
378
|
else {
|
|
@@ -354,7 +397,7 @@ export async function runAnalyze(options = {}) {
|
|
|
354
397
|
console.log('');
|
|
355
398
|
}
|
|
356
399
|
export async function runCodeRefresh(repoPath, config) {
|
|
357
|
-
const cached = readCache('meta',
|
|
400
|
+
const cached = readCache('meta', 60 * 60 * 1000);
|
|
358
401
|
const dynamoMeta = cached?.dynamoMeta ?? [];
|
|
359
402
|
const postgresMeta = cached?.postgresMeta ?? [];
|
|
360
403
|
const mysqlMeta = cached?.mysqlMeta ?? [];
|
|
@@ -380,35 +423,46 @@ export async function runCodeRefresh(repoPath, config) {
|
|
|
380
423
|
}
|
|
381
424
|
const graph = buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta, mongoMeta, servicesMeta);
|
|
382
425
|
const analyzers = [
|
|
383
|
-
...(config.dynamodb?.enabled === true
|
|
384
|
-
new FullTableScanAnalyzer(), new MissingGSIAnalyzer(), new HotPartitionAnalyzer()
|
|
385
|
-
|
|
386
|
-
...(config.postgres?.enabled
|
|
387
|
-
new MissingIndexAnalyzer(), new NplusOneAnalyzer(), new LargeSelectAnalyzer()
|
|
388
|
-
|
|
389
|
-
...(config.mysql?.enabled
|
|
390
|
-
new MissingMySQLIndexAnalyzer(), new MySQLFullTableScanAnalyzer()
|
|
391
|
-
|
|
392
|
-
...(config.mongodb?.enabled
|
|
393
|
-
new MissingMongoIndexAnalyzer(), new MongoCollectionScanAnalyzer()
|
|
394
|
-
|
|
395
|
-
...(config.sqs?.enabled === true
|
|
396
|
-
new MissingDLQAnalyzer(), new UnencryptedQueueAnalyzer(), new LargeQueueBacklogAnalyzer()
|
|
397
|
-
|
|
426
|
+
...(config.dynamodb?.enabled === true
|
|
427
|
+
? [new FullTableScanAnalyzer(), new MissingGSIAnalyzer(), new HotPartitionAnalyzer()]
|
|
428
|
+
: []),
|
|
429
|
+
...(config.postgres?.enabled
|
|
430
|
+
? [new MissingIndexAnalyzer(), new NplusOneAnalyzer(), new LargeSelectAnalyzer()]
|
|
431
|
+
: []),
|
|
432
|
+
...(config.mysql?.enabled
|
|
433
|
+
? [new MissingMySQLIndexAnalyzer(), new MySQLFullTableScanAnalyzer()]
|
|
434
|
+
: []),
|
|
435
|
+
...(config.mongodb?.enabled
|
|
436
|
+
? [new MissingMongoIndexAnalyzer(), new MongoCollectionScanAnalyzer()]
|
|
437
|
+
: []),
|
|
438
|
+
...(config.sqs?.enabled === true
|
|
439
|
+
? [new MissingDLQAnalyzer(), new UnencryptedQueueAnalyzer(), new LargeQueueBacklogAnalyzer()]
|
|
440
|
+
: []),
|
|
398
441
|
...(config.secretsManager?.enabled === true ? [new MissingSecretRotationAnalyzer()] : []),
|
|
399
442
|
...(config.cloudwatchLogs?.enabled ? [new MissingLogRetentionAnalyzer()] : []),
|
|
400
|
-
...(config.lambda?.enabled === true
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
...(config.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
443
|
+
...(config.lambda?.enabled === true
|
|
444
|
+
? [
|
|
445
|
+
new LambdaDefaultMemoryAnalyzer(),
|
|
446
|
+
new LambdaHighTimeoutAnalyzer(),
|
|
447
|
+
new LambdaMissingTriggerDLQAnalyzer(),
|
|
448
|
+
]
|
|
449
|
+
: []),
|
|
450
|
+
...(config.rds?.enabled === true
|
|
451
|
+
? [
|
|
452
|
+
new RDSPubliclyAccessibleAnalyzer(),
|
|
453
|
+
new RDSNoBackupAnalyzer(),
|
|
454
|
+
new RDSUnencryptedAnalyzer(),
|
|
455
|
+
new RDSNoDeletionProtectionAnalyzer(),
|
|
456
|
+
new RDSNoMultiAZAnalyzer(),
|
|
457
|
+
]
|
|
458
|
+
: []),
|
|
459
|
+
...(config.s3?.enabled === true
|
|
460
|
+
? [
|
|
461
|
+
new S3PublicAccessAnalyzer(),
|
|
462
|
+
new S3MissingVersioningAnalyzer(),
|
|
463
|
+
new S3UnencryptedAnalyzer(),
|
|
464
|
+
]
|
|
465
|
+
: []),
|
|
412
466
|
...(iacDriftAnalyzer ? [iacDriftAnalyzer] : []),
|
|
413
467
|
];
|
|
414
468
|
const findings = await runAllAnalyzers(graph, analyzers);
|
|
@@ -25,7 +25,10 @@ export async function runAuth() {
|
|
|
25
25
|
},
|
|
26
26
|
]);
|
|
27
27
|
console.log('');
|
|
28
|
-
const spin = ora({
|
|
28
|
+
const spin = ora({
|
|
29
|
+
text: chalk.dim(`Validating "${selectedProfile}"...`),
|
|
30
|
+
color: 'cyan',
|
|
31
|
+
}).start();
|
|
29
32
|
const testConfig = {
|
|
30
33
|
project: 'auth-test',
|
|
31
34
|
aws: { profile: selectedProfile, region: 'us-east-1' },
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import ora from 'ora';
|
|
5
|
-
import { loadConfig, formatError, readCache } from '../../core/index.js';
|
|
5
|
+
import { loadConfig, formatError, readCache, setCacheDir } from '../../core/index.js';
|
|
6
6
|
import { createServer, setGraphState } from '../../server/index.js';
|
|
7
7
|
import { log, printHeader } from '../utils.js';
|
|
8
8
|
import { runAnalyze, runCodeRefresh } from './analyze.js';
|
|
@@ -59,6 +59,7 @@ export async function runDev(options = {}) {
|
|
|
59
59
|
let config;
|
|
60
60
|
try {
|
|
61
61
|
config = loadConfig(options.config);
|
|
62
|
+
setCacheDir(path.dirname(path.resolve(options.config ?? 'infrawise.yaml')));
|
|
62
63
|
log.success('Config loaded', options.config ?? 'infrawise.yaml');
|
|
63
64
|
}
|
|
64
65
|
catch (err) {
|
|
@@ -147,10 +148,12 @@ export async function runDev(options = {}) {
|
|
|
147
148
|
try {
|
|
148
149
|
const { graph, findings } = await runCodeRefresh(repoPath, config);
|
|
149
150
|
setGraphState(graph, findings, config);
|
|
150
|
-
spin.succeed(chalk.green('Analysis refreshed') +
|
|
151
|
+
spin.succeed(chalk.green('Analysis refreshed') +
|
|
152
|
+
chalk.dim(` ${graph.nodes.length} nodes ยท ${findings.length} finding(s)`));
|
|
151
153
|
}
|
|
152
154
|
catch (err) {
|
|
153
|
-
spin.warn(chalk.yellow('Refresh failed') +
|
|
155
|
+
spin.warn(chalk.yellow('Refresh failed') +
|
|
156
|
+
chalk.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
154
157
|
}
|
|
155
158
|
finally {
|
|
156
159
|
refreshing = false;
|