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
|
@@ -58,7 +58,9 @@ export async function runDoctor(options = {}) {
|
|
|
58
58
|
}
|
|
59
59
|
catch (err) {
|
|
60
60
|
return {
|
|
61
|
-
name: 'Config validation',
|
|
61
|
+
name: 'Config validation',
|
|
62
|
+
status: 'fail',
|
|
63
|
+
message: 'Invalid config',
|
|
62
64
|
detail: err instanceof Error ? err.message : String(err),
|
|
63
65
|
};
|
|
64
66
|
}
|
|
@@ -75,7 +77,11 @@ export async function runDoctor(options = {}) {
|
|
|
75
77
|
detail: hasCreds ? undefined : 'Run: aws configure',
|
|
76
78
|
};
|
|
77
79
|
}));
|
|
78
|
-
const awsCfg = {
|
|
80
|
+
const awsCfg = {
|
|
81
|
+
region: config?.aws?.region,
|
|
82
|
+
profile: config?.aws?.profile,
|
|
83
|
+
endpoint: config?.aws?.endpoint,
|
|
84
|
+
};
|
|
79
85
|
// DynamoDB
|
|
80
86
|
results.push(await runCheck('Testing DynamoDB access...', async () => {
|
|
81
87
|
if (!config)
|
|
@@ -84,11 +90,16 @@ export async function runDoctor(options = {}) {
|
|
|
84
90
|
return { name: 'DynamoDB', status: 'skip', message: 'Disabled in config' };
|
|
85
91
|
try {
|
|
86
92
|
await probeDynamoAccess(config);
|
|
87
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
name: 'DynamoDB',
|
|
95
|
+
status: 'pass',
|
|
96
|
+
message: `Connected (profile: ${config.aws?.profile ?? 'default'})`,
|
|
97
|
+
};
|
|
88
98
|
}
|
|
89
99
|
catch (err) {
|
|
90
100
|
return {
|
|
91
|
-
name: 'DynamoDB',
|
|
101
|
+
name: 'DynamoDB',
|
|
102
|
+
status: 'warn',
|
|
92
103
|
message: err instanceof Error ? err.message : String(err),
|
|
93
104
|
detail: 'Check IAM: dynamodb:ListTables, dynamodb:DescribeTable',
|
|
94
105
|
};
|
|
@@ -104,7 +115,8 @@ export async function runDoctor(options = {}) {
|
|
|
104
115
|
}
|
|
105
116
|
catch (err) {
|
|
106
117
|
return {
|
|
107
|
-
name: 'SQS',
|
|
118
|
+
name: 'SQS',
|
|
119
|
+
status: 'warn',
|
|
108
120
|
message: err instanceof Error ? err.message : String(err),
|
|
109
121
|
detail: 'Check IAM: sqs:ListQueues, sqs:GetQueueAttributes',
|
|
110
122
|
};
|
|
@@ -120,7 +132,8 @@ export async function runDoctor(options = {}) {
|
|
|
120
132
|
}
|
|
121
133
|
catch (err) {
|
|
122
134
|
return {
|
|
123
|
-
name: 'SNS',
|
|
135
|
+
name: 'SNS',
|
|
136
|
+
status: 'warn',
|
|
124
137
|
message: err instanceof Error ? err.message : String(err),
|
|
125
138
|
detail: 'Check IAM: sns:ListTopics, sns:GetTopicAttributes, sns:ListSubscriptionsByTopic',
|
|
126
139
|
};
|
|
@@ -136,7 +149,8 @@ export async function runDoctor(options = {}) {
|
|
|
136
149
|
}
|
|
137
150
|
catch (err) {
|
|
138
151
|
return {
|
|
139
|
-
name: 'SSM',
|
|
152
|
+
name: 'SSM',
|
|
153
|
+
status: 'warn',
|
|
140
154
|
message: err instanceof Error ? err.message : String(err),
|
|
141
155
|
detail: 'Check IAM: ssm:DescribeParameters',
|
|
142
156
|
};
|
|
@@ -148,11 +162,16 @@ export async function runDoctor(options = {}) {
|
|
|
148
162
|
return { name: 'Secrets Manager', status: 'skip', message: 'Disabled in config' };
|
|
149
163
|
try {
|
|
150
164
|
await validateSecretsAccess(awsCfg);
|
|
151
|
-
return {
|
|
165
|
+
return {
|
|
166
|
+
name: 'Secrets Manager',
|
|
167
|
+
status: 'pass',
|
|
168
|
+
message: 'Connected (names/rotation only)',
|
|
169
|
+
};
|
|
152
170
|
}
|
|
153
171
|
catch (err) {
|
|
154
172
|
return {
|
|
155
|
-
name: 'Secrets Manager',
|
|
173
|
+
name: 'Secrets Manager',
|
|
174
|
+
status: 'warn',
|
|
156
175
|
message: err instanceof Error ? err.message : String(err),
|
|
157
176
|
detail: 'Check IAM: secretsmanager:ListSecrets',
|
|
158
177
|
};
|
|
@@ -168,7 +187,8 @@ export async function runDoctor(options = {}) {
|
|
|
168
187
|
}
|
|
169
188
|
catch (err) {
|
|
170
189
|
return {
|
|
171
|
-
name: 'Lambda',
|
|
190
|
+
name: 'Lambda',
|
|
191
|
+
status: 'warn',
|
|
172
192
|
message: err instanceof Error ? err.message : String(err),
|
|
173
193
|
detail: 'Check IAM: lambda:ListFunctions',
|
|
174
194
|
};
|
|
@@ -184,7 +204,8 @@ export async function runDoctor(options = {}) {
|
|
|
184
204
|
}
|
|
185
205
|
catch (err) {
|
|
186
206
|
return {
|
|
187
|
-
name: 'EventBridge',
|
|
207
|
+
name: 'EventBridge',
|
|
208
|
+
status: 'warn',
|
|
188
209
|
message: err instanceof Error ? err.message : String(err),
|
|
189
210
|
detail: 'Check IAM: events:ListRules, events:ListTargetsByRule',
|
|
190
211
|
};
|
|
@@ -200,7 +221,8 @@ export async function runDoctor(options = {}) {
|
|
|
200
221
|
}
|
|
201
222
|
catch (err) {
|
|
202
223
|
return {
|
|
203
|
-
name: 'S3',
|
|
224
|
+
name: 'S3',
|
|
225
|
+
status: 'warn',
|
|
204
226
|
message: err instanceof Error ? err.message : String(err),
|
|
205
227
|
detail: 'Check IAM: s3:ListBuckets, s3:GetBucketNotificationConfiguration, s3:GetBucketVersioning, s3:GetBucketEncryption, s3:GetPublicAccessBlock',
|
|
206
228
|
};
|
|
@@ -216,7 +238,8 @@ export async function runDoctor(options = {}) {
|
|
|
216
238
|
}
|
|
217
239
|
catch (err) {
|
|
218
240
|
return {
|
|
219
|
-
name: 'CloudWatch Logs',
|
|
241
|
+
name: 'CloudWatch Logs',
|
|
242
|
+
status: 'warn',
|
|
220
243
|
message: err instanceof Error ? err.message : String(err),
|
|
221
244
|
detail: 'Check IAM: logs:DescribeLogGroups, logs:FilterLogEvents',
|
|
222
245
|
};
|
|
@@ -230,13 +253,18 @@ export async function runDoctor(options = {}) {
|
|
|
230
253
|
try {
|
|
231
254
|
const ok = await validatePostgresAccess(config.postgres.connectionString);
|
|
232
255
|
return {
|
|
233
|
-
name: 'PostgreSQL',
|
|
256
|
+
name: 'PostgreSQL',
|
|
257
|
+
status: ok ? 'pass' : 'fail',
|
|
234
258
|
message: ok ? 'Connected' : 'Cannot connect',
|
|
235
259
|
detail: ok ? undefined : 'Check connection string and security group',
|
|
236
260
|
};
|
|
237
261
|
}
|
|
238
262
|
catch (err) {
|
|
239
|
-
return {
|
|
263
|
+
return {
|
|
264
|
+
name: 'PostgreSQL',
|
|
265
|
+
status: 'fail',
|
|
266
|
+
message: err instanceof Error ? err.message : String(err),
|
|
267
|
+
};
|
|
240
268
|
}
|
|
241
269
|
}));
|
|
242
270
|
// MySQL
|
|
@@ -247,13 +275,18 @@ export async function runDoctor(options = {}) {
|
|
|
247
275
|
try {
|
|
248
276
|
const ok = await validateMySQLAccess(config.mysql.connectionString);
|
|
249
277
|
return {
|
|
250
|
-
name: 'MySQL',
|
|
278
|
+
name: 'MySQL',
|
|
279
|
+
status: ok ? 'pass' : 'fail',
|
|
251
280
|
message: ok ? 'Connected' : 'Cannot connect',
|
|
252
281
|
detail: ok ? undefined : 'Check connection string, host, port 3306',
|
|
253
282
|
};
|
|
254
283
|
}
|
|
255
284
|
catch (err) {
|
|
256
|
-
return {
|
|
285
|
+
return {
|
|
286
|
+
name: 'MySQL',
|
|
287
|
+
status: 'fail',
|
|
288
|
+
message: err instanceof Error ? err.message : String(err),
|
|
289
|
+
};
|
|
257
290
|
}
|
|
258
291
|
}));
|
|
259
292
|
// MongoDB
|
|
@@ -264,13 +297,18 @@ export async function runDoctor(options = {}) {
|
|
|
264
297
|
try {
|
|
265
298
|
const ok = await validateMongoAccess(config.mongodb.connectionString);
|
|
266
299
|
return {
|
|
267
|
-
name: 'MongoDB',
|
|
300
|
+
name: 'MongoDB',
|
|
301
|
+
status: ok ? 'pass' : 'fail',
|
|
268
302
|
message: ok ? 'Connected' : 'Cannot connect',
|
|
269
303
|
detail: ok ? undefined : 'Check connection string and port 27017',
|
|
270
304
|
};
|
|
271
305
|
}
|
|
272
306
|
catch (err) {
|
|
273
|
-
return {
|
|
307
|
+
return {
|
|
308
|
+
name: 'MongoDB',
|
|
309
|
+
status: 'fail',
|
|
310
|
+
message: err instanceof Error ? err.message : String(err),
|
|
311
|
+
};
|
|
274
312
|
}
|
|
275
313
|
}));
|
|
276
314
|
// Project type
|
|
@@ -288,11 +326,13 @@ export async function runDoctor(options = {}) {
|
|
|
288
326
|
results.push(await runCheck('Detecting IaC files...', async () => {
|
|
289
327
|
const cwd = process.cwd();
|
|
290
328
|
const hasTfFile = (dir) => fs.existsSync(dir) && fs.readdirSync(dir).some((f) => f.endsWith('.tf'));
|
|
291
|
-
const hasTF = hasTfFile(cwd) ||
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
329
|
+
const hasTF = hasTfFile(cwd) ||
|
|
330
|
+
fs.readdirSync(cwd).some((entry) => {
|
|
331
|
+
const full = path.join(cwd, entry);
|
|
332
|
+
return fs.statSync(full).isDirectory() && hasTfFile(full);
|
|
333
|
+
});
|
|
334
|
+
const hasCFN = fs.existsSync(path.join(cwd, 'template.yaml')) ||
|
|
335
|
+
fs.existsSync(path.join(cwd, 'template.json'));
|
|
296
336
|
const hasCDK = fs.existsSync(path.join(cwd, 'cdk.json'));
|
|
297
337
|
const hasCDKOut = fs.existsSync(path.join(cwd, 'cdk.out'));
|
|
298
338
|
const found = [];
|
|
@@ -184,24 +184,42 @@ export async function runInit(options = {}) {
|
|
|
184
184
|
]);
|
|
185
185
|
// ── Build config ───────────────────────────────────────────────────────────
|
|
186
186
|
const includeTables = services.dynamoTables
|
|
187
|
-
? services.dynamoTables
|
|
187
|
+
? services.dynamoTables
|
|
188
|
+
.split(',')
|
|
189
|
+
.map((t) => t.trim())
|
|
190
|
+
.filter(Boolean)
|
|
188
191
|
: [];
|
|
189
192
|
const ssmPaths = services.ssmPaths
|
|
190
|
-
? services.ssmPaths
|
|
193
|
+
? services.ssmPaths
|
|
194
|
+
.split(',')
|
|
195
|
+
.map((p) => p.trim())
|
|
196
|
+
.filter(Boolean)
|
|
191
197
|
: [];
|
|
192
198
|
const logGroupPrefixes = services.logGroupPrefixes
|
|
193
|
-
? services.logGroupPrefixes
|
|
199
|
+
? services.logGroupPrefixes
|
|
200
|
+
.split(',')
|
|
201
|
+
.map((p) => p.trim())
|
|
202
|
+
.filter(Boolean)
|
|
194
203
|
: [];
|
|
195
204
|
const isLocalStack = core.awsProfile === '__localstack__';
|
|
196
205
|
const isEnvVars = core.awsProfile === '__env__';
|
|
197
206
|
const resolvedProfile = isLocalStack || isEnvVars ? '' : core.awsProfile;
|
|
198
|
-
const resolvedEndpoint = isLocalStack ?
|
|
207
|
+
const resolvedEndpoint = isLocalStack ? core.endpoint || 'http://localhost:4566' : undefined;
|
|
199
208
|
const configContent = generateDefaultConfig(core.project, {
|
|
200
209
|
aws: { profile: resolvedProfile, region: core.region, endpoint: resolvedEndpoint },
|
|
201
210
|
dynamodb: { enabled: services.dynamoEnabled, includeTables },
|
|
202
|
-
postgres: {
|
|
203
|
-
|
|
204
|
-
|
|
211
|
+
postgres: {
|
|
212
|
+
enabled: databases.pgEnabled,
|
|
213
|
+
connectionString: databases.pgConnectionString ?? '',
|
|
214
|
+
},
|
|
215
|
+
mysql: {
|
|
216
|
+
enabled: databases.mysqlEnabled,
|
|
217
|
+
connectionString: databases.mysqlConnectionString ?? '',
|
|
218
|
+
},
|
|
219
|
+
mongodb: {
|
|
220
|
+
enabled: databases.mongoEnabled,
|
|
221
|
+
connectionString: databases.mongoConnectionString ?? '',
|
|
222
|
+
},
|
|
205
223
|
sqs: { enabled: services.sqsEnabled },
|
|
206
224
|
sns: { enabled: services.snsEnabled },
|
|
207
225
|
ssm: { enabled: services.ssmEnabled, paths: ssmPaths },
|
package/dist/cli/utils.js
CHANGED
|
@@ -90,10 +90,14 @@ export const log = {
|
|
|
90
90
|
// ─── Findings ────────────────────────────────────────────────────────────────
|
|
91
91
|
function severityBadge(severity) {
|
|
92
92
|
switch (severity) {
|
|
93
|
-
case 'high':
|
|
94
|
-
|
|
95
|
-
case '
|
|
96
|
-
|
|
93
|
+
case 'high':
|
|
94
|
+
return chalk.bgRed.white.bold(` HIGH `);
|
|
95
|
+
case 'medium':
|
|
96
|
+
return chalk.bgYellow.black.bold(` MED `);
|
|
97
|
+
case 'low':
|
|
98
|
+
return chalk.bgCyan.black.bold(` LOW `);
|
|
99
|
+
case 'verify':
|
|
100
|
+
return chalk.bgBlue.white.bold(` VER? `);
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
export function printFinding(finding, index) {
|
|
@@ -112,11 +116,21 @@ export function printSummaryBox(findings) {
|
|
|
112
116
|
console.log(chalk.dim(' ┌─────────────────────────────┐'));
|
|
113
117
|
console.log(chalk.dim(' │') + chalk.bold(' Analysis Summary ') + chalk.dim('│'));
|
|
114
118
|
console.log(chalk.dim(' ├─────────────────────────────┤'));
|
|
115
|
-
console.log(chalk.dim(' │') +
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.log(chalk.dim(' │') +
|
|
119
|
+
console.log(chalk.dim(' │') +
|
|
120
|
+
` ${chalk.red('●')} High ${chalk.red.bold(String(high).padStart(3))} ` +
|
|
121
|
+
chalk.dim('│'));
|
|
122
|
+
console.log(chalk.dim(' │') +
|
|
123
|
+
` ${chalk.yellow('●')} Medium ${chalk.yellow.bold(String(medium).padStart(3))} ` +
|
|
124
|
+
chalk.dim('│'));
|
|
125
|
+
console.log(chalk.dim(' │') +
|
|
126
|
+
` ${chalk.cyan('●')} Low ${chalk.cyan.bold(String(low).padStart(3))} ` +
|
|
127
|
+
chalk.dim('│'));
|
|
128
|
+
console.log(chalk.dim(' │') +
|
|
129
|
+
` ${chalk.blue('●')} Verify ${chalk.blue.bold(String(verify).padStart(3))} ` +
|
|
130
|
+
chalk.dim('│'));
|
|
119
131
|
console.log(chalk.dim(' ├─────────────────────────────┤'));
|
|
120
|
-
console.log(chalk.dim(' │') +
|
|
132
|
+
console.log(chalk.dim(' │') +
|
|
133
|
+
` Total ${chalk.bold(String(findings.length).padStart(3))} ` +
|
|
134
|
+
chalk.dim('│'));
|
|
121
135
|
console.log(chalk.dim(' └─────────────────────────────┘'));
|
|
122
136
|
}
|
package/dist/context/index.js
CHANGED
|
@@ -23,7 +23,16 @@ const DYNAMO_OPERATIONS = new Set([
|
|
|
23
23
|
]);
|
|
24
24
|
const DYNAMO_CLIENT_PATTERNS = ['DynamoDBClient', 'DynamoDB', 'dynamoDB', 'dynamo', 'ddb'];
|
|
25
25
|
const POSTGRES_QUERY_METHODS = new Set(['query', 'execute', 'exec']);
|
|
26
|
-
const KNEX_METHODS = new Set([
|
|
26
|
+
const KNEX_METHODS = new Set([
|
|
27
|
+
'select',
|
|
28
|
+
'where',
|
|
29
|
+
'join',
|
|
30
|
+
'from',
|
|
31
|
+
'insert',
|
|
32
|
+
'update',
|
|
33
|
+
'delete',
|
|
34
|
+
'del',
|
|
35
|
+
]);
|
|
27
36
|
// MySQL-specific patterns
|
|
28
37
|
const MYSQL_QUERY_METHODS = new Set(['query', 'execute', 'exec']);
|
|
29
38
|
const MYSQL_CLIENT_PATTERNS = ['mysql', 'mysql2', 'connection', 'pool', 'conn'];
|
|
@@ -44,14 +53,29 @@ const MONGO_READ_METHODS = new Set([
|
|
|
44
53
|
]);
|
|
45
54
|
const MONGO_COLLECTION_METHODS = new Set(['collection']);
|
|
46
55
|
// ── AWS service patterns ──────────────────────────────────────────────────────
|
|
47
|
-
const SQS_COMMANDS = new Set([
|
|
56
|
+
const SQS_COMMANDS = new Set([
|
|
57
|
+
'SendMessageCommand',
|
|
58
|
+
'SendMessageBatchCommand',
|
|
59
|
+
'ReceiveMessageCommand',
|
|
60
|
+
'DeleteMessageCommand',
|
|
61
|
+
'sendMessage',
|
|
62
|
+
'sendMessageBatch',
|
|
63
|
+
'receiveMessage',
|
|
64
|
+
]);
|
|
48
65
|
const SNS_COMMANDS = new Set(['PublishCommand', 'PublishBatchCommand', 'publish', 'publishBatch']);
|
|
49
66
|
// kafkajs patterns — detection relies on variable naming (producer/consumer/kafka)
|
|
50
67
|
const KAFKA_PRODUCER_METHODS = new Set(['send', 'sendBatch']);
|
|
51
68
|
const KAFKA_CONSUMER_METHODS = new Set(['subscribe']);
|
|
52
69
|
const KAFKA_CLIENT_PATTERNS = ['kafka', 'producer', 'consumer'];
|
|
53
70
|
const KAFKA_TOPIC_KEYS = ['topic'];
|
|
54
|
-
const SSM_COMMANDS = new Set([
|
|
71
|
+
const SSM_COMMANDS = new Set([
|
|
72
|
+
'GetParameterCommand',
|
|
73
|
+
'GetParametersCommand',
|
|
74
|
+
'GetParametersByPathCommand',
|
|
75
|
+
'getParameter',
|
|
76
|
+
'getParameters',
|
|
77
|
+
'getParametersByPath',
|
|
78
|
+
]);
|
|
55
79
|
const SECRETS_COMMANDS = new Set(['GetSecretValueCommand', 'getSecretValue']);
|
|
56
80
|
const LAMBDA_COMMANDS = new Set(['InvokeCommand', 'InvokeAsyncCommand', 'invoke', 'invokeAsync']);
|
|
57
81
|
const SQS_ARG_KEYS = ['QueueUrl', 'QueueName'];
|
|
@@ -247,9 +271,7 @@ function detectPostgresOperations(callExpr, filePath) {
|
|
|
247
271
|
if (KNEX_METHODS.has(methodName)) {
|
|
248
272
|
const calleeText = objExpr.getText();
|
|
249
273
|
// Check if it's a chained knex call
|
|
250
|
-
if (calleeText.includes('knex') ||
|
|
251
|
-
calleeText.includes('db(') ||
|
|
252
|
-
calleeText.includes('trx(')) {
|
|
274
|
+
if (calleeText.includes('knex') || calleeText.includes('db(') || calleeText.includes('trx(')) {
|
|
253
275
|
return {
|
|
254
276
|
functionName: getEnclosingFunctionName(callExpr),
|
|
255
277
|
operationType: methodName,
|
|
@@ -261,7 +283,10 @@ function detectPostgresOperations(callExpr, filePath) {
|
|
|
261
283
|
// Knex call expression style: knex('users').select(...)
|
|
262
284
|
if (Node.isCallExpression(objExpr)) {
|
|
263
285
|
const innerExpr = objExpr.getExpression();
|
|
264
|
-
if (innerExpr
|
|
286
|
+
if (innerExpr
|
|
287
|
+
.getText()
|
|
288
|
+
.toLowerCase()
|
|
289
|
+
.match(/knex|db|trx/)) {
|
|
265
290
|
const innerArgs = objExpr.getArguments();
|
|
266
291
|
const tableName = innerArgs.length > 0 && Node.isStringLiteral(innerArgs[0])
|
|
267
292
|
? innerArgs[0].getLiteralValue()
|
|
@@ -513,7 +538,8 @@ function detectAWSServiceOperations(callExpr, filePath) {
|
|
|
513
538
|
filePath,
|
|
514
539
|
};
|
|
515
540
|
}
|
|
516
|
-
if (SSM_COMMANDS.has(methodName) &&
|
|
541
|
+
if (SSM_COMMANDS.has(methodName) &&
|
|
542
|
+
(objText.includes('ssm') || objText.includes('parameter'))) {
|
|
517
543
|
return {
|
|
518
544
|
functionName: getEnclosingFunctionName(callExpr),
|
|
519
545
|
operationType: methodName,
|
|
@@ -522,7 +548,8 @@ function detectAWSServiceOperations(callExpr, filePath) {
|
|
|
522
548
|
filePath,
|
|
523
549
|
};
|
|
524
550
|
}
|
|
525
|
-
if (SECRETS_COMMANDS.has(methodName) &&
|
|
551
|
+
if (SECRETS_COMMANDS.has(methodName) &&
|
|
552
|
+
(objText.includes('secret') || objText.includes('secrets'))) {
|
|
526
553
|
return {
|
|
527
554
|
functionName: getEnclosingFunctionName(callExpr),
|
|
528
555
|
operationType: methodName,
|
package/dist/core/cache.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
const CACHE_VERSION = '1.0.0';
|
|
4
|
-
|
|
4
|
+
let cacheDir = path.join(process.cwd(), '.infrawise', 'cache');
|
|
5
|
+
export function setCacheDir(dir) {
|
|
6
|
+
cacheDir = path.join(dir, '.infrawise', 'cache');
|
|
7
|
+
}
|
|
5
8
|
function ensureCacheDir() {
|
|
6
|
-
if (!fs.existsSync(
|
|
7
|
-
fs.mkdirSync(
|
|
9
|
+
if (!fs.existsSync(cacheDir)) {
|
|
10
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
8
11
|
}
|
|
9
12
|
}
|
|
10
13
|
export function writeCache(key, data) {
|
|
@@ -14,11 +17,11 @@ export function writeCache(key, data) {
|
|
|
14
17
|
data,
|
|
15
18
|
version: CACHE_VERSION,
|
|
16
19
|
};
|
|
17
|
-
const filePath = path.join(
|
|
20
|
+
const filePath = path.join(cacheDir, `${key}.json`);
|
|
18
21
|
fs.writeFileSync(filePath, JSON.stringify(entry, null, 2), 'utf-8');
|
|
19
22
|
}
|
|
20
23
|
export function readCache(key, maxAgeMs = 3600000) {
|
|
21
|
-
const filePath = path.join(
|
|
24
|
+
const filePath = path.join(cacheDir, `${key}.json`);
|
|
22
25
|
if (!fs.existsSync(filePath))
|
|
23
26
|
return null;
|
|
24
27
|
try {
|
|
@@ -36,15 +39,15 @@ export function readCache(key, maxAgeMs = 3600000) {
|
|
|
36
39
|
}
|
|
37
40
|
export function clearCache(key) {
|
|
38
41
|
if (key) {
|
|
39
|
-
const filePath = path.join(
|
|
42
|
+
const filePath = path.join(cacheDir, `${key}.json`);
|
|
40
43
|
if (fs.existsSync(filePath))
|
|
41
44
|
fs.unlinkSync(filePath);
|
|
42
45
|
}
|
|
43
46
|
else {
|
|
44
|
-
if (fs.existsSync(
|
|
45
|
-
const files = fs.readdirSync(
|
|
47
|
+
if (fs.existsSync(cacheDir)) {
|
|
48
|
+
const files = fs.readdirSync(cacheDir);
|
|
46
49
|
for (const file of files) {
|
|
47
|
-
fs.unlinkSync(path.join(
|
|
50
|
+
fs.unlinkSync(path.join(cacheDir, file));
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
}
|
package/dist/core/config.js
CHANGED
|
@@ -12,44 +12,63 @@ export const InfrawiseConfigSchema = z.object({
|
|
|
12
12
|
})
|
|
13
13
|
.optional()
|
|
14
14
|
.default({ profile: 'default', region: 'us-east-1' }),
|
|
15
|
-
dynamodb: z
|
|
16
|
-
|
|
15
|
+
dynamodb: z
|
|
16
|
+
.object({
|
|
17
|
+
enabled: z.boolean().optional().default(true),
|
|
18
|
+
includeTables: z.array(z.string()).optional(),
|
|
19
|
+
})
|
|
20
|
+
.optional(),
|
|
21
|
+
postgres: z
|
|
22
|
+
.object({
|
|
17
23
|
enabled: z.boolean().optional().default(false),
|
|
18
24
|
connectionString: z.string().optional(),
|
|
19
|
-
})
|
|
20
|
-
|
|
25
|
+
})
|
|
26
|
+
.optional(),
|
|
27
|
+
mysql: z
|
|
28
|
+
.object({
|
|
21
29
|
enabled: z.boolean().optional().default(false),
|
|
22
30
|
connectionString: z.string().optional(),
|
|
23
|
-
})
|
|
24
|
-
|
|
31
|
+
})
|
|
32
|
+
.optional(),
|
|
33
|
+
mongodb: z
|
|
34
|
+
.object({
|
|
25
35
|
enabled: z.boolean().optional().default(false),
|
|
26
36
|
connectionString: z.string().optional(),
|
|
27
37
|
databases: z.array(z.string()).optional(),
|
|
28
|
-
})
|
|
38
|
+
})
|
|
39
|
+
.optional(),
|
|
29
40
|
terraform: z.object({ enabled: z.boolean().optional().default(true) }).optional(),
|
|
30
41
|
sqs: z.object({ enabled: z.boolean().optional().default(true) }).optional(),
|
|
31
42
|
sns: z.object({ enabled: z.boolean().optional().default(true) }).optional(),
|
|
32
|
-
ssm: z
|
|
43
|
+
ssm: z
|
|
44
|
+
.object({
|
|
33
45
|
enabled: z.boolean().optional().default(true),
|
|
34
46
|
paths: z.array(z.string()).optional(),
|
|
35
|
-
})
|
|
47
|
+
})
|
|
48
|
+
.optional(),
|
|
36
49
|
secretsManager: z.object({ enabled: z.boolean().optional().default(true) }).optional(),
|
|
37
|
-
lambda: z
|
|
50
|
+
lambda: z
|
|
51
|
+
.object({
|
|
38
52
|
enabled: z.boolean().optional().default(true),
|
|
39
53
|
includeFunctions: z.array(z.string()).optional(),
|
|
40
|
-
})
|
|
54
|
+
})
|
|
55
|
+
.optional(),
|
|
41
56
|
eventbridge: z.object({ enabled: z.boolean().optional().default(true) }).optional(),
|
|
42
57
|
rds: z.object({ enabled: z.boolean().optional().default(false) }).optional(),
|
|
43
58
|
s3: z.object({ enabled: z.boolean().optional().default(false) }).optional(),
|
|
44
59
|
kafka: z.object({ enabled: z.boolean().optional().default(false) }).optional(),
|
|
45
|
-
cloudwatchLogs: z
|
|
60
|
+
cloudwatchLogs: z
|
|
61
|
+
.object({
|
|
46
62
|
enabled: z.boolean().optional().default(false),
|
|
47
63
|
logGroupPrefixes: z.array(z.string()).optional(),
|
|
48
64
|
windowHours: z.number().int().positive().optional().default(24),
|
|
49
|
-
})
|
|
50
|
-
|
|
65
|
+
})
|
|
66
|
+
.optional(),
|
|
67
|
+
analysis: z
|
|
68
|
+
.object({
|
|
51
69
|
sampleSize: z.number().int().positive().optional().default(100),
|
|
52
|
-
})
|
|
70
|
+
})
|
|
71
|
+
.optional(),
|
|
53
72
|
});
|
|
54
73
|
export class ConfigError extends Error {
|
|
55
74
|
details;
|
|
@@ -103,7 +122,10 @@ export function generateDefaultConfig(projectName, options) {
|
|
|
103
122
|
region: options?.aws?.region ?? 'us-east-1',
|
|
104
123
|
...(options?.aws?.endpoint ? { endpoint: options.aws.endpoint } : {}),
|
|
105
124
|
},
|
|
106
|
-
dynamodb: {
|
|
125
|
+
dynamodb: {
|
|
126
|
+
enabled: options?.dynamodb?.enabled ?? true,
|
|
127
|
+
includeTables: options?.dynamodb?.includeTables ?? [],
|
|
128
|
+
},
|
|
107
129
|
postgres: {
|
|
108
130
|
enabled: options?.postgres?.enabled ?? false,
|
|
109
131
|
connectionString: options?.postgres?.connectionString ?? '',
|
package/dist/core/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { loadConfig, generateDefaultConfig, InfrawiseConfigSchema, ConfigError as ConfigValidationError } from './config.js';
|
|
1
|
+
export { loadConfig, generateDefaultConfig, InfrawiseConfigSchema, ConfigError as ConfigValidationError, } from './config.js';
|
|
2
2
|
export { logger } from './logger.js';
|
|
3
3
|
export { InfrawiseError, AWSConnectionError, DynamoDBError, PostgresConnectionError, RepositoryScanError, ConfigError, formatError, } from './errors.js';
|
|
4
|
-
export { writeCache, readCache, clearCache } from './cache.js';
|
|
4
|
+
export { writeCache, readCache, clearCache, setCacheDir } from './cache.js';
|