@prism-d1/cli 1.0.94 → 1.0.95

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.
@@ -0,0 +1,331 @@
1
+ import { aws } from './aws.js';
2
+ /**
3
+ * Audit Bedrock blast-radius controls -- what bounds runaway model spend, how
4
+ * fast you find out, and whether you can attribute it afterwards.
5
+ *
6
+ * These checks are composed by `bedrock-protection scan-account` alongside the
7
+ * IAM hygiene checks in audit-iam.ts. Repository credential exposure is the
8
+ * third side, covered by `bedrock-protection scan-repo`.
9
+ *
10
+ * Audit-only: every AWS call here is a read.
11
+ */
12
+ /**
13
+ * Bedrock spend lands on TWO billing surfaces, and a filter scoped to only the
14
+ * first silently misses the second:
15
+ *
16
+ * 1. `Amazon Bedrock*` services -- first-party models, AgentCore, Knowledge
17
+ * Bases, Guardrails.
18
+ * 2. `AWS Marketplace` -- every third-party model, billed as a subscription
19
+ * line item named e.g. "Claude Opus 5 (Amazon Bedrock Edition)".
20
+ *
21
+ * So a budget filtered on Amazon Bedrock alone reports nothing while an attacker
22
+ * burns Opus -- both the priciest option and the likeliest target. The two
23
+ * surfaces are therefore separate checks below, and a budget covering only one
24
+ * does not clear the other.
25
+ */
26
+ const BEDROCK_HINTS = ['bedrock'];
27
+ const MARKETPLACE_HINTS = ['marketplace'];
28
+ function budgetCovers(budget, hints) {
29
+ // Older budgets carry CostFilters (dimension -> values). Newer ones carry
30
+ // FilterExpression, a nested tree. Rather than pretend to fully interpret an
31
+ // expression tree, match its serialised form -- and return 'unknown' when
32
+ // neither field is present, because an unrecognised budget must never be
33
+ // counted as coverage.
34
+ const filters = budget?.CostFilters;
35
+ if (filters && Object.keys(filters).length > 0) {
36
+ const blob = JSON.stringify(filters).toLowerCase();
37
+ return hints.some((h) => blob.includes(h)) ? 'yes' : 'no';
38
+ }
39
+ if (budget?.FilterExpression) {
40
+ const blob = JSON.stringify(budget.FilterExpression).toLowerCase();
41
+ return hints.some((h) => blob.includes(h)) ? 'yes' : 'no';
42
+ }
43
+ // No filter at all means the budget covers total account spend. That bounds
44
+ // Bedrock, but only bluntly -- it will not fire until everything else has
45
+ // grown too.
46
+ return 'unknown';
47
+ }
48
+ /**
49
+ * Only a COST budget bounds spend.
50
+ *
51
+ * BudgetType is one of USAGE, COST, RI_UTILIZATION, RI_COVERAGE,
52
+ * SAVINGS_PLANS_UTILIZATION or SAVINGS_PLANS_COVERAGE. A USAGE budget filtered on
53
+ * Bedrock tracks request counts, and an RI/SP budget tracks commitment
54
+ * efficiency -- neither caps a dollar figure. Treating any of them as coverage
55
+ * would report a spend ceiling that does not exist, which is worse than
56
+ * reporting none because it stops the reader looking.
57
+ */
58
+ function isCostBudget(budget) {
59
+ return budget?.BudgetType === 'COST';
60
+ }
61
+ export function auditBudgets(profile, accountId) {
62
+ // describe-budgets requires an explicit account id, and returns an EMPTY body
63
+ // -- not {"Budgets": []} -- when no budgets exist.
64
+ const bud = aws(['budgets', 'describe-budgets', '--account-id', accountId], profile);
65
+ if (!bud.ok) {
66
+ return { coveringBudgets: [], findings: [
67
+ ['bedrock-budget', 'Budget covering Bedrock services'],
68
+ ['marketplace-budget', 'Budget covering Marketplace (Bedrock Edition) models'],
69
+ ].map(([id, title]) => ({
70
+ id, category: 'budget', title,
71
+ status: 'INDETERMINATE', severity: 'HIGH',
72
+ detail: `Could not list budgets (${bud.errorCode || 'unknown error'}).`,
73
+ remediation: 'Grant budgets:DescribeBudgets and re-run.',
74
+ })) };
75
+ }
76
+ const budgets = bud.json?.Budgets ?? [];
77
+ // Coverage requires BOTH a matching filter and BudgetType === COST.
78
+ const covering = (hints) => budgets.filter((b) => budgetCovers(b, hints) === 'yes' && isCostBudget(b)).map((b) => b.BudgetName);
79
+ // Matching filter but the wrong budget type: named explicitly, because from
80
+ // the console it looks like a Bedrock budget while capping no dollar figure.
81
+ const wrongType = (hints) => budgets.filter((b) => budgetCovers(b, hints) === 'yes' && !isCostBudget(b))
82
+ .map((b) => `${b.BudgetName} (BudgetType=${b.BudgetType})`);
83
+ const untargeted = budgets
84
+ .filter((b) => budgetCovers(b, BEDROCK_HINTS) === 'unknown' && isCostBudget(b))
85
+ .map((b) => b.BudgetName);
86
+ const bedrock = covering(BEDROCK_HINTS);
87
+ const marketplace = covering(MARKETPLACE_HINTS);
88
+ const bedrockWrongType = wrongType(BEDROCK_HINTS);
89
+ const marketplaceWrongType = wrongType(MARKETPLACE_HINTS);
90
+ return { coveringBudgets: [...new Set([...bedrock, ...marketplace])], findings: [
91
+ {
92
+ id: 'bedrock-budget', category: 'budget', title: 'Budget covering Bedrock services',
93
+ status: bedrock.length ? 'PASS' : 'FAIL', severity: 'HIGH',
94
+ detail: bedrock.length
95
+ ? `Covered by: ${bedrock.join(', ')}.`
96
+ : budgets.length === 0
97
+ ? 'No budgets exist in this account, so runaway Bedrock spend has no ceiling and raises no alert.'
98
+ : bedrockWrongType.length
99
+ ? `${bedrockWrongType.join(', ')} filters on Bedrock but is not a COST budget, so it caps no dollar figure — it looks like a Bedrock budget in the console while bounding nothing.`
100
+ : `${budgets.length} budget(s) exist but none is a COST budget filtering on a Bedrock service${untargeted.length ? ` (${untargeted.length} cover total account spend, which bounds Bedrock only indirectly)` : ''}.`,
101
+ remediation: bedrock.length ? undefined : 'Create a monthly budget filtered on Service contains "Bedrock", alerting at 50/80/100% to SNS.',
102
+ },
103
+ {
104
+ id: 'marketplace-budget', category: 'budget', title: 'Budget covering Marketplace (Bedrock Edition) models',
105
+ status: marketplace.length ? 'PASS' : 'FAIL', severity: 'HIGH',
106
+ detail: marketplace.length
107
+ ? `Covered by: ${marketplace.join(', ')}.`
108
+ : (marketplaceWrongType.length ? `${marketplaceWrongType.join(', ')} filters on Marketplace but is not a COST budget, so it caps no dollar figure. ` : '')
109
+ + 'No COST budget filters on AWS Marketplace. Third-party models invoked through Bedrock — Anthropic, Meta, Mistral, Cohere — bill as Marketplace subscription line items named "(Amazon Bedrock Edition)", NOT under Amazon Bedrock. A Bedrock-only budget misses them entirely, which is the most expensive blind spot here because Opus-class models are both the priciest and the likeliest target.',
110
+ remediation: marketplace.length ? undefined : 'Add a second COST budget filtered on Service = AWS Marketplace, or define a Cost Category grouping both surfaces and budget on that.',
111
+ },
112
+ ] };
113
+ }
114
+ /**
115
+ * A budget that alerts nobody is a record, not a control.
116
+ *
117
+ * `describe-budgets` says nothing about whether anyone is told when a threshold
118
+ * is crossed -- notifications and their subscribers are separate resources. A
119
+ * budget created without them still shows a spend bar in the console and still
120
+ * reports as "a Bedrock budget exists", while nothing reaches a human. For
121
+ * LLMjacking specifically, being told is the entire value: the spend has already
122
+ * happened by the time anyone opens the Billing console.
123
+ *
124
+ * `DescribeBudgetNotificationsForAccount` returns notifications for every budget
125
+ * in ONE call. Subscribers need a call per notification, which is why this is
126
+ * scoped to the budgets that actually cover Bedrock rather than all of them.
127
+ */
128
+ export function auditBudgetAlerting(profile, accountId, coveringBudgets) {
129
+ const id = 'budget-alerting';
130
+ const title = 'Bedrock budgets actually notify someone';
131
+ if (coveringBudgets.length === 0) {
132
+ // Nothing to evaluate. Not a PASS -- no alerting was established -- and not
133
+ // a duplicate FAIL, since bedrock-budget/marketplace-budget already report
134
+ // the absence.
135
+ return {
136
+ id, category: 'budget', title,
137
+ status: 'INDETERMINATE', severity: 'HIGH',
138
+ detail: 'No COST budget covers Bedrock or Marketplace, so there is no budget whose alerting could be checked (see bedrock-budget and marketplace-budget above).',
139
+ };
140
+ }
141
+ const all = aws(['budgets', 'describe-budget-notifications-for-account', '--account-id', accountId], profile);
142
+ if (!all.ok) {
143
+ return {
144
+ id, category: 'budget', title,
145
+ status: 'INDETERMINATE', severity: 'HIGH',
146
+ detail: `Could not read budget notifications (${all.errorCode || 'unknown error'}), so it is unknown whether ${coveringBudgets.join(', ')} alerts anyone.`,
147
+ remediation: 'Grant budgets:DescribeBudgetNotificationsForAccount and budgets:DescribeSubscribersForNotification, then re-run.',
148
+ };
149
+ }
150
+ const perBudget = all.json?.BudgetNotificationsForAccount ?? [];
151
+ const silent = [];
152
+ const unsubscribed = [];
153
+ let alerting = 0;
154
+ for (const name of coveringBudgets) {
155
+ const entry = perBudget.find((e) => e.BudgetName === name);
156
+ const notifications = entry?.Notifications ?? [];
157
+ if (notifications.length === 0) {
158
+ silent.push(name);
159
+ continue;
160
+ }
161
+ let subscribers = 0;
162
+ for (const n of notifications) {
163
+ const subs = aws([
164
+ 'budgets', 'describe-subscribers-for-notification',
165
+ '--account-id', accountId, '--budget-name', name,
166
+ '--notification', JSON.stringify(n),
167
+ ], profile);
168
+ if (subs.ok)
169
+ subscribers += (subs.json?.Subscribers ?? []).length;
170
+ }
171
+ if (subscribers === 0)
172
+ unsubscribed.push(name);
173
+ else
174
+ alerting++;
175
+ }
176
+ const broken = [...silent, ...unsubscribed];
177
+ return {
178
+ id, category: 'budget', title,
179
+ status: broken.length ? 'FAIL' : 'PASS', severity: 'HIGH',
180
+ detail: broken.length
181
+ ? [
182
+ silent.length ? `${silent.join(', ')} has no notification thresholds at all` : '',
183
+ unsubscribed.length ? `${unsubscribed.join(', ')} has thresholds but no subscribers` : '',
184
+ ].filter(Boolean).join('; ')
185
+ + '. A budget with nothing to notify records the overspend without telling anyone, which reads as protection while providing none.'
186
+ : `All ${alerting} covering budget(s) have at least one threshold with a subscriber.`,
187
+ remediation: broken.length
188
+ ? 'Add notification thresholds (50/80/100%) with an SNS topic or email subscriber. Prefer SNS — it can fan out to a rota rather than one mailbox.'
189
+ : undefined,
190
+ };
191
+ }
192
+ /**
193
+ * One title constant per check, because this check is pushed from three
194
+ * branches (CE disabled, CE unreadable, CE readable). Inlining the string in
195
+ * each made them drift the moment the check gained its subscription
196
+ * requirement: the same `anomaly-monitor` id reported two different titles
197
+ * depending on account state, which is unstable in --json output.
198
+ */
199
+ const ANOMALY_TITLE = 'Cost anomaly monitor covering Bedrock, with an alert subscription';
200
+ export function auditDetection(profile, region) {
201
+ const out = [];
202
+ // Cost Explorer is an account-level opt-in that stays OFF until someone
203
+ // enables it -- measured error: AccessDeniedException "User not enabled for
204
+ // cost explorer access". Every CE-backed check must be gated on this, or the
205
+ // audit reports "no anomaly monitor configured" when the truth is that it
206
+ // could not look.
207
+ const ce = aws(['ce', 'get-anomaly-monitors'], profile);
208
+ const ceDisabled = !ce.ok && /not enabled for cost explorer/i.test(ce.raw);
209
+ out.push({
210
+ id: 'cost-explorer-enabled', category: 'detection', title: 'Cost Explorer enabled',
211
+ status: ceDisabled ? 'FAIL' : ce.ok ? 'PASS' : 'INDETERMINATE',
212
+ severity: 'HIGH',
213
+ detail: ceDisabled
214
+ ? 'Cost Explorer is not enabled on this account, so cost anomaly detection is impossible and spend cannot be attributed to a service or model.'
215
+ : ce.ok
216
+ ? 'Cost Explorer is enabled.'
217
+ : `Could not confirm Cost Explorer access (${ce.errorCode || 'unknown error'}).`,
218
+ remediation: ceDisabled ? 'Enable Cost Explorer in Billing settings (management account for an org). It backfills up to 12 months and can take 24h to become queryable.' : undefined,
219
+ });
220
+ if (ceDisabled || !ce.ok) {
221
+ out.push({
222
+ id: 'anomaly-monitor', category: 'detection', title: ANOMALY_TITLE,
223
+ status: 'INDETERMINATE', severity: 'MEDIUM',
224
+ detail: ceDisabled
225
+ ? 'Cannot be checked while Cost Explorer is disabled.'
226
+ : `Could not list anomaly monitors (${ce.errorCode || 'unknown error'}).`,
227
+ });
228
+ }
229
+ else {
230
+ const monitors = ce.json?.AnomalyMonitors ?? [];
231
+ const serviceWide = monitors.filter((m) => m.MonitorDimension === 'SERVICE');
232
+ const bedrockScoped = monitors.filter((m) => JSON.stringify(m.MonitorSpecification ?? {}).toLowerCase().includes('bedrock'));
233
+ const covering = [...serviceWide, ...bedrockScoped];
234
+ // A monitor with no subscription is the same failure mode as a budget with
235
+ // no subscriber: it detects the anomaly and tells nobody. Monitors and
236
+ // subscriptions are separate resources, and get-anomaly-monitors reports
237
+ // nothing about whether one is attached, so this needs a second call.
238
+ //
239
+ // Worth knowing: creating a first budget auto-enables Cost Explorer, and AWS
240
+ // then auto-creates a "Default-Services-Monitor" (SERVICE dimension) WITH a
241
+ // subscription -- observed live. So this check often passes as a side effect
242
+ // of setting up budgets rather than deliberately.
243
+ const subs = aws(['ce', 'get-anomaly-subscriptions'], profile);
244
+ const coveringArns = new Set(covering.map((m) => m.MonitorArn));
245
+ const subscribed = subs.ok
246
+ ? (subs.json?.AnomalySubscriptions ?? []).some((s) => (s.MonitorArnList ?? []).some((arn) => coveringArns.has(arn)))
247
+ : null;
248
+ const status = covering.length === 0 ? 'FAIL' : subscribed === null ? 'INDETERMINATE' : subscribed ? 'PASS' : 'FAIL';
249
+ out.push({
250
+ id: 'anomaly-monitor', category: 'detection', title: ANOMALY_TITLE,
251
+ status, severity: 'MEDIUM',
252
+ detail: covering.length === 0
253
+ ? (monitors.length === 0
254
+ ? 'No cost anomaly monitors. A budget only fires once a threshold is crossed; anomaly detection catches spend that is abnormal for this account, which is what a stolen key looks like on day one.'
255
+ : `${monitors.length} monitor(s) exist but none covers Bedrock.`)
256
+ : subscribed === null
257
+ ? `${covering.length} covering monitor(s), but the subscription list could not be read (${subs.errorCode || 'unknown error'}), so it is unknown whether anomalies reach anyone.`
258
+ : subscribed
259
+ ? `${covering.length} covering monitor(s) (${serviceWide.length} SERVICE-dimension, ${bedrockScoped.length} Bedrock-scoped), with an alert subscription attached.`
260
+ : `${covering.length} covering monitor(s) exist but NO subscription references them, so detected anomalies notify nobody — the monitor records them and stops there.`,
261
+ remediation: status === 'PASS' ? undefined
262
+ : covering.length === 0
263
+ ? 'Create a SERVICE-dimension monitor — it covers every service including Marketplace — then attach a subscription with a threshold low enough to fire before the monthly budget would.'
264
+ : 'Attach an anomaly subscription to the monitor. Without one the monitor is detection with no delivery.',
265
+ });
266
+ }
267
+ // CloudWatch is the only fast signal. Cost data lags 12-24h, so a cost-only
268
+ // posture lets a stolen key run for up to a day before anything fires.
269
+ const alarms = aws(['cloudwatch', 'describe-alarms', '--region', region], profile);
270
+ if (!alarms.ok) {
271
+ out.push({
272
+ id: 'bedrock-invocation-alarm', category: 'detection', title: 'CloudWatch alarm on Bedrock invocation volume',
273
+ status: 'INDETERMINATE', severity: 'MEDIUM',
274
+ detail: `Could not list alarms in ${region} (${alarms.errorCode || 'unknown error'}).`,
275
+ });
276
+ }
277
+ else {
278
+ const bedrockAlarms = (alarms.json?.MetricAlarms ?? []).filter((a) => a.Namespace === 'AWS/Bedrock');
279
+ out.push({
280
+ id: 'bedrock-invocation-alarm', category: 'detection', title: 'CloudWatch alarm on Bedrock invocation volume',
281
+ status: bedrockAlarms.length ? 'PASS' : 'FAIL', severity: 'MEDIUM',
282
+ detail: bedrockAlarms.length
283
+ ? `${bedrockAlarms.length} alarm(s) on AWS/Bedrock metrics: ${bedrockAlarms.map((a) => a.AlarmName).slice(0, 5).join(', ')}.`
284
+ : `No alarms on AWS/Bedrock metrics in ${region}. Cost data lags 12–24h, so budgets and anomaly detection cannot catch a stolen key inside the first day. Token-count metrics are near real-time and are the only fast signal available.`,
285
+ remediation: bedrockAlarms.length ? undefined : 'Alarm on AWS/Bedrock Invocations and InputTokenCount with a threshold above normal peak; it fires in minutes rather than hours.',
286
+ });
287
+ }
288
+ return out;
289
+ }
290
+ export function auditCommitments(profile, region) {
291
+ const pt = aws(['bedrock', 'list-provisioned-model-throughputs', '--region', region], profile);
292
+ if (!pt.ok) {
293
+ return {
294
+ id: 'provisioned-throughput', category: 'commitment', title: 'Provisioned Throughput commitments',
295
+ status: 'INDETERMINATE', severity: 'INFO',
296
+ detail: `Could not list provisioned throughput in ${region} (${pt.errorCode || 'unknown error'}).`,
297
+ };
298
+ }
299
+ const pts = pt.json?.provisionedModelSummaries ?? [];
300
+ return {
301
+ id: 'provisioned-throughput', category: 'commitment', title: 'Provisioned Throughput commitments',
302
+ status: pts.length ? 'FAIL' : 'PASS',
303
+ severity: pts.length ? 'MEDIUM' : 'INFO',
304
+ detail: pts.length
305
+ ? `${pts.length} commitment(s) in ${region}: ${pts.map((p) => p.provisionedModelName || p.modelArn).slice(0, 5).join(', ')}. These bill monthly whether or not they are used.`
306
+ : `No Provisioned Throughput commitments in ${region}. NOTE: region-scoped — a commitment in another region will not appear here.`,
307
+ remediation: pts.length ? 'Confirm each commitment is intentional and still in use; an abandoned one bills indefinitely.' : undefined,
308
+ };
309
+ }
310
+ export function auditForensics(profile, region) {
311
+ // Returns an EMPTY body when logging is off -- same trap as describe-budgets.
312
+ const log = aws(['bedrock', 'get-model-invocation-logging-configuration', '--region', region], profile);
313
+ if (!log.ok) {
314
+ return {
315
+ id: 'model-invocation-logging', category: 'forensics', title: 'Bedrock model invocation logging',
316
+ status: 'INDETERMINATE', severity: 'MEDIUM',
317
+ detail: `Could not read the logging configuration in ${region} (${log.errorCode || 'unknown error'}).`,
318
+ };
319
+ }
320
+ const cfg = log.json?.loggingConfig;
321
+ const configured = Boolean(cfg && (cfg.cloudWatchConfig || cfg.s3Config));
322
+ return {
323
+ id: 'model-invocation-logging', category: 'forensics', title: 'Bedrock model invocation logging',
324
+ status: configured ? 'PASS' : 'FAIL', severity: 'MEDIUM',
325
+ detail: configured
326
+ ? `Enabled in ${region} (${[cfg.cloudWatchConfig && 'CloudWatch', cfg.s3Config && 'S3'].filter(Boolean).join(' + ')}).`
327
+ : `Off in ${region}. CloudTrail records that InvokeModel was called but not the model, prompt or token counts, so during an incident you can see spend rose without being able to attribute it to a caller or workload.`,
328
+ remediation: configured ? undefined : 'Enable model invocation logging to CloudWatch Logs or S3. Prompts and completions are captured, so treat the destination as sensitive and scope its access accordingly.',
329
+ };
330
+ }
331
+ //# sourceMappingURL=audit-bedrock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-bedrock.js","sourceRoot":"","sources":["../../../src/utils/audit-bedrock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Finding } from './audit.js';
2
+ export declare function auditRoot(profile?: string): Finding[];
3
+ export declare function auditPasswordPolicy(profile?: string): Finding;
4
+ export declare function auditUsers(profile: string | undefined, maxKeyAge: number, unusedDays: number): Finding[];
5
+ export declare function auditUserPolicies(profile?: string): Finding;
6
+ //# sourceMappingURL=audit-iam.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-iam.d.ts","sourceRoot":"","sources":["../../../src/utils/audit-iam.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAY,MAAM,YAAY,CAAC;AAwE/C,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CA6CrD;AAED,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAmC7D;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,EAAE,CA2ExG;AAED,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAgC3D"}
@@ -0,0 +1,239 @@
1
+ import { aws, daysSince } from './aws.js';
2
+ /**
3
+ * GetCredentialReport fails with ReportNotPresent until GenerateCredentialReport
4
+ * has been called -- measured, not assumed. Generation is asynchronous, so this
5
+ * kicks it off and re-reads a bounded number of times.
6
+ *
7
+ * This is the only non-read AWS call in the command. It produces a report
8
+ * artifact and modifies no resource.
9
+ */
10
+ function credentialReport(profile) {
11
+ let got = aws(['iam', 'get-credential-report'], profile);
12
+ if (!got.ok && got.errorCode === 'ReportNotPresent') {
13
+ const gen = aws(['iam', 'generate-credential-report'], profile);
14
+ if (!gen.ok)
15
+ return { error: gen.errorCode || 'generate-credential-report failed' };
16
+ for (let i = 0; i < 10 && !got.ok; i++) {
17
+ got = aws(['iam', 'get-credential-report'], profile);
18
+ if (!got.ok && got.errorCode !== 'ReportNotPresent')
19
+ break;
20
+ }
21
+ }
22
+ if (!got.ok)
23
+ return { error: got.errorCode || 'get-credential-report failed' };
24
+ const b64 = got.json?.Content;
25
+ if (!b64)
26
+ return { error: 'credential report had no Content' };
27
+ const csv = Buffer.from(b64, 'base64').toString('utf8');
28
+ const lines = csv.trim().split('\n');
29
+ const header = lines[0].split(',');
30
+ const col = (name) => header.indexOf(name);
31
+ const rows = [];
32
+ for (const line of lines.slice(1)) {
33
+ const c = line.split(',');
34
+ const at = (name) => (col(name) >= 0 ? c[col(name)] : '');
35
+ rows.push({
36
+ user: at('user'),
37
+ passwordEnabled: at('password_enabled') === 'true',
38
+ mfaActive: at('mfa_active') === 'true',
39
+ passwordLastUsed: at('password_last_used'),
40
+ keys: [1, 2].map((n) => ({
41
+ n,
42
+ active: at(`access_key_${n}_active`) === 'true',
43
+ lastRotated: at(`access_key_${n}_last_rotated`),
44
+ lastUsed: at(`access_key_${n}_last_used_date`),
45
+ })),
46
+ });
47
+ }
48
+ return rows;
49
+ }
50
+ export function auditRoot(profile) {
51
+ const out = [];
52
+ // GetAccountSummary answers the two most severe questions in one call and
53
+ // needs no credential report. AccountMFAEnabled and AccountAccessKeysPresent
54
+ // both describe the ROOT user specifically.
55
+ const summary = aws(['iam', 'get-account-summary'], profile);
56
+ const map = summary.ok ? (summary.json?.SummaryMap ?? null) : null;
57
+ if (!map) {
58
+ out.push({
59
+ id: 'root-mfa', category: 'root', title: 'Root account MFA',
60
+ status: 'INDETERMINATE', severity: 'CRITICAL',
61
+ detail: `Could not read the account summary (${summary.errorCode || 'unknown error'}).`,
62
+ remediation: 'Grant iam:GetAccountSummary and re-run.',
63
+ });
64
+ out.push({
65
+ id: 'root-access-keys', category: 'root', title: 'Root access keys',
66
+ status: 'INDETERMINATE', severity: 'CRITICAL',
67
+ detail: `Could not read the account summary (${summary.errorCode || 'unknown error'}).`,
68
+ });
69
+ return out;
70
+ }
71
+ const rootMfa = map.AccountMFAEnabled === 1;
72
+ out.push({
73
+ id: 'root-mfa', category: 'root', title: 'Root account MFA',
74
+ status: rootMfa ? 'PASS' : 'FAIL', severity: 'CRITICAL',
75
+ detail: rootMfa
76
+ ? 'Root user has an MFA device.'
77
+ : 'Root user has NO MFA device. Root cannot be constrained by SCPs or permission boundaries, so a compromised root is unbounded.',
78
+ remediation: 'Attach a FIDO2 security key to root — phishing resistant, unlike TOTP. In Amazon-internal Isengard accounts root is managed for you and this may not be actionable; confirm before escalating.',
79
+ });
80
+ const rootKeys = map.AccountAccessKeysPresent === 1;
81
+ out.push({
82
+ id: 'root-access-keys', category: 'root', title: 'Root access keys',
83
+ status: rootKeys ? 'FAIL' : 'PASS', severity: 'CRITICAL',
84
+ detail: rootKeys
85
+ ? 'Root has active access keys — unconstrainable long-lived credentials, the single worst credential to leak.'
86
+ : 'No root access keys.',
87
+ remediation: 'Delete root access keys. Nothing legitimate requires them.',
88
+ });
89
+ return out;
90
+ }
91
+ export function auditPasswordPolicy(profile) {
92
+ const pol = aws(['iam', 'get-account-password-policy'], profile);
93
+ // NoSuchEntity is the API saying "no policy set", which means AWS defaults
94
+ // apply. That is a finding, not an error -- treating it as an error hides it.
95
+ if (!pol.ok && pol.errorCode === 'NoSuchEntity') {
96
+ return {
97
+ id: 'password-policy', category: 'policy', title: 'Account password policy',
98
+ status: 'FAIL', severity: 'MEDIUM',
99
+ detail: 'No password policy is set, so only AWS defaults apply (minimum length 8, no complexity or reuse rules).',
100
+ remediation: 'Set MinimumPasswordLength >= 14 with complexity and reuse prevention — or remove console passwords entirely in favour of Identity Center.',
101
+ };
102
+ }
103
+ if (!pol.ok) {
104
+ return {
105
+ id: 'password-policy', category: 'policy', title: 'Account password policy',
106
+ status: 'INDETERMINATE', severity: 'MEDIUM',
107
+ detail: `Could not read the password policy (${pol.errorCode || 'unknown error'}).`,
108
+ };
109
+ }
110
+ const p = pol.json?.PasswordPolicy ?? {};
111
+ const weak = [];
112
+ if ((p.MinimumPasswordLength ?? 0) < 14)
113
+ weak.push(`MinimumPasswordLength=${p.MinimumPasswordLength ?? 'unset'} (< 14)`);
114
+ if (!p.RequireSymbols)
115
+ weak.push('RequireSymbols=false');
116
+ if (!p.RequireNumbers)
117
+ weak.push('RequireNumbers=false');
118
+ if (!p.RequireUppercaseCharacters || !p.RequireLowercaseCharacters)
119
+ weak.push('mixed case not required');
120
+ if (!p.PasswordReusePrevention)
121
+ weak.push('PasswordReusePrevention unset');
122
+ return {
123
+ id: 'password-policy', category: 'policy', title: 'Account password policy',
124
+ status: weak.length ? 'FAIL' : 'PASS', severity: 'MEDIUM',
125
+ detail: weak.length ? `Weaker than baseline: ${weak.join(', ')}.` : 'Policy meets the baseline.',
126
+ remediation: weak.length ? 'Raise minimum length to 14+, require complexity, enable reuse prevention.' : undefined,
127
+ };
128
+ }
129
+ export function auditUsers(profile, maxKeyAge, unusedDays) {
130
+ const out = [];
131
+ const report = credentialReport(profile);
132
+ if ('error' in report) {
133
+ for (const [id, title, sev] of [
134
+ ['access-key-age', 'Long-lived access keys', 'HIGH'],
135
+ ['console-user-no-mfa', 'Console users without MFA', 'HIGH'],
136
+ ['unused-credentials', 'Unused credentials', 'MEDIUM'],
137
+ ]) {
138
+ out.push({
139
+ id, category: 'users', title,
140
+ status: 'INDETERMINATE', severity: sev,
141
+ detail: `Credential report unavailable (${report.error}).`,
142
+ remediation: 'Grant iam:GenerateCredentialReport and iam:GetCredentialReport, then re-run.',
143
+ });
144
+ }
145
+ return out;
146
+ }
147
+ // <root_account> is covered by the summary checks and reported with a
148
+ // different shape, so exclude it here rather than double-reporting.
149
+ const users = report.filter((r) => r.user !== '<root_account>');
150
+ const stale = [];
151
+ for (const u of users) {
152
+ for (const k of u.keys) {
153
+ if (!k.active)
154
+ continue;
155
+ const age = daysSince(k.lastRotated);
156
+ if (age !== null && age > maxKeyAge)
157
+ stale.push(`${u.user} key ${k.n} (${age}d)`);
158
+ }
159
+ }
160
+ out.push({
161
+ id: 'access-key-age', category: 'users', title: 'Long-lived access keys',
162
+ status: stale.length ? 'FAIL' : 'PASS', severity: 'HIGH',
163
+ detail: stale.length
164
+ ? `${stale.length} active key(s) older than ${maxKeyAge}d: ${stale.slice(0, 8).join(', ')}${stale.length > 8 ? ', …' : ''}. Key age is the strongest single predictor of credential-theft exposure — an old key has had more opportunities to leak and is rarely missed when it does.`
165
+ : `No active access key is older than ${maxKeyAge}d.`,
166
+ remediation: stale.length ? 'Rotate, or delete the key and move the workload onto an IAM role. A role issues short-lived credentials that cannot be exfiltrated as a static string.' : undefined,
167
+ });
168
+ const noMfa = users.filter((u) => u.passwordEnabled && !u.mfaActive).map((u) => u.user);
169
+ out.push({
170
+ id: 'console-user-no-mfa', category: 'users', title: 'Console users without MFA',
171
+ status: noMfa.length ? 'FAIL' : 'PASS', severity: 'HIGH',
172
+ detail: noMfa.length
173
+ ? `${noMfa.length} user(s) have console passwords and no MFA: ${noMfa.slice(0, 8).join(', ')}${noMfa.length > 8 ? ', …' : ''}.`
174
+ : 'Every console-enabled user has MFA.',
175
+ remediation: noMfa.length ? 'Enforce MFA with a deny policy conditioned on aws:MultiFactorAuthPresent rather than relying on voluntary enrolment.' : undefined,
176
+ });
177
+ const unused = [];
178
+ for (const u of users) {
179
+ if (u.passwordEnabled) {
180
+ const d = daysSince(u.passwordLastUsed);
181
+ if (d !== null && d > unusedDays)
182
+ unused.push(`${u.user} password (${d}d)`);
183
+ }
184
+ for (const k of u.keys) {
185
+ if (!k.active)
186
+ continue;
187
+ // 'N/A' means active but never used; fall back to key age so a
188
+ // never-used key is not silently skipped.
189
+ const d = daysSince(k.lastUsed) ?? daysSince(k.lastRotated);
190
+ if (d !== null && d > unusedDays)
191
+ unused.push(`${u.user} key ${k.n} (${d}d)`);
192
+ }
193
+ }
194
+ out.push({
195
+ id: 'unused-credentials', category: 'users', title: 'Unused credentials',
196
+ status: unused.length ? 'FAIL' : 'PASS', severity: 'MEDIUM',
197
+ detail: unused.length
198
+ ? `${unused.length} credential(s) idle for over ${unusedDays}d: ${unused.slice(0, 8).join(', ')}${unused.length > 8 ? ', …' : ''}. An idle credential is pure attack surface — its first malicious use looks identical to its first legitimate use.`
199
+ : `No credential has been idle longer than ${unusedDays}d.`,
200
+ remediation: unused.length ? 'Delete them. Idle credentials are the ones whose misuse goes unnoticed longest.' : undefined,
201
+ });
202
+ return out;
203
+ }
204
+ export function auditUserPolicies(profile) {
205
+ const list = aws(['iam', 'list-users', '--query', 'Users[].UserName'], profile);
206
+ if (!list.ok) {
207
+ return {
208
+ id: 'user-admin-policy', category: 'users', title: 'AdministratorAccess on IAM users',
209
+ status: 'INDETERMINATE', severity: 'HIGH',
210
+ detail: `Could not list users (${list.errorCode || 'unknown error'}).`,
211
+ };
212
+ }
213
+ const names = Array.isArray(list.json) ? list.json : [];
214
+ const admins = [];
215
+ let unchecked = 0;
216
+ for (const name of names) {
217
+ const att = aws(['iam', 'list-attached-user-policies', '--user-name', name], profile);
218
+ if (!att.ok) {
219
+ unchecked++;
220
+ continue;
221
+ }
222
+ const policies = att.json?.AttachedPolicies ?? [];
223
+ if (policies.some((p) => p.PolicyName === 'AdministratorAccess'))
224
+ admins.push(name);
225
+ }
226
+ return {
227
+ id: 'user-admin-policy', category: 'users', title: 'AdministratorAccess on IAM users',
228
+ // A partial read cannot clear the check: unchecked users may be the admins.
229
+ status: admins.length ? 'FAIL' : unchecked > 0 ? 'INDETERMINATE' : 'PASS',
230
+ severity: 'HIGH',
231
+ detail: admins.length
232
+ ? `AdministratorAccess is attached directly to: ${admins.join(', ')}. Any access key belonging to these users is an unbounded credential.`
233
+ : unchecked > 0
234
+ ? `Checked ${names.length - unchecked}/${names.length} users; ${unchecked} could not be read, so this result is incomplete.`
235
+ : `None of ${names.length} user(s) has AdministratorAccess attached directly.`,
236
+ remediation: admins.length ? 'Move admin access behind an assumable role gated on MFA, so no static key carries it.' : undefined,
237
+ };
238
+ }
239
+ //# sourceMappingURL=audit-iam.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-iam.js","sourceRoot":"","sources":["../../../src/utils/audit-iam.ts"],"names":[],"mappings":"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"}