infrawise 0.8.2 → 0.8.3

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.
@@ -184,24 +184,42 @@ export async function runInit(options = {}) {
184
184
  ]);
185
185
  // ── Build config ───────────────────────────────────────────────────────────
186
186
  const includeTables = services.dynamoTables
187
- ? services.dynamoTables.split(',').map((t) => t.trim()).filter(Boolean)
187
+ ? services.dynamoTables
188
+ .split(',')
189
+ .map((t) => t.trim())
190
+ .filter(Boolean)
188
191
  : [];
189
192
  const ssmPaths = services.ssmPaths
190
- ? services.ssmPaths.split(',').map((p) => p.trim()).filter(Boolean)
193
+ ? services.ssmPaths
194
+ .split(',')
195
+ .map((p) => p.trim())
196
+ .filter(Boolean)
191
197
  : [];
192
198
  const logGroupPrefixes = services.logGroupPrefixes
193
- ? services.logGroupPrefixes.split(',').map((p) => p.trim()).filter(Boolean)
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 ? (core.endpoint || 'http://localhost:4566') : undefined;
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: { enabled: databases.pgEnabled, connectionString: databases.pgConnectionString ?? '' },
203
- mysql: { enabled: databases.mysqlEnabled, connectionString: databases.mysqlConnectionString ?? '' },
204
- mongodb: { enabled: databases.mongoEnabled, connectionString: databases.mongoConnectionString ?? '' },
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': return chalk.bgRed.white.bold(` HIGH `);
94
- case 'medium': return chalk.bgYellow.black.bold(` MED `);
95
- case 'low': return chalk.bgCyan.black.bold(` LOW `);
96
- case 'verify': return chalk.bgBlue.white.bold(` VER? `);
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(' │') + ` ${chalk.red('●')} High ${chalk.red.bold(String(high).padStart(3))} ` + chalk.dim('│'));
116
- console.log(chalk.dim(' │') + ` ${chalk.yellow('●')} Medium ${chalk.yellow.bold(String(medium).padStart(3))} ` + chalk.dim('│'));
117
- console.log(chalk.dim(' │') + ` ${chalk.cyan('●')} Low ${chalk.cyan.bold(String(low).padStart(3))} ` + chalk.dim('│'));
118
- console.log(chalk.dim(' │') + ` ${chalk.blue('●')} Verify ${chalk.blue.bold(String(verify).padStart(3))} ` + 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(' │') + ` Total ${chalk.bold(String(findings.length).padStart(3))} ` + 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
  }
@@ -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(['select', 'where', 'join', 'from', 'insert', 'update', 'delete', 'del']);
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(['SendMessageCommand', 'SendMessageBatchCommand', 'ReceiveMessageCommand', 'DeleteMessageCommand', 'sendMessage', 'sendMessageBatch', 'receiveMessage']);
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(['GetParameterCommand', 'GetParametersCommand', 'GetParametersByPathCommand', 'getParameter', 'getParameters', 'getParametersByPath']);
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.getText().toLowerCase().match(/knex|db|trx/)) {
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) && (objText.includes('ssm') || objText.includes('parameter'))) {
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) && (objText.includes('secret') || objText.includes('secrets'))) {
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,
@@ -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.object({ enabled: z.boolean().optional().default(true), includeTables: z.array(z.string()).optional() }).optional(),
16
- postgres: z.object({
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
- }).optional(),
20
- mysql: z.object({
25
+ })
26
+ .optional(),
27
+ mysql: z
28
+ .object({
21
29
  enabled: z.boolean().optional().default(false),
22
30
  connectionString: z.string().optional(),
23
- }).optional(),
24
- mongodb: z.object({
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
- }).optional(),
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.object({
43
+ ssm: z
44
+ .object({
33
45
  enabled: z.boolean().optional().default(true),
34
46
  paths: z.array(z.string()).optional(),
35
- }).optional(),
47
+ })
48
+ .optional(),
36
49
  secretsManager: z.object({ enabled: z.boolean().optional().default(true) }).optional(),
37
- lambda: z.object({
50
+ lambda: z
51
+ .object({
38
52
  enabled: z.boolean().optional().default(true),
39
53
  includeFunctions: z.array(z.string()).optional(),
40
- }).optional(),
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.object({
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
- }).optional(),
50
- analysis: z.object({
65
+ })
66
+ .optional(),
67
+ analysis: z
68
+ .object({
51
69
  sampleSize: z.number().int().positive().optional().default(100),
52
- }).optional(),
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: { enabled: options?.dynamodb?.enabled ?? true, includeTables: options?.dynamodb?.includeTables ?? [] },
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 ?? '',
@@ -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
4
  export { writeCache, readCache, clearCache } from './cache.js';
@@ -22,7 +22,12 @@ export function buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta = [],
22
22
  }
23
23
  for (const table of postgresMeta) {
24
24
  const nodeId = `table:postgres:${table.schema}.${table.table}`;
25
- addNode({ id: nodeId, type: 'table', name: `${table.schema}.${table.table}`, databaseType: 'postgres' });
25
+ addNode({
26
+ id: nodeId,
27
+ type: 'table',
28
+ name: `${table.schema}.${table.table}`,
29
+ databaseType: 'postgres',
30
+ });
26
31
  for (const indexName of table.indexes) {
27
32
  const indexNodeId = `index:${table.schema}.${table.table}:${indexName}`;
28
33
  addNode({ id: indexNodeId, type: 'index', name: indexName });
@@ -31,7 +36,12 @@ export function buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta = [],
31
36
  }
32
37
  for (const table of mysqlMeta) {
33
38
  const nodeId = `table:mysql:${table.schema}.${table.table}`;
34
- addNode({ id: nodeId, type: 'table', name: `${table.schema}.${table.table}`, databaseType: 'mysql' });
39
+ addNode({
40
+ id: nodeId,
41
+ type: 'table',
42
+ name: `${table.schema}.${table.table}`,
43
+ databaseType: 'mysql',
44
+ });
35
45
  for (const indexName of table.indexes) {
36
46
  const indexNodeId = `index:${table.schema}.${table.table}:${indexName}`;
37
47
  addNode({ id: indexNodeId, type: 'index', name: indexName });
@@ -40,7 +50,12 @@ export function buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta = [],
40
50
  }
41
51
  for (const coll of mongoMeta) {
42
52
  const nodeId = `table:mongodb:${coll.database}.${coll.collection}`;
43
- addNode({ id: nodeId, type: 'table', name: `${coll.database}.${coll.collection}`, databaseType: 'mongodb' });
53
+ addNode({
54
+ id: nodeId,
55
+ type: 'table',
56
+ name: `${coll.database}.${coll.collection}`,
57
+ databaseType: 'mongodb',
58
+ });
44
59
  for (const idx of coll.indexes) {
45
60
  if (idx.name === '_id_')
46
61
  continue;
@@ -122,15 +137,34 @@ export function buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta = [],
122
137
  let sourceId;
123
138
  if (trigger.type === 'sqs') {
124
139
  sourceId = `queue:aws:${trigger.sourceName}`;
125
- addNode({ id: sourceId, type: 'queue', name: trigger.sourceName, provider: 'aws', hasDLQ: false, encrypted: false });
140
+ addNode({
141
+ id: sourceId,
142
+ type: 'queue',
143
+ name: trigger.sourceName,
144
+ provider: 'aws',
145
+ hasDLQ: false,
146
+ encrypted: false,
147
+ });
126
148
  }
127
149
  else if (trigger.type === 'dynamodb') {
128
150
  sourceId = `table:dynamo:${trigger.sourceName}`;
129
- addNode({ id: sourceId, type: 'table', name: trigger.sourceName, databaseType: 'dynamodb' });
151
+ addNode({
152
+ id: sourceId,
153
+ type: 'table',
154
+ name: trigger.sourceName,
155
+ databaseType: 'dynamodb',
156
+ });
130
157
  }
131
158
  else if (trigger.type === 'kinesis') {
132
159
  sourceId = `queue:aws:${trigger.sourceName}`;
133
- addNode({ id: sourceId, type: 'queue', name: trigger.sourceName, provider: 'aws', hasDLQ: false, encrypted: false });
160
+ addNode({
161
+ id: sourceId,
162
+ type: 'queue',
163
+ name: trigger.sourceName,
164
+ provider: 'aws',
165
+ hasDLQ: false,
166
+ encrypted: false,
167
+ });
134
168
  }
135
169
  else {
136
170
  continue;
@@ -223,7 +257,14 @@ export function buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta = [],
223
257
  // AWS service operations create edges to service nodes
224
258
  if (op.serviceType === 'sqs') {
225
259
  const queueId = `queue:aws:${op.target}`;
226
- addNode({ id: queueId, type: 'queue', name: op.target, provider: 'aws', hasDLQ: false, encrypted: false });
260
+ addNode({
261
+ id: queueId,
262
+ type: 'queue',
263
+ name: op.target,
264
+ provider: 'aws',
265
+ hasDLQ: false,
266
+ encrypted: false,
267
+ });
227
268
  edges.push({ from: funcNodeId, to: queueId, type: 'publishes_to' });
228
269
  continue;
229
270
  }
@@ -242,13 +283,26 @@ export function buildGraph(operations, dynamoMeta, postgresMeta, mysqlMeta = [],
242
283
  }
243
284
  if (op.serviceType === 'ssm') {
244
285
  const paramId = `parameter:aws:${op.target}`;
245
- addNode({ id: paramId, type: 'parameter', name: op.target, provider: 'aws', paramType: 'String', tier: 'Standard' });
286
+ addNode({
287
+ id: paramId,
288
+ type: 'parameter',
289
+ name: op.target,
290
+ provider: 'aws',
291
+ paramType: 'String',
292
+ tier: 'Standard',
293
+ });
246
294
  edges.push({ from: funcNodeId, to: paramId, type: 'reads_parameter' });
247
295
  continue;
248
296
  }
249
297
  if (op.serviceType === 'secretsmanager') {
250
298
  const secretId = `secret:aws:${op.target}`;
251
- addNode({ id: secretId, type: 'secret', name: op.target, provider: 'aws', rotationEnabled: false });
299
+ addNode({
300
+ id: secretId,
301
+ type: 'secret',
302
+ name: op.target,
303
+ provider: 'aws',
304
+ rotationEnabled: false,
305
+ });
252
306
  edges.push({ from: funcNodeId, to: secretId, type: 'reads_secret' });
253
307
  continue;
254
308
  }