@prism-d1/cli 1.0.95 → 1.0.96
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/dist/src/commands/bedrock-protection/scan-account.d.ts.map +1 -1
- package/dist/src/commands/bedrock-protection/scan-account.js +12 -10
- package/dist/src/commands/bedrock-protection/scan-account.js.map +1 -1
- package/dist/src/commands/bedrock-protection/scan-org.d.ts +23 -0
- package/dist/src/commands/bedrock-protection/scan-org.d.ts.map +1 -0
- package/dist/src/commands/bedrock-protection/scan-org.js +328 -0
- package/dist/src/commands/bedrock-protection/scan-org.js.map +1 -0
- package/dist/src/utils/audit-bedrock.d.ts +6 -5
- package/dist/src/utils/audit-bedrock.d.ts.map +1 -1
- package/dist/src/utils/audit-bedrock.js +13 -13
- package/dist/src/utils/audit-bedrock.js.map +1 -1
- package/dist/src/utils/audit-iam.d.ts +5 -4
- package/dist/src/utils/audit-iam.d.ts.map +1 -1
- package/dist/src/utils/audit-iam.js +13 -13
- package/dist/src/utils/audit-iam.js.map +1 -1
- package/dist/src/utils/audit-org.d.ts +80 -0
- package/dist/src/utils/audit-org.d.ts.map +1 -0
- package/dist/src/utils/audit-org.js +304 -0
- package/dist/src/utils/audit-org.js.map +1 -0
- package/dist/src/utils/aws.d.ts +33 -2
- package/dist/src/utils/aws.d.ts.map +1 -1
- package/dist/src/utils/aws.js +47 -5
- package/dist/src/utils/aws.js.map +1 -1
- package/dist/src/utils/exec.d.ts +12 -1
- package/dist/src/utils/exec.d.ts.map +1 -1
- package/dist/src/utils/exec.js +23 -2
- package/dist/src/utils/exec.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-account.d.ts","sourceRoot":"","sources":["../../../../src/commands/bedrock-protection/scan-account.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;;;;;;iBAckB;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KAC3E;;AAfH,
|
|
1
|
+
{"version":3,"file":"scan-account.d.ts","sourceRoot":"","sources":["../../../../src/commands/bedrock-protection/scan-account.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;;;;;;iBAckB;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KAC3E;;AAfH,wBAgFE"}
|
|
@@ -54,7 +54,9 @@ export default {
|
|
|
54
54
|
throw new Error('--max-key-age must be a positive integer');
|
|
55
55
|
if (!Number.isFinite(unusedDays) || unusedDays < 1)
|
|
56
56
|
throw new Error('--unused-days must be a positive integer');
|
|
57
|
-
|
|
57
|
+
// One target for every check; scan-org supplies assumed-role creds instead.
|
|
58
|
+
const target = { profile: opts.profile };
|
|
59
|
+
const accountId = resolveAccount(target);
|
|
58
60
|
if (!accountId) {
|
|
59
61
|
process.exitCode = 2;
|
|
60
62
|
return;
|
|
@@ -63,20 +65,20 @@ export default {
|
|
|
63
65
|
const runBedrock = !opts.iamOnly;
|
|
64
66
|
// auditBudgets returns the covering budget names so the alerting check can
|
|
65
67
|
// scope its per-notification calls to them rather than every budget.
|
|
66
|
-
const budgets = runBedrock ? auditBudgets(
|
|
68
|
+
const budgets = runBedrock ? auditBudgets(target, accountId) : null;
|
|
67
69
|
const findings = [
|
|
68
70
|
...(runIam ? [
|
|
69
|
-
...auditRoot(
|
|
70
|
-
auditPasswordPolicy(
|
|
71
|
-
...auditUsers(
|
|
72
|
-
auditUserPolicies(
|
|
71
|
+
...auditRoot(target),
|
|
72
|
+
auditPasswordPolicy(target),
|
|
73
|
+
...auditUsers(target, maxKeyAge, unusedDays),
|
|
74
|
+
auditUserPolicies(target),
|
|
73
75
|
] : []),
|
|
74
76
|
...(runBedrock && budgets ? [
|
|
75
77
|
...budgets.findings,
|
|
76
|
-
auditBudgetAlerting(
|
|
77
|
-
...auditDetection(
|
|
78
|
-
auditCommitments(
|
|
79
|
-
auditForensics(
|
|
78
|
+
auditBudgetAlerting(target, accountId, budgets.coveringBudgets),
|
|
79
|
+
...auditDetection(target, region),
|
|
80
|
+
auditCommitments(target, region),
|
|
81
|
+
auditForensics(target, region),
|
|
80
82
|
] : []),
|
|
81
83
|
];
|
|
82
84
|
const scope = runIam && runBedrock ? '' : runIam ? ' Scope: IAM only' : ' Scope: Bedrock only';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-account.js","sourceRoot":"","sources":["../../../../src/commands/bedrock-protection/scan-account.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"scan-account.js","sourceRoot":"","sources":["../../../../src/commands/bedrock-protection/scan-account.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
description: string;
|
|
3
|
+
options: {
|
|
4
|
+
flags: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}[];
|
|
7
|
+
action(opts: {
|
|
8
|
+
ou?: string;
|
|
9
|
+
roleName?: string;
|
|
10
|
+
region?: string;
|
|
11
|
+
profile?: string;
|
|
12
|
+
maxAccounts?: string;
|
|
13
|
+
sessionDuration?: string;
|
|
14
|
+
maxKeyAge?: string;
|
|
15
|
+
unusedDays?: string;
|
|
16
|
+
skipBedrock?: boolean;
|
|
17
|
+
skipGuardrails?: boolean;
|
|
18
|
+
json?: boolean;
|
|
19
|
+
failOn?: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
export default _default;
|
|
23
|
+
//# sourceMappingURL=scan-org.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-org.d.ts","sourceRoot":"","sources":["../../../../src/commands/bedrock-protection/scan-org.ts"],"names":[],"mappings":";;;;;;iBA0JqB;QACjB,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAClE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAC;QAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAC;QACzF,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjC;;AArBH,wBAiRE"}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { aws, assumeRole, resolveAccount, PROFILE_OPTION } from '../../utils/aws.js';
|
|
2
|
+
import { DEFAULT_REGION } from '../../utils/region.js';
|
|
3
|
+
import { validateAwsRegion } from '../../utils/validate.js';
|
|
4
|
+
import { severityRank, parseFailOn, applyFailOn, FAIL_ON_OPTION, JSON_OPTION, } from '../../utils/audit.js';
|
|
5
|
+
import { auditRoot, auditPasswordPolicy, auditUsers, auditUserPolicies } from '../../utils/audit-iam.js';
|
|
6
|
+
import { auditBudgets, auditBudgetAlerting, auditDetection, auditCommitments, auditForensics } from '../../utils/audit-bedrock.js';
|
|
7
|
+
import { auditServiceControlPolicies, auditConfigRules } from '../../utils/audit-org.js';
|
|
8
|
+
/**
|
|
9
|
+
* Walk a parent (root or OU) collecting ACTIVE accounts.
|
|
10
|
+
*
|
|
11
|
+
* Always recurses into nested OUs. `list-accounts-for-parent` returns only
|
|
12
|
+
* direct children, so a non-recursive walk of an OU containing sub-OUs would
|
|
13
|
+
* silently report on a fraction of it and look complete -- the dangerous
|
|
14
|
+
* direction for an audit, so recursion is not optional.
|
|
15
|
+
*/
|
|
16
|
+
function collectAccounts(parentId, parentName, target, seen, out, errors) {
|
|
17
|
+
const accts = aws(['organizations', 'list-accounts-for-parent', '--parent-id', parentId], target);
|
|
18
|
+
if (!accts.ok) {
|
|
19
|
+
errors.push(`${parentId}: could not list accounts (${accts.errorCode || 'unknown error'})`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
for (const a of accts.json?.Accounts ?? []) {
|
|
23
|
+
if (seen.has(a.Id))
|
|
24
|
+
continue;
|
|
25
|
+
seen.add(a.Id);
|
|
26
|
+
out.push({ id: a.Id, name: a.Name ?? '(unnamed)', status: a.Status ?? 'UNKNOWN', parent: parentName });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const ous = aws(['organizations', 'list-organizational-units-for-parent', '--parent-id', parentId], target);
|
|
30
|
+
if (!ous.ok) {
|
|
31
|
+
errors.push(`${parentId}: could not list child OUs (${ous.errorCode || 'unknown error'})`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
for (const ou of ous.json?.OrganizationalUnits ?? []) {
|
|
35
|
+
collectAccounts(ou.Id, `${parentName}/${ou.Name ?? ou.Id}`, target, seen, out, errors);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function orgScopeAccounts(target, ous) {
|
|
39
|
+
const errors = [];
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
const accounts = [];
|
|
42
|
+
if (ous.length === 0) {
|
|
43
|
+
const roots = aws(['organizations', 'list-roots'], target);
|
|
44
|
+
if (!roots.ok) {
|
|
45
|
+
return { accounts, errors: [`list-roots failed (${roots.errorCode || 'unknown error'})`], scopeLabel: 'org' };
|
|
46
|
+
}
|
|
47
|
+
for (const r of roots.json?.Roots ?? []) {
|
|
48
|
+
collectAccounts(r.Id, r.Name ?? r.Id, target, seen, accounts, errors);
|
|
49
|
+
}
|
|
50
|
+
return { accounts, errors, scopeLabel: 'entire organization' };
|
|
51
|
+
}
|
|
52
|
+
for (const ou of ous) {
|
|
53
|
+
// Resolve the name for readable output; fall back to the id.
|
|
54
|
+
const desc = aws(['organizations', 'describe-organizational-unit', '--organizational-unit-id', ou], target);
|
|
55
|
+
const name = desc.ok ? (desc.json?.OrganizationalUnit?.Name ?? ou) : ou;
|
|
56
|
+
if (!desc.ok)
|
|
57
|
+
errors.push(`${ou}: could not describe OU (${desc.errorCode || 'unknown error'})`);
|
|
58
|
+
collectAccounts(ou, name, target, seen, accounts, errors);
|
|
59
|
+
}
|
|
60
|
+
return { accounts, errors, scopeLabel: `OU(s) ${ous.join(', ')}` };
|
|
61
|
+
}
|
|
62
|
+
/** Mark every IAM check for an account we could not get into. */
|
|
63
|
+
function unreachableFindings(reason) {
|
|
64
|
+
return [
|
|
65
|
+
['root-mfa', 'Root account MFA', 'CRITICAL'],
|
|
66
|
+
['root-access-keys', 'Root access keys', 'CRITICAL'],
|
|
67
|
+
['password-policy', 'Account password policy', 'MEDIUM'],
|
|
68
|
+
['access-key-age', 'Long-lived access keys', 'HIGH'],
|
|
69
|
+
['console-user-no-mfa', 'Console users without MFA', 'HIGH'],
|
|
70
|
+
['unused-credentials', 'Unused credentials', 'MEDIUM'],
|
|
71
|
+
['user-admin-policy', 'AdministratorAccess on IAM users', 'HIGH'],
|
|
72
|
+
].map(([id, title, severity]) => ({
|
|
73
|
+
id, category: 'iam', title,
|
|
74
|
+
status: 'INDETERMINATE', severity: severity,
|
|
75
|
+
detail: `Account could not be audited: ${reason}.`,
|
|
76
|
+
remediation: 'Ensure the audit role exists in this account and is assumable from the management account, then re-run.',
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
export default {
|
|
80
|
+
description: 'Audit an AWS Organization or specific OUs: per-account IAM credential hygiene plus org-wide Bedrock spend guardrails',
|
|
81
|
+
options: [
|
|
82
|
+
{ flags: '--ou <ids>', description: 'Comma-separated OU ids to scope to (default: the entire organization, recursing from every root)' },
|
|
83
|
+
{ flags: '--role-name <name>', description: 'Role to assume in each member account', default: 'OrganizationAccountAccessRole' },
|
|
84
|
+
{ flags: '--region <region>', description: 'Region for Bedrock, CloudWatch and Provisioned Throughput checks', default: DEFAULT_REGION },
|
|
85
|
+
PROFILE_OPTION,
|
|
86
|
+
{ flags: '--max-accounts <n>', description: 'Stop after this many accounts (each costs ~10 AWS API calls)', default: '50' },
|
|
87
|
+
{ flags: '--session-duration <seconds>', description: 'AssumeRole session length', default: '3600' },
|
|
88
|
+
{ flags: '--max-key-age <days>', description: 'Flag active access keys older than this', default: '90' },
|
|
89
|
+
{ flags: '--unused-days <days>', description: 'Flag credentials idle longer than this', default: '90' },
|
|
90
|
+
{ flags: '--skip-bedrock', description: 'Skip the org-wide Bedrock spend guardrail checks' },
|
|
91
|
+
{ flags: '--skip-guardrails', description: 'Skip the SCP and AWS Config organization guardrail checks' },
|
|
92
|
+
JSON_OPTION,
|
|
93
|
+
FAIL_ON_OPTION,
|
|
94
|
+
],
|
|
95
|
+
async action(opts) {
|
|
96
|
+
const region = validateAwsRegion(opts.region || DEFAULT_REGION);
|
|
97
|
+
const failOn = parseFailOn(opts.failOn);
|
|
98
|
+
const roleName = opts.roleName || 'OrganizationAccountAccessRole';
|
|
99
|
+
if (!/^[\w+=,.@-]{1,64}$/.test(roleName)) {
|
|
100
|
+
throw new Error(`--role-name must be a valid IAM role name (got "${roleName}")`);
|
|
101
|
+
}
|
|
102
|
+
const maxAccounts = Number.parseInt(opts.maxAccounts ?? '50', 10);
|
|
103
|
+
const sessionDuration = Number.parseInt(opts.sessionDuration ?? '3600', 10);
|
|
104
|
+
const maxKeyAge = Number.parseInt(opts.maxKeyAge ?? '90', 10);
|
|
105
|
+
const unusedDays = Number.parseInt(opts.unusedDays ?? '90', 10);
|
|
106
|
+
for (const [name, v, min] of [['--max-accounts', maxAccounts, 1], ['--session-duration', sessionDuration, 900], ['--max-key-age', maxKeyAge, 1], ['--unused-days', unusedDays, 1]]) {
|
|
107
|
+
if (!Number.isFinite(v) || v < min)
|
|
108
|
+
throw new Error(`${name} must be an integer >= ${min}`);
|
|
109
|
+
}
|
|
110
|
+
const ous = (opts.ou ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
111
|
+
for (const ou of ous) {
|
|
112
|
+
if (!/^ou-[0-9a-z]{4,32}-[a-z0-9]{8,32}$/.test(ou)) {
|
|
113
|
+
throw new Error(`"${ou}" is not a valid OU id (expected ou-xxxx-xxxxxxxx)`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const mgmt = { profile: opts.profile };
|
|
117
|
+
const callerAccount = resolveAccount(mgmt);
|
|
118
|
+
if (!callerAccount) {
|
|
119
|
+
process.exitCode = 2;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Confirm we are actually in the management account (or a delegated admin).
|
|
123
|
+
// Running from a member account silently returns only that account, which
|
|
124
|
+
// would look like a one-account org rather than a permissions problem.
|
|
125
|
+
const org = aws(['organizations', 'describe-organization'], mgmt);
|
|
126
|
+
if (!org.ok) {
|
|
127
|
+
console.error(`\n❌ Cannot read the organization (${org.errorCode || 'unknown error'}).`);
|
|
128
|
+
console.error(` Run this from the management account or a delegated admin, with organizations:Describe*/List* granted.\n`);
|
|
129
|
+
process.exitCode = 2;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const orgId = org.json?.Organization?.Id ?? 'unknown';
|
|
133
|
+
const payerId = org.json?.Organization?.MasterAccountId ?? '';
|
|
134
|
+
if (payerId && payerId !== callerAccount) {
|
|
135
|
+
console.log(`\n⚠️ Running from ${callerAccount}, which is not the management account (${payerId}).`);
|
|
136
|
+
console.log(` Org-wide Bedrock guardrails are evaluated against the CALLER, so budget findings may not reflect the payer.\n`);
|
|
137
|
+
}
|
|
138
|
+
const { accounts, errors, scopeLabel } = orgScopeAccounts(mgmt, ous);
|
|
139
|
+
// Attachment is judged against the audited scope, not the org at large: an
|
|
140
|
+
// SCP attached to an unrelated OU does not constrain these accounts.
|
|
141
|
+
const scopeTargetIds = ous.length
|
|
142
|
+
? ous
|
|
143
|
+
: (aws(['organizations', 'list-roots'], mgmt).json?.Roots ?? []).map((r) => r.Id);
|
|
144
|
+
const active = accounts.filter((a) => a.status === 'ACTIVE');
|
|
145
|
+
const suspended = accounts.filter((a) => a.status !== 'ACTIVE');
|
|
146
|
+
const selected = active.slice(0, maxAccounts);
|
|
147
|
+
const truncated = active.length - selected.length;
|
|
148
|
+
if (!opts.json) {
|
|
149
|
+
console.log(`\n🏛️ Bedrock protection — organization audit`);
|
|
150
|
+
console.log(` Org: ${orgId} Scope: ${scopeLabel} Region: ${region}`);
|
|
151
|
+
console.log(` ${active.length} active account(s)${suspended.length ? `, ${suspended.length} non-active skipped` : ''}${truncated > 0 ? `, ${truncated} beyond --max-accounts` : ''}`);
|
|
152
|
+
console.log(` Audit only — this command makes no changes.\n`);
|
|
153
|
+
for (const e of errors)
|
|
154
|
+
console.log(` ⚠️ ${e}`);
|
|
155
|
+
if (errors.length)
|
|
156
|
+
console.log('');
|
|
157
|
+
}
|
|
158
|
+
// ---- per-account IAM hygiene ----
|
|
159
|
+
const results = [];
|
|
160
|
+
for (const acct of selected) {
|
|
161
|
+
let target;
|
|
162
|
+
if (acct.id === callerAccount) {
|
|
163
|
+
// Cannot assume into ourselves via the org role; use ambient creds.
|
|
164
|
+
target = mgmt;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const assumed = assumeRole(acct.id, roleName, 'prism-bedrock-protection', sessionDuration, mgmt, region);
|
|
168
|
+
if ('error' in assumed) {
|
|
169
|
+
results.push({ account: acct, findings: unreachableFindings(`${roleName} not assumable (${assumed.error})`), unreachable: assumed.error });
|
|
170
|
+
if (!opts.json)
|
|
171
|
+
console.log(` ❓ ${acct.id} ${acct.name} — unreachable: ${assumed.error}`);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
target = { env: assumed.env };
|
|
175
|
+
// Confirm the session really landed in the intended account before
|
|
176
|
+
// attributing any finding to it. Misattribution is the worst failure
|
|
177
|
+
// available here -- a stale AWS_PROFILE or a mis-set env var would
|
|
178
|
+
// report the caller's own posture under a member account's name, and
|
|
179
|
+
// every number would be wrong in a way no reader could detect. Cheap
|
|
180
|
+
// insurance: one extra GetCallerIdentity per account.
|
|
181
|
+
const landed = aws(['sts', 'get-caller-identity'], target);
|
|
182
|
+
const landedId = landed.ok ? landed.json?.Account : null;
|
|
183
|
+
if (landedId !== acct.id) {
|
|
184
|
+
const why = landed.ok
|
|
185
|
+
? `session resolved to ${landedId ?? 'unknown'}, not ${acct.id}`
|
|
186
|
+
: `could not confirm identity after assuming (${landed.errorCode || 'unknown error'})`;
|
|
187
|
+
results.push({ account: acct, findings: unreachableFindings(why), unreachable: why });
|
|
188
|
+
if (!opts.json)
|
|
189
|
+
console.log(` ❓ ${acct.id} ${acct.name} — ${why}`);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const findings = [
|
|
194
|
+
...auditRoot(target),
|
|
195
|
+
auditPasswordPolicy(target),
|
|
196
|
+
...auditUsers(target, maxKeyAge, unusedDays),
|
|
197
|
+
auditUserPolicies(target),
|
|
198
|
+
];
|
|
199
|
+
results.push({ account: acct, findings });
|
|
200
|
+
if (!opts.json) {
|
|
201
|
+
const f = findings.filter((x) => x.status === 'FAIL');
|
|
202
|
+
const i = findings.filter((x) => x.status === 'INDETERMINATE');
|
|
203
|
+
const pass = findings.length - f.length - i.length;
|
|
204
|
+
const worst = f.length ? f.reduce((a, b) => (severityRank(b.severity) > severityRank(a.severity) ? b : a)).severity : '—';
|
|
205
|
+
// Absence of failures is NOT a pass. An account where every check came
|
|
206
|
+
// back indeterminate established nothing, and a green tick there is the
|
|
207
|
+
// same laundering this audit exists to prevent.
|
|
208
|
+
const icon = f.some((x) => x.severity === 'CRITICAL') ? '❌'
|
|
209
|
+
: f.length ? '⚠️ '
|
|
210
|
+
: pass === 0 ? '❓'
|
|
211
|
+
: i.length ? '⚠️ '
|
|
212
|
+
: '✅';
|
|
213
|
+
console.log(` ${icon} ${acct.id} ${acct.name.padEnd(24).slice(0, 24)} ${pass}/${findings.length} pass, ${f.length} fail (worst ${worst})${i.length ? `, ${i.length} INDETERMINATE` : ''}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// ---- org-wide Bedrock guardrails, evaluated once at the payer ----
|
|
217
|
+
const orgFindings = [];
|
|
218
|
+
if (!opts.skipBedrock) {
|
|
219
|
+
const budgets = auditBudgets(mgmt, callerAccount);
|
|
220
|
+
orgFindings.push(...budgets.findings, auditBudgetAlerting(mgmt, callerAccount, budgets.coveringBudgets), ...auditDetection(mgmt, region), auditCommitments(mgmt, region), auditForensics(mgmt, region));
|
|
221
|
+
}
|
|
222
|
+
if (!opts.skipGuardrails) {
|
|
223
|
+
orgFindings.push(...auditServiceControlPolicies(mgmt, scopeTargetIds), ...auditConfigRules(mgmt));
|
|
224
|
+
}
|
|
225
|
+
const allFindings = [...results.flatMap((r) => r.findings), ...orgFindings];
|
|
226
|
+
if (opts.json) {
|
|
227
|
+
console.log(JSON.stringify({
|
|
228
|
+
organization: orgId,
|
|
229
|
+
scope: scopeLabel,
|
|
230
|
+
region,
|
|
231
|
+
caller_account: callerAccount,
|
|
232
|
+
management_account: payerId || null,
|
|
233
|
+
role_name: roleName,
|
|
234
|
+
accounts_active: active.length,
|
|
235
|
+
accounts_audited: selected.length,
|
|
236
|
+
accounts_truncated: truncated,
|
|
237
|
+
enumeration_errors: errors,
|
|
238
|
+
generated_at: new Date().toISOString(),
|
|
239
|
+
per_account: results.map((r) => ({
|
|
240
|
+
account_id: r.account.id,
|
|
241
|
+
account_name: r.account.name,
|
|
242
|
+
ou_path: r.account.parent,
|
|
243
|
+
unreachable: r.unreachable ?? null,
|
|
244
|
+
findings: r.findings,
|
|
245
|
+
})),
|
|
246
|
+
org_wide_findings: orgFindings,
|
|
247
|
+
}, null, 2));
|
|
248
|
+
applyFailOn(allFindings, failOn, true);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// ---- org-wide section ----
|
|
252
|
+
const printGroup = (heading, note, cats) => {
|
|
253
|
+
const rows = orgFindings.filter((f) => cats.includes(f.category));
|
|
254
|
+
if (!rows.length)
|
|
255
|
+
return;
|
|
256
|
+
console.log(`\n── ${heading} ──\n`);
|
|
257
|
+
for (const n of note)
|
|
258
|
+
console.log(` ${n}`);
|
|
259
|
+
if (note.length)
|
|
260
|
+
console.log('');
|
|
261
|
+
for (const f of rows.slice().sort((a, b) => severityRank(b.severity) - severityRank(a.severity))) {
|
|
262
|
+
const icon = f.status === 'PASS' ? '✅' : f.status === 'FAIL' ? '❌' : '❓';
|
|
263
|
+
console.log(` ${icon} [${f.severity}] ${f.title}`);
|
|
264
|
+
console.log(` ${f.detail}`);
|
|
265
|
+
if (f.status !== 'PASS' && f.remediation)
|
|
266
|
+
console.log(` → ${f.remediation}`);
|
|
267
|
+
console.log('');
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
printGroup(`Org-wide Bedrock guardrails (evaluated once at ${callerAccount})`, [
|
|
271
|
+
'Under consolidated billing these bound every linked account, so they are',
|
|
272
|
+
'reported once rather than per account. A member account may still hold its',
|
|
273
|
+
'own budget, so a FAIL here does not prove no ceiling exists anywhere.',
|
|
274
|
+
], ['budget', 'detection', 'commitment', 'forensics']);
|
|
275
|
+
printGroup('Preventive guardrails — Service Control Policies', [
|
|
276
|
+
'SCPs refuse the action rather than reporting it, and bind even a compromised',
|
|
277
|
+
'admin. Detected structurally: presence and attachment, NOT enforcement.',
|
|
278
|
+
], ['preventive']);
|
|
279
|
+
printGroup('Detective guardrails — AWS Config organization rules', [
|
|
280
|
+
'This audit is a point-in-time sample. Config rules evaluate on every',
|
|
281
|
+
'configuration change, which is what catches drift between runs.',
|
|
282
|
+
], ['detective']);
|
|
283
|
+
// ---- rollup ----
|
|
284
|
+
const fail = allFindings.filter((f) => f.status === 'FAIL');
|
|
285
|
+
const indet = allFindings.filter((f) => f.status === 'INDETERMINATE');
|
|
286
|
+
const bySev = (s) => fail.filter((f) => f.severity === s).length;
|
|
287
|
+
const unreachableAccounts = results.filter((r) => r.unreachable);
|
|
288
|
+
console.log('── Organization rollup ──\n');
|
|
289
|
+
console.log(` ${selected.length} account(s) audited, ${allFindings.length} checks: ${allFindings.length - fail.length - indet.length} pass, ${fail.length} fail, ${indet.length} indeterminate`);
|
|
290
|
+
console.log(` Findings by severity: CRITICAL ${bySev('CRITICAL')}, HIGH ${bySev('HIGH')}, MEDIUM ${bySev('MEDIUM')}, LOW ${bySev('LOW')}\n`);
|
|
291
|
+
// Rank accounts so the worst is actionable first rather than buried.
|
|
292
|
+
const ranked = results
|
|
293
|
+
.map((r) => ({
|
|
294
|
+
r,
|
|
295
|
+
crit: r.findings.filter((f) => f.status === 'FAIL' && f.severity === 'CRITICAL').length,
|
|
296
|
+
high: r.findings.filter((f) => f.status === 'FAIL' && f.severity === 'HIGH').length,
|
|
297
|
+
}))
|
|
298
|
+
.filter((x) => x.crit + x.high > 0)
|
|
299
|
+
.sort((a, b) => b.crit - a.crit || b.high - a.high);
|
|
300
|
+
if (ranked.length) {
|
|
301
|
+
console.log(` Accounts with CRITICAL or HIGH findings, worst first:`);
|
|
302
|
+
for (const x of ranked) {
|
|
303
|
+
console.log(` ${x.r.account.id} ${x.r.account.name} [${x.r.account.parent}] CRITICAL ${x.crit}, HIGH ${x.high}`);
|
|
304
|
+
}
|
|
305
|
+
console.log('');
|
|
306
|
+
}
|
|
307
|
+
const establishedNothing = results.filter((r) => !r.unreachable
|
|
308
|
+
&& r.findings.every((f) => f.status === 'INDETERMINATE'));
|
|
309
|
+
if (establishedNothing.length) {
|
|
310
|
+
console.log(` ❓ ${establishedNothing.length} reachable account(s) produced NO usable checks (every result indeterminate):`);
|
|
311
|
+
for (const r of establishedNothing)
|
|
312
|
+
console.log(` ${r.account.id} ${r.account.name} — likely missing IAM read permissions on the assumed role`);
|
|
313
|
+
console.log('');
|
|
314
|
+
}
|
|
315
|
+
if (unreachableAccounts.length) {
|
|
316
|
+
console.log(` ❓ ${unreachableAccounts.length} account(s) could not be audited and are NOT passes:`);
|
|
317
|
+
for (const r of unreachableAccounts)
|
|
318
|
+
console.log(` ${r.account.id} ${r.account.name} — ${r.unreachable}`);
|
|
319
|
+
console.log(` Their IAM posture is unknown. Org coverage is ${selected.length - unreachableAccounts.length}/${selected.length}.\n`);
|
|
320
|
+
}
|
|
321
|
+
if (truncated > 0) {
|
|
322
|
+
console.log(` ⚠️ ${truncated} active account(s) were not audited because of --max-accounts ${maxAccounts}.`);
|
|
323
|
+
console.log(` Raise it to cover the whole scope; the result is a floor until you do.\n`);
|
|
324
|
+
}
|
|
325
|
+
applyFailOn(allFindings, failOn, false);
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
//# sourceMappingURL=scan-org.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-org.js","sourceRoot":"","sources":["../../../../src/commands/bedrock-protection/scan-org.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { AwsTarget } from './aws.js';
|
|
1
2
|
import { Finding } from './audit.js';
|
|
2
|
-
export declare function auditBudgets(
|
|
3
|
+
export declare function auditBudgets(target: AwsTarget | undefined, accountId: string): {
|
|
3
4
|
findings: Finding[];
|
|
4
5
|
coveringBudgets: string[];
|
|
5
6
|
};
|
|
@@ -17,8 +18,8 @@ export declare function auditBudgets(profile: string | undefined, accountId: str
|
|
|
17
18
|
* in ONE call. Subscribers need a call per notification, which is why this is
|
|
18
19
|
* scoped to the budgets that actually cover Bedrock rather than all of them.
|
|
19
20
|
*/
|
|
20
|
-
export declare function auditBudgetAlerting(
|
|
21
|
-
export declare function auditDetection(
|
|
22
|
-
export declare function auditCommitments(
|
|
23
|
-
export declare function auditForensics(
|
|
21
|
+
export declare function auditBudgetAlerting(target: AwsTarget | undefined, accountId: string, coveringBudgets: string[]): Finding;
|
|
22
|
+
export declare function auditDetection(target: AwsTarget | undefined, region: string): Finding[];
|
|
23
|
+
export declare function auditCommitments(target: AwsTarget | undefined, region: string): Finding;
|
|
24
|
+
export declare function auditForensics(target: AwsTarget | undefined, region: string): Finding;
|
|
24
25
|
//# sourceMappingURL=audit-bedrock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-bedrock.d.ts","sourceRoot":"","sources":["../../../src/utils/audit-bedrock.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audit-bedrock.d.ts","sourceRoot":"","sources":["../../../src/utils/audit-bedrock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiErC,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,CA2DjI;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CA8DT;AAWD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CA+FvF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAmBvF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAoBrF"}
|
|
@@ -58,10 +58,10 @@ function budgetCovers(budget, hints) {
|
|
|
58
58
|
function isCostBudget(budget) {
|
|
59
59
|
return budget?.BudgetType === 'COST';
|
|
60
60
|
}
|
|
61
|
-
export function auditBudgets(
|
|
61
|
+
export function auditBudgets(target, accountId) {
|
|
62
62
|
// describe-budgets requires an explicit account id, and returns an EMPTY body
|
|
63
63
|
// -- not {"Budgets": []} -- when no budgets exist.
|
|
64
|
-
const bud = aws(['budgets', 'describe-budgets', '--account-id', accountId],
|
|
64
|
+
const bud = aws(['budgets', 'describe-budgets', '--account-id', accountId], target);
|
|
65
65
|
if (!bud.ok) {
|
|
66
66
|
return { coveringBudgets: [], findings: [
|
|
67
67
|
['bedrock-budget', 'Budget covering Bedrock services'],
|
|
@@ -125,7 +125,7 @@ export function auditBudgets(profile, accountId) {
|
|
|
125
125
|
* in ONE call. Subscribers need a call per notification, which is why this is
|
|
126
126
|
* scoped to the budgets that actually cover Bedrock rather than all of them.
|
|
127
127
|
*/
|
|
128
|
-
export function auditBudgetAlerting(
|
|
128
|
+
export function auditBudgetAlerting(target, accountId, coveringBudgets) {
|
|
129
129
|
const id = 'budget-alerting';
|
|
130
130
|
const title = 'Bedrock budgets actually notify someone';
|
|
131
131
|
if (coveringBudgets.length === 0) {
|
|
@@ -138,7 +138,7 @@ export function auditBudgetAlerting(profile, accountId, coveringBudgets) {
|
|
|
138
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
139
|
};
|
|
140
140
|
}
|
|
141
|
-
const all = aws(['budgets', 'describe-budget-notifications-for-account', '--account-id', accountId],
|
|
141
|
+
const all = aws(['budgets', 'describe-budget-notifications-for-account', '--account-id', accountId], target);
|
|
142
142
|
if (!all.ok) {
|
|
143
143
|
return {
|
|
144
144
|
id, category: 'budget', title,
|
|
@@ -164,7 +164,7 @@ export function auditBudgetAlerting(profile, accountId, coveringBudgets) {
|
|
|
164
164
|
'budgets', 'describe-subscribers-for-notification',
|
|
165
165
|
'--account-id', accountId, '--budget-name', name,
|
|
166
166
|
'--notification', JSON.stringify(n),
|
|
167
|
-
],
|
|
167
|
+
], target);
|
|
168
168
|
if (subs.ok)
|
|
169
169
|
subscribers += (subs.json?.Subscribers ?? []).length;
|
|
170
170
|
}
|
|
@@ -197,14 +197,14 @@ export function auditBudgetAlerting(profile, accountId, coveringBudgets) {
|
|
|
197
197
|
* depending on account state, which is unstable in --json output.
|
|
198
198
|
*/
|
|
199
199
|
const ANOMALY_TITLE = 'Cost anomaly monitor covering Bedrock, with an alert subscription';
|
|
200
|
-
export function auditDetection(
|
|
200
|
+
export function auditDetection(target, region) {
|
|
201
201
|
const out = [];
|
|
202
202
|
// Cost Explorer is an account-level opt-in that stays OFF until someone
|
|
203
203
|
// enables it -- measured error: AccessDeniedException "User not enabled for
|
|
204
204
|
// cost explorer access". Every CE-backed check must be gated on this, or the
|
|
205
205
|
// audit reports "no anomaly monitor configured" when the truth is that it
|
|
206
206
|
// could not look.
|
|
207
|
-
const ce = aws(['ce', 'get-anomaly-monitors'],
|
|
207
|
+
const ce = aws(['ce', 'get-anomaly-monitors'], target);
|
|
208
208
|
const ceDisabled = !ce.ok && /not enabled for cost explorer/i.test(ce.raw);
|
|
209
209
|
out.push({
|
|
210
210
|
id: 'cost-explorer-enabled', category: 'detection', title: 'Cost Explorer enabled',
|
|
@@ -240,7 +240,7 @@ export function auditDetection(profile, region) {
|
|
|
240
240
|
// then auto-creates a "Default-Services-Monitor" (SERVICE dimension) WITH a
|
|
241
241
|
// subscription -- observed live. So this check often passes as a side effect
|
|
242
242
|
// of setting up budgets rather than deliberately.
|
|
243
|
-
const subs = aws(['ce', 'get-anomaly-subscriptions'],
|
|
243
|
+
const subs = aws(['ce', 'get-anomaly-subscriptions'], target);
|
|
244
244
|
const coveringArns = new Set(covering.map((m) => m.MonitorArn));
|
|
245
245
|
const subscribed = subs.ok
|
|
246
246
|
? (subs.json?.AnomalySubscriptions ?? []).some((s) => (s.MonitorArnList ?? []).some((arn) => coveringArns.has(arn)))
|
|
@@ -266,7 +266,7 @@ export function auditDetection(profile, region) {
|
|
|
266
266
|
}
|
|
267
267
|
// CloudWatch is the only fast signal. Cost data lags 12-24h, so a cost-only
|
|
268
268
|
// posture lets a stolen key run for up to a day before anything fires.
|
|
269
|
-
const alarms = aws(['cloudwatch', 'describe-alarms', '--region', region],
|
|
269
|
+
const alarms = aws(['cloudwatch', 'describe-alarms', '--region', region], target);
|
|
270
270
|
if (!alarms.ok) {
|
|
271
271
|
out.push({
|
|
272
272
|
id: 'bedrock-invocation-alarm', category: 'detection', title: 'CloudWatch alarm on Bedrock invocation volume',
|
|
@@ -287,8 +287,8 @@ export function auditDetection(profile, region) {
|
|
|
287
287
|
}
|
|
288
288
|
return out;
|
|
289
289
|
}
|
|
290
|
-
export function auditCommitments(
|
|
291
|
-
const pt = aws(['bedrock', 'list-provisioned-model-throughputs', '--region', region],
|
|
290
|
+
export function auditCommitments(target, region) {
|
|
291
|
+
const pt = aws(['bedrock', 'list-provisioned-model-throughputs', '--region', region], target);
|
|
292
292
|
if (!pt.ok) {
|
|
293
293
|
return {
|
|
294
294
|
id: 'provisioned-throughput', category: 'commitment', title: 'Provisioned Throughput commitments',
|
|
@@ -307,9 +307,9 @@ export function auditCommitments(profile, region) {
|
|
|
307
307
|
remediation: pts.length ? 'Confirm each commitment is intentional and still in use; an abandoned one bills indefinitely.' : undefined,
|
|
308
308
|
};
|
|
309
309
|
}
|
|
310
|
-
export function auditForensics(
|
|
310
|
+
export function auditForensics(target, region) {
|
|
311
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],
|
|
312
|
+
const log = aws(['bedrock', 'get-model-invocation-logging-configuration', '--region', region], target);
|
|
313
313
|
if (!log.ok) {
|
|
314
314
|
return {
|
|
315
315
|
id: 'model-invocation-logging', category: 'forensics', title: 'Bedrock model invocation logging',
|