@prism-d1/cli 1.0.95 → 1.0.97

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,311 @@
1
+ import { aws } from './aws.js';
2
+ function asArray(v) {
3
+ if (!v)
4
+ return [];
5
+ return Array.isArray(v) ? v : [v];
6
+ }
7
+ /**
8
+ * IAM action glob matching. `bedrock:*` covers
9
+ * `bedrock:CreateProvisionedModelThroughput`; a bare `*` covers everything.
10
+ * Without this, a broad deny would read as no coverage at all.
11
+ */
12
+ function actionMatches(pattern, action) {
13
+ if (pattern === '*')
14
+ return true;
15
+ const rx = new RegExp('^' + pattern.split('*').map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('.*') + '$', 'i');
16
+ return rx.test(action);
17
+ }
18
+ function parseStatements(content) {
19
+ try {
20
+ const doc = JSON.parse(content);
21
+ const st = doc?.Statement;
22
+ if (!st)
23
+ return [];
24
+ return Array.isArray(st) ? st : [st];
25
+ }
26
+ catch {
27
+ return [];
28
+ }
29
+ }
30
+ /** Names of policies containing a Deny that covers any of `actions`. */
31
+ export function policiesDenying(policies, actions) {
32
+ return policies.filter((p) => p.statements.some((s) => {
33
+ if (s.Effect !== 'Deny')
34
+ return false;
35
+ const pats = asArray(s.Action);
36
+ return actions.some((a) => pats.some((pat) => actionMatches(pat, a)));
37
+ })).map((p) => p.name);
38
+ }
39
+ /**
40
+ * Names of policies whose Deny is region-conditioned in a way that would
41
+ * constrain Bedrock.
42
+ *
43
+ * The common shape is a broad `Deny *` with
44
+ * `StringNotEquals: { "aws:RequestedRegion": [...] }` -- everything outside the
45
+ * listed regions is refused. A Bedrock-specific variant denies `bedrock:*` under
46
+ * the same condition. Both count; a region condition that cannot reach Bedrock
47
+ * (e.g. `Action: s3:*`) does not.
48
+ */
49
+ export function policiesRestrictingBedrockRegion(policies) {
50
+ return policies.filter((p) => p.statements.some((s) => {
51
+ if (s.Effect !== 'Deny')
52
+ return false;
53
+ const cond = s.Condition ?? {};
54
+ const mentionsRegion = Object.values(cond).some((kv) => Object.keys(kv).some((k) => k.toLowerCase() === 'aws:requestedregion'));
55
+ if (!mentionsRegion)
56
+ return false;
57
+ const pats = asArray(s.Action);
58
+ // Must be able to reach a Bedrock call to be relevant here.
59
+ return pats.some((pat) => actionMatches(pat, 'bedrock:InvokeModel'));
60
+ })).map((p) => p.name);
61
+ }
62
+ const ENFORCEMENT_CAVEAT = 'Detected structurally, not proven: inheritance, NotAction and principal-tag exemptions can neutralise a policy that reads correctly. Confirm with a live call from a representative role.';
63
+ /**
64
+ * Expand each audited target to itself plus every ancestor up to the root.
65
+ *
66
+ * SCPs INHERIT downward: a policy attached to the root constrains every account
67
+ * in every OU beneath it. Judging attachment against the audited OU alone
68
+ * therefore ignores the most common place guardrails actually live, and would
69
+ * report a root-attached Bedrock region deny as absent -- a false FAIL on the
70
+ * highest-leverage control in this file. Verified against a live org where two
71
+ * of four customer SCPs sat at the root and were invisible to the OU-only view.
72
+ */
73
+ export function expandToAncestors(target, scopeTargetIds) {
74
+ const ids = new Set(scopeTargetIds);
75
+ const errors = [];
76
+ for (const start of scopeTargetIds) {
77
+ let child = start;
78
+ // Bounded rather than while(true): OU nesting is capped at 5 levels, and a
79
+ // cycle here would otherwise hang the audit.
80
+ for (let depth = 0; depth < 10; depth++) {
81
+ if (/^r-/.test(child))
82
+ break;
83
+ const parents = aws(['organizations', 'list-parents', '--child-id', child], target);
84
+ if (!parents.ok) {
85
+ errors.push(`${child}: could not resolve parents (${parents.errorCode || 'unknown error'}), so inherited SCPs may be missed`);
86
+ break;
87
+ }
88
+ const p = (parents.json?.Parents ?? [])[0];
89
+ if (!p?.Id)
90
+ break;
91
+ ids.add(p.Id);
92
+ child = p.Id;
93
+ }
94
+ }
95
+ return { ids: [...ids], errors };
96
+ }
97
+ /**
98
+ * Managed Config rule identifiers that continuously assert what the IAM checks
99
+ * in this audit only sample. Keyed by the check they correspond to, so the
100
+ * output can name the gap in terms the reader already recognises.
101
+ */
102
+ const IAM_CONFIG_RULES = [
103
+ ['ACCESS_KEYS_ROTATED', 'access-key-age'],
104
+ ['IAM_USER_UNUSED_CREDENTIALS_CHECK', 'unused-credentials'],
105
+ ['ROOT_ACCOUNT_MFA_ENABLED', 'root-mfa'],
106
+ ['MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS', 'console-user-no-mfa'],
107
+ ['IAM_PASSWORD_POLICY', 'password-policy'],
108
+ ['IAM_USER_NO_POLICIES_CHECK', 'user-admin-policy'],
109
+ ];
110
+ /** The four SCP checks whose verdict depends on knowing the audited scope. */
111
+ const SCOPE_DEPENDENT_SCP_CHECKS = [
112
+ ['scp-attached', 'SCPs attached to the audited scope', 'HIGH'],
113
+ ['scp-detection-tamper', 'SCP preventing tampering with detection', 'HIGH'],
114
+ ['scp-bedrock-region', 'SCP restricting which regions Bedrock can be used in', 'MEDIUM'],
115
+ ['scp-bedrock-provisioned-throughput', 'SCP denying Provisioned Throughput creation', 'MEDIUM'],
116
+ ];
117
+ function scopeDependentIndeterminate(detail, remediation) {
118
+ return SCOPE_DEPENDENT_SCP_CHECKS.map(([id, title, severity]) => ({
119
+ id, category: 'preventive', title,
120
+ status: 'INDETERMINATE', severity,
121
+ detail, remediation,
122
+ }));
123
+ }
124
+ /**
125
+ * @param scopeTargetIds Root and OU ids in scope, so attachment is judged
126
+ * against what is actually being audited rather than the org at large.
127
+ * `null` means the caller could not resolve the scope -- every
128
+ * attachment-dependent check is then INDETERMINATE, because an empty
129
+ * scope would otherwise make all four FAIL and present a permissions
130
+ * problem as four real absences.
131
+ */
132
+ export function auditServiceControlPolicies(target, scopeTargetIds, scopeError) {
133
+ const out = [];
134
+ // SCPs must be enabled on the root before any can take effect. This gates the
135
+ // rest, the same way Cost Explorer gates the anomaly check.
136
+ const roots = aws(['organizations', 'list-roots'], target);
137
+ if (!roots.ok) {
138
+ const why = `Could not read organization roots (${roots.errorCode || 'unknown error'}).`;
139
+ const fix = 'Grant organizations:ListRoots and re-run.';
140
+ // Emit EVERY SCP check, not just this one. Returning a single finding here
141
+ // would silently shrink the check count, so a reader comparing runs would
142
+ // see four checks disappear rather than four checks it could not answer.
143
+ return [
144
+ {
145
+ id: 'scp-enabled', category: 'preventive', title: 'Service Control Policies enabled',
146
+ status: 'INDETERMINATE', severity: 'HIGH', detail: why, remediation: fix,
147
+ },
148
+ ...scopeDependentIndeterminate(why, fix),
149
+ ];
150
+ }
151
+ const rootList = roots.json?.Roots ?? [];
152
+ const scpEnabled = rootList.some((r) => (r.PolicyTypes ?? [])
153
+ .some((p) => p.Type === 'SERVICE_CONTROL_POLICY' && p.Status === 'ENABLED'));
154
+ out.push({
155
+ id: 'scp-enabled', category: 'preventive', title: 'Service Control Policies enabled',
156
+ status: scpEnabled ? 'PASS' : 'FAIL', severity: 'HIGH',
157
+ detail: scpEnabled
158
+ ? 'SERVICE_CONTROL_POLICY is enabled on the organization root.'
159
+ : 'SERVICE_CONTROL_POLICY is not enabled, so no preventive guardrail can exist at any level. Every control in this organization is therefore detective — it can tell you a stolen key was used, never refuse the call.',
160
+ remediation: scpEnabled ? undefined : 'Enable the SERVICE_CONTROL_POLICY type on the root (Organizations → Policies). Enabling it alone changes nothing: FullAWSAccess stays attached and no action is newly denied.',
161
+ });
162
+ if (!scpEnabled) {
163
+ out.push(...scopeDependentIndeterminate('Cannot be evaluated while Service Control Policies are disabled.'));
164
+ return out;
165
+ }
166
+ // Scope unknown: an empty target list would make all four attachment-dependent
167
+ // checks FAIL, dressing a permissions problem up as four real absences.
168
+ if (scopeTargetIds === null) {
169
+ out.push(...scopeDependentIndeterminate(`The audited scope could not be resolved${scopeError ? ` (${scopeError})` : ''}, so it is unknown which SCPs apply to it.`, 'Grant organizations:ListRoots and re-run.'));
170
+ return out;
171
+ }
172
+ // Include every ancestor: SCPs attached above the audited OU still apply to it.
173
+ const { ids: effectiveTargets, errors: ancestorErrors } = expandToAncestors(target, scopeTargetIds);
174
+ const list = aws(['organizations', 'list-policies', '--filter', 'SERVICE_CONTROL_POLICY'], target);
175
+ if (!list.ok) {
176
+ out.push(...scopeDependentIndeterminate(`Could not list SCPs (${list.errorCode || 'unknown error'}).`, 'Grant organizations:ListPolicies, DescribePolicy and ListTargetsForPolicy, then re-run.'));
177
+ return out;
178
+ }
179
+ // FullAWSAccess is attached everywhere by default and allows everything, so it
180
+ // is never evidence of a guardrail. Excluding it keeps the attachment check
181
+ // from passing on AWS's own default.
182
+ const customer = (list.json?.Policies ?? []).filter((p) => p.AwsManaged !== true);
183
+ const policies = [];
184
+ let unreadable = 0;
185
+ for (const p of customer) {
186
+ const desc = aws(['organizations', 'describe-policy', '--policy-id', p.Id], target);
187
+ const targets = aws(['organizations', 'list-targets-for-policy', '--policy-id', p.Id], target);
188
+ if (!desc.ok || !targets.ok) {
189
+ unreadable++;
190
+ continue;
191
+ }
192
+ policies.push({
193
+ name: p.Name ?? p.Id,
194
+ id: p.Id,
195
+ statements: parseStatements(desc.json?.Policy?.Content ?? ''),
196
+ targets: (targets.json?.Targets ?? []).map((t) => t.TargetId),
197
+ });
198
+ }
199
+ // An SCP attached nowhere in the audited scope constrains nothing there --
200
+ // the same shape as a budget with no subscriber, and just as easy to mistake
201
+ // for protection.
202
+ const inScope = policies.filter((p) => p.targets.some((t) => effectiveTargets.includes(t)));
203
+ out.push({
204
+ id: 'scp-attached', category: 'preventive', title: 'SCPs attached to the audited scope',
205
+ status: inScope.length ? 'PASS' : 'FAIL', severity: 'HIGH',
206
+ detail: inScope.length
207
+ ? `${inScope.length} customer-managed SCP(s) apply to the audited scope (including inheritance from ${effectiveTargets.length - scopeTargetIds.length} ancestor target(s)): ${inScope.map((p) => p.name).join(', ')}.`
208
+ : customer.length === 0
209
+ ? 'No customer-managed SCPs exist. Only AWS\'s default FullAWSAccess is attached, which allows everything.'
210
+ : `${customer.length} customer-managed SCP(s) exist but none is attached to the audited target(s) or any ancestor, so none constrains the accounts in scope.`,
211
+ remediation: inScope.length ? undefined : 'Attach the relevant SCP to the root or the OUs being audited. An unattached policy is inert.',
212
+ });
213
+ // Region restriction: the highest-leverage preventive control for LLMjacking.
214
+ const regionScoped = policiesRestrictingBedrockRegion(inScope);
215
+ out.push({
216
+ id: 'scp-bedrock-region', category: 'preventive', title: 'SCP restricting which regions Bedrock can be used in',
217
+ status: regionScoped.length ? 'PASS' : 'FAIL', severity: 'MEDIUM',
218
+ detail: regionScoped.length
219
+ ? `${regionScoped.join(', ')} denies Bedrock-reaching actions outside an allowed region set. ${ENFORCEMENT_CAVEAT}`
220
+ : 'No in-scope SCP constrains Bedrock by region. A stolen credential can call InvokeModel in every enabled region, so spend is not bounded by the regions you actually operate in — and per-region CloudWatch alarms and Provisioned Throughput checks all miss activity elsewhere.',
221
+ remediation: regionScoped.length ? undefined : 'Add a Deny on `*` (or `bedrock:*`) with StringNotEquals on aws:RequestedRegion listing only the regions you use. This is the cheapest large reduction in blast radius available, and it costs nothing to run.',
222
+ });
223
+ const ptDenied = policiesDenying(inScope, ['bedrock:CreateProvisionedModelThroughput']);
224
+ out.push({
225
+ id: 'scp-bedrock-provisioned-throughput', category: 'preventive', title: 'SCP denying Provisioned Throughput creation',
226
+ status: ptDenied.length ? 'PASS' : 'FAIL', severity: 'MEDIUM',
227
+ detail: ptDenied.length
228
+ ? `${ptDenied.join(', ')} denies bedrock:CreateProvisionedModelThroughput. ${ENFORCEMENT_CAVEAT}`
229
+ : 'No in-scope SCP denies bedrock:CreateProvisionedModelThroughput. It is the largest single commitment available in Bedrock — billed monthly whether used or not — and one API call creates it.',
230
+ remediation: ptDenied.length ? undefined : 'Deny bedrock:CreateProvisionedModelThroughput org-wide and exempt only the accounts that genuinely need it. Most organizations need it nowhere.',
231
+ });
232
+ // Protecting the detection layer the rest of this audit depends on.
233
+ const tamperActions = ['cloudtrail:StopLogging', 'config:DeleteConfigRule', 'guardduty:DeleteDetector'];
234
+ const tamperDenied = policiesDenying(inScope, tamperActions);
235
+ out.push({
236
+ id: 'scp-detection-tamper', category: 'preventive', title: 'SCP preventing tampering with detection',
237
+ status: tamperDenied.length ? 'PASS' : 'FAIL', severity: 'HIGH',
238
+ detail: tamperDenied.length
239
+ ? `${tamperDenied.join(', ')} denies at least one of CloudTrail/Config/GuardDuty teardown. ${ENFORCEMENT_CAVEAT}`
240
+ : 'No in-scope SCP prevents stopping CloudTrail, deleting Config rules or disabling GuardDuty. An attacker who reaches an admin credential can therefore remove the evidence trail before running up spend, which defeats every detective control this audit recommends.',
241
+ remediation: tamperDenied.length ? undefined : 'Deny cloudtrail:StopLogging/DeleteTrail, config:Delete*, guardduty:DeleteDetector org-wide. Nothing legitimate needs them from a member account.',
242
+ });
243
+ for (const e of ancestorErrors) {
244
+ out.push({
245
+ id: 'scp-inheritance-resolved', category: 'preventive', title: 'SCP inheritance fully resolved',
246
+ status: 'INDETERMINATE', severity: 'MEDIUM',
247
+ detail: e,
248
+ remediation: 'Grant organizations:ListParents and re-run; without it an inherited guardrail can read as absent.',
249
+ });
250
+ }
251
+ if (unreadable > 0) {
252
+ out.push({
253
+ id: 'scp-readable', category: 'preventive', title: 'All SCPs readable',
254
+ status: 'INDETERMINATE', severity: 'MEDIUM',
255
+ detail: `${unreadable} of ${customer.length} customer-managed SCP(s) could not be read, so the SCP findings above are based on a partial view.`,
256
+ remediation: 'Grant organizations:DescribePolicy and ListTargetsForPolicy, then re-run.',
257
+ });
258
+ }
259
+ return out;
260
+ }
261
+ export function auditConfigRules(target) {
262
+ const rules = aws(['configservice', 'describe-organization-config-rules'], target);
263
+ if (!rules.ok) {
264
+ // Config not being enabled for the org is a legitimate state, and distinct
265
+ // from a permissions problem. Both are reported, neither as a pass.
266
+ const notEnabled = /OrganizationAccessDenied|NoAvailableOrganization|AccessDenied/i.test(rules.errorCode || rules.raw);
267
+ return [{
268
+ id: 'config-org-rules', category: 'detective', title: 'Organization Config rules deployed',
269
+ status: 'INDETERMINATE', severity: 'MEDIUM',
270
+ detail: notEnabled
271
+ ? `Could not read organization Config rules (${rules.errorCode || 'access denied'}). Either AWS Config is not enabled for the organization, or trusted access / delegated administration is not configured.`
272
+ : `Could not read organization Config rules (${rules.errorCode || 'unknown error'}).`,
273
+ remediation: 'Enable trusted access for config.amazonaws.com in Organizations, or grant config:DescribeOrganizationConfigRules, then re-run.',
274
+ }, {
275
+ id: 'config-iam-coverage', category: 'detective', title: 'Continuous coverage of the IAM hygiene checks',
276
+ status: 'INDETERMINATE', severity: 'MEDIUM',
277
+ detail: 'Cannot be evaluated without the organization Config rule list.',
278
+ }];
279
+ }
280
+ const list = rules.json?.OrganizationConfigRules ?? [];
281
+ const out = [];
282
+ out.push({
283
+ id: 'config-org-rules', category: 'detective', title: 'Organization Config rules deployed',
284
+ status: list.length ? 'PASS' : 'FAIL', severity: 'MEDIUM',
285
+ detail: list.length
286
+ ? `${list.length} organization Config rule(s) deployed.`
287
+ : 'No organization Config rules. This audit is a point-in-time sample; without continuous rules, a key created the day after a run goes unnoticed until the next one.',
288
+ remediation: list.length ? undefined : 'Deploy an Organization Conformance Pack with the IAM managed rules. They evaluate on every configuration change rather than when someone remembers to audit.',
289
+ });
290
+ // Which of this audit's own IAM checks have a continuous equivalent deployed.
291
+ const deployed = new Set();
292
+ for (const r of list) {
293
+ const id = r?.OrganizationManagedRuleMetadata?.RuleIdentifier;
294
+ if (id)
295
+ deployed.add(String(id).toUpperCase());
296
+ }
297
+ const missing = IAM_CONFIG_RULES.filter(([ruleId]) => !deployed.has(ruleId));
298
+ const present = IAM_CONFIG_RULES.filter(([ruleId]) => deployed.has(ruleId));
299
+ out.push({
300
+ id: 'config-iam-coverage', category: 'detective', title: 'Continuous coverage of the IAM hygiene checks',
301
+ status: missing.length === 0 ? 'PASS' : list.length === 0 ? 'FAIL' : 'FAIL',
302
+ severity: 'MEDIUM',
303
+ detail: missing.length === 0
304
+ ? `All ${IAM_CONFIG_RULES.length} IAM hygiene checks have a continuously-evaluating Config rule: ${present.map(([r]) => r).join(', ')}.`
305
+ : `${missing.length} of ${IAM_CONFIG_RULES.length} IAM checks in this audit have no continuous equivalent. Sampled here but not monitored: ${missing.map(([, check]) => check).join(', ')}.${present.length ? ` Covered: ${present.map(([, check]) => check).join(', ')}.` : ''}`,
306
+ remediation: missing.length === 0 ? undefined
307
+ : `Deploy the matching managed rules org-wide: ${missing.map(([r]) => r).join(', ')}. Note the ordering pitfall — a Config rule evaluates nothing until the configuration recorder is running in the target accounts.`,
308
+ });
309
+ return out;
310
+ }
311
+ //# sourceMappingURL=audit-org.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-org.js","sourceRoot":"","sources":["../../../src/utils/audit-org.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,23 @@
5
5
  * exposed to shell interpretation even though callers pass a user-supplied
6
6
  * --profile and --region.
7
7
  */
8
+ /**
9
+ * Where an AWS call should be made: a named CLI profile, or temporary
10
+ * credentials from an assumed role.
11
+ *
12
+ * Both are carried in one object so every check function is agnostic about how
13
+ * the caller authenticated. `scan-account` supplies a profile; `scan-org`
14
+ * supplies assumed-role credentials per member account. Credentials travel in
15
+ * the child process environment rather than being written to `~/.aws/config`,
16
+ * so nothing is persisted and the assumed role cannot leak into a later call.
17
+ */
18
+ export interface AwsTarget {
19
+ /** AWS CLI profile name. */
20
+ profile?: string;
21
+ /** Temporary credentials injected into the child environment. A key mapped
22
+ * to undefined is removed from it. */
23
+ env?: Record<string, string | undefined>;
24
+ }
8
25
  export interface AwsResult {
9
26
  ok: boolean;
10
27
  json: any;
@@ -21,7 +38,21 @@ export interface AwsResult {
21
38
  * precisely the accounts that most need auditing. Empty output is normalised to
22
39
  * {} here and interpreted by each individual check.
23
40
  */
24
- export declare function aws(args: string[], profile?: string): AwsResult;
41
+ export declare function aws(args: string[], target?: AwsTarget): AwsResult;
42
+ /**
43
+ * Assume a role and return credentials as an env fragment, or an error string.
44
+ *
45
+ * Deliberately returns the failure rather than throwing: at org scale a role
46
+ * that cannot be assumed is an expected condition (an account onboarded without
47
+ * the role, an SCP denying it), and the caller must surface that account as
48
+ * INDETERMINATE rather than dropping it. A silently skipped account is exactly
49
+ * how an org audit ends up reporting on a subset while looking complete.
50
+ */
51
+ export declare function assumeRole(accountId: string, roleName: string, sessionName: string, durationSeconds: number, from?: AwsTarget, region?: string): {
52
+ env: Record<string, string | undefined>;
53
+ } | {
54
+ error: string;
55
+ };
25
56
  /**
26
57
  * Resolve the caller's account id, or print guidance and return null.
27
58
  *
@@ -29,7 +60,7 @@ export declare function aws(args: string[], profile?: string): AwsResult;
29
60
  * --profile fails in the CLI's own config layer and never reaches the service,
30
61
  * so fall back to its stderr rather than reporting "unknown error".
31
62
  */
32
- export declare function resolveAccount(profile?: string): string | null;
63
+ export declare function resolveAccount(target?: AwsTarget): string | null;
33
64
  export declare function daysSince(iso: string): number | null;
34
65
  export declare const PROFILE_OPTION: {
35
66
  flags: string;
@@ -1 +1 @@
1
- {"version":3,"file":"aws.d.ts","sourceRoot":"","sources":["../../../src/utils/aws.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;IACV,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAe/D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS9D;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIpD;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAC"}
1
+ {"version":3,"file":"aws.d.ts","sourceRoot":"","sources":["../../../src/utils/aws.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;2CACuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;IACV,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAejE;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,IAAI,CAAC,EAAE,SAAS,EAChB,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAiCjE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAShE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIpD;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAC"}
@@ -14,9 +14,9 @@ import { run } from './exec.js';
14
14
  * precisely the accounts that most need auditing. Empty output is normalised to
15
15
  * {} here and interpreted by each individual check.
16
16
  */
17
- export function aws(args, profile) {
18
- const argv = profile ? [...args, '--profile', profile] : args;
19
- const res = run('aws', [...argv, '--output', 'json']);
17
+ export function aws(args, target) {
18
+ const argv = target?.profile ? [...args, '--profile', target.profile] : args;
19
+ const res = run('aws', [...argv, '--output', 'json'], target?.env);
20
20
  const body = (res.stdout || '').trim();
21
21
  if (!res.ok) {
22
22
  const m = /An error occurred \(([A-Za-z0-9.]+)\)/.exec(res.stderr || '');
@@ -31,6 +31,48 @@ export function aws(args, profile) {
31
31
  return { ok: false, json: null, errorCode: 'UnparseableResponse', raw: body };
32
32
  }
33
33
  }
34
+ /**
35
+ * Assume a role and return credentials as an env fragment, or an error string.
36
+ *
37
+ * Deliberately returns the failure rather than throwing: at org scale a role
38
+ * that cannot be assumed is an expected condition (an account onboarded without
39
+ * the role, an SCP denying it), and the caller must surface that account as
40
+ * INDETERMINATE rather than dropping it. A silently skipped account is exactly
41
+ * how an org audit ends up reporting on a subset while looking complete.
42
+ */
43
+ export function assumeRole(accountId, roleName, sessionName, durationSeconds, from, region) {
44
+ const res = aws([
45
+ 'sts', 'assume-role',
46
+ '--role-arn', `arn:aws:iam::${accountId}:role/${roleName}`,
47
+ '--role-session-name', sessionName,
48
+ '--duration-seconds', String(durationSeconds),
49
+ ], from);
50
+ if (!res.ok) {
51
+ const reason = res.errorCode
52
+ || (res.raw.split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? 'unknown error');
53
+ return { error: reason };
54
+ }
55
+ const c = res.json?.Credentials;
56
+ if (!c?.AccessKeyId || !c?.SecretAccessKey || !c?.SessionToken) {
57
+ return { error: 'assume-role returned no usable credentials' };
58
+ }
59
+ return {
60
+ env: {
61
+ AWS_ACCESS_KEY_ID: c.AccessKeyId,
62
+ AWS_SECRET_ACCESS_KEY: c.SecretAccessKey,
63
+ AWS_SESSION_TOKEN: c.SessionToken,
64
+ // REMOVED, not blanked. A profile left in the ambient environment takes
65
+ // precedence over the injected keys and would silently audit the WRONG
66
+ // account -- the worst failure available here, since every finding would
67
+ // be misattributed. Blanking instead of removing breaks the CLI outright.
68
+ AWS_PROFILE: undefined,
69
+ AWS_DEFAULT_PROFILE: undefined,
70
+ // The unset profile also took its region with it, so pin one explicitly
71
+ // rather than depending on whatever the CLI can still resolve.
72
+ ...(region ? { AWS_REGION: region, AWS_DEFAULT_REGION: region } : {}),
73
+ },
74
+ };
75
+ }
34
76
  /**
35
77
  * Resolve the caller's account id, or print guidance and return null.
36
78
  *
@@ -38,8 +80,8 @@ export function aws(args, profile) {
38
80
  * --profile fails in the CLI's own config layer and never reaches the service,
39
81
  * so fall back to its stderr rather than reporting "unknown error".
40
82
  */
41
- export function resolveAccount(profile) {
42
- const who = aws(['sts', 'get-caller-identity'], profile);
83
+ export function resolveAccount(target) {
84
+ const who = aws(['sts', 'get-caller-identity'], target);
43
85
  if (who.ok)
44
86
  return who.json?.Account ?? null;
45
87
  const reason = who.errorCode
@@ -1 +1 @@
1
- {"version":3,"file":"aws.js","sourceRoot":"","sources":["../../../src/utils/aws.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAWhC;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,CAAC,IAAc,EAAE,OAAgB;IAClD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAEvC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,uCAAuC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QACzE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;IACpF,CAAC;IACD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;IACjE,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAChF,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,qBAAqB,CAAC,EAAE,OAAO,CAAC,CAAC;IACzD,IAAI,GAAG,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;IAE7C,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS;WACvB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,eAAe,CAAC,CAAC;IACzF,OAAO,CAAC,KAAK,CAAC,oCAAoC,MAAM,EAAE,CAAC,CAAC;IAC5D,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACvE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,KAAK,EAAE,kBAAkB;IACzB,WAAW,EAAE,gEAAgE;CAC9E,CAAC"}
1
+ {"version":3,"file":"aws.js","sourceRoot":"","sources":["../../../src/utils/aws.ts"],"names":[],"mappings":"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"}
@@ -42,10 +42,21 @@ export interface RunResult {
42
42
  * latter would arrive as a single argument and the command would fail, which
43
43
  * is a loud failure rather than a silent injection.
44
44
  *
45
+ * `extraEnv` is merged over the inherited environment. It exists so a caller
46
+ * can hand a child process temporary STS credentials without writing them to
47
+ * disk or mutating this process's own environment -- the latter would leak the
48
+ * assumed role into every later call in the same run.
49
+ *
50
+ * A key mapped to `undefined` is REMOVED rather than set. Blanking is not
51
+ * equivalent: `AWS_PROFILE=''` makes the AWS CLI look for a profile literally
52
+ * named `()` and fail with "The config profile () could not be found", so
53
+ * injected credentials are ignored and every call errors. Measured, not
54
+ * theoretical.
55
+ *
45
56
  * Never throws: a non-zero exit, a missing binary, and a signal kill all come
46
57
  * back as `ok: false`. Callers branch on `ok` and surface `stderr`.
47
58
  */
48
- export declare function run(file: string, args: string[]): RunResult;
59
+ export declare function run(file: string, args: string[], extraEnv?: Record<string, string | undefined>): RunResult;
49
60
  /**
50
61
  * Like run(), but inherits stdio so the child can drive the terminal.
51
62
  *
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/utils/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAW3D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAOpE"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/utils/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAmB1G;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAOpE"}
@@ -38,15 +38,36 @@ import { execFileSync } from 'node:child_process';
38
38
  * latter would arrive as a single argument and the command would fail, which
39
39
  * is a loud failure rather than a silent injection.
40
40
  *
41
+ * `extraEnv` is merged over the inherited environment. It exists so a caller
42
+ * can hand a child process temporary STS credentials without writing them to
43
+ * disk or mutating this process's own environment -- the latter would leak the
44
+ * assumed role into every later call in the same run.
45
+ *
46
+ * A key mapped to `undefined` is REMOVED rather than set. Blanking is not
47
+ * equivalent: `AWS_PROFILE=''` makes the AWS CLI look for a profile literally
48
+ * named `()` and fail with "The config profile () could not be found", so
49
+ * injected credentials are ignored and every call errors. Measured, not
50
+ * theoretical.
51
+ *
41
52
  * Never throws: a non-zero exit, a missing binary, and a signal kill all come
42
53
  * back as `ok: false`. Callers branch on `ok` and surface `stderr`.
43
54
  */
44
- export function run(file, args) {
55
+ export function run(file, args, extraEnv) {
56
+ let env = process.env;
57
+ if (extraEnv) {
58
+ env = { ...process.env };
59
+ for (const [k, v] of Object.entries(extraEnv)) {
60
+ if (v === undefined)
61
+ delete env[k];
62
+ else
63
+ env[k] = v;
64
+ }
65
+ }
45
66
  try {
46
67
  const stdout = execFileSync(file, args, {
47
68
  encoding: 'utf8',
48
69
  stdio: ['pipe', 'pipe', 'pipe'],
49
- env: process.env,
70
+ env,
50
71
  }).trim();
51
72
  return { ok: true, stdout, stderr: '' };
52
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/utils/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,IAAc;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE;YACtC,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;IACrF,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAc;IACzD,IAAI,CAAC;QACH,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/utils/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,IAAc,EAAE,QAA6C;IAC7F,IAAI,GAAG,GAAsB,OAAO,CAAC,GAAG,CAAC;IACzC,IAAI,QAAQ,EAAE,CAAC;QACb,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,KAAK,SAAS;gBAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;;gBAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE;YACtC,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,GAAG;SACJ,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;IACrF,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAc;IACzD,IAAI,CAAC;QACH,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prism-d1/cli",
3
- "version": "1.0.95",
3
+ "version": "1.0.97",
4
4
  "description": "PRISM D1 Velocity CLI — AI development lifecycle assessment tool",
5
5
  "author": "andklee",
6
6
  "engines": {