UNPKG

@cloud-copilot/iam-lens

Version:

Visibility in IAM in and across AWS accounts

104 lines 4.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolvePrincipalScope = resolvePrincipalScope; exports.intersectWithPrincipalScope = intersectWithPrincipalScope; const iam_utils_1 = require("@cloud-copilot/iam-utils"); /** * Resolves a `WhoCanPrincipalScope` into concrete sets of account IDs and principal ARNs * using the collect client for OU lookups. * * `scope.principals` are kept separate from `scope.accounts` — a scope like * `{ principals: ['arn:...:role/Foo'] }` tests only that one role and does NOT * expand to search every principal in Foo's account. * * @param client The collect client used to resolve OU paths to account IDs. * @param scope The principal scope to resolve. * @returns An object with `accounts` and `principals` sets. */ async function resolvePrincipalScope(client, scope) { const accounts = new Set(); const principals = new Set(); for (const p of scope.principals ?? []) { principals.add(p); } for (const a of scope.accounts ?? []) { accounts.add(a); } for (const ouPath of scope.ous ?? []) { const parts = ouPath.split('/'); const orgId = parts[0]; const pathParts = parts.slice(1); const [, ouAccounts] = await client.getAccountsForOrgPath(orgId, pathParts); for (const a of ouAccounts) { accounts.add(a); } } return { accounts, principals }; } /** * Intersects the resource-policy-derived scope with a caller-supplied principal scope. * Returns the narrowed set of accounts (for full-account search) and principals * (for individual principal testing). * * @param resourcePolicyAccounts Account IDs derived from the resource policy. * @param resourcePolicyPrincipals Individual principal ARNs derived from the resource policy. * @param resourcePolicyCheckAllAccounts Whether the resource policy implies all accounts should be checked. * @param scopeAccounts Account IDs from the resolved principal scope. * @param suggestedPrincipals Principal ARNs from the resolved principal scope. * @returns The intersected accounts and principals to search. */ function intersectWithPrincipalScope(resourcePolicyAccounts, resourcePolicyPrincipals, resourcePolicyCheckAllAccounts, scopeAccounts, scopePrincipals) { // Accounts: intersection of resource policy accounts and scope accounts const rpAccountSet = new Set(resourcePolicyAccounts); const accounts = resourcePolicyCheckAllAccounts ? Array.from(scopeAccounts) : resourcePolicyAccounts.filter((a) => scopeAccounts.has(a)); const accountsResultSet = new Set(accounts); // Principals: merge from both sides, filtering by the other side's scope const principalSet = new Set(); // From resource policy principals: keep if the principal's account is in scopeAccounts, // OR the principal ARN is in scopePrincipals. for (const p of resourcePolicyPrincipals) { if ((0, iam_utils_1.isServicePrincipal)(p)) { if (scopePrincipals.has(p)) { principalSet.add(p); } } else { const accountId = (0, iam_utils_1.splitArnParts)(p).accountId; if (accountId && accountsResultSet.has(accountId)) { // Account loop already covers this principal — skip continue; } if (accountId && scopeAccounts.has(accountId)) { principalSet.add(p); } else if (scopePrincipals.has(p)) { principalSet.add(p); } } } const rpPrincipalSet = new Set(resourcePolicyPrincipals); // From scope principals: keep if the principal's account is in resource policy accounts // or resourcePolicyCheckAllAccounts is true. Exclude if account is already in accounts result. // Service principals only survive if resourcePolicyCheckAllAccounts or the resource policy named them. for (const p of scopePrincipals) { if ((0, iam_utils_1.isServicePrincipal)(p)) { if (resourcePolicyCheckAllAccounts || rpPrincipalSet.has(p)) { principalSet.add(p); } } else { const accountId = (0, iam_utils_1.splitArnParts)(p).accountId; if (accountId && accountsResultSet.has(accountId)) { // Account loop already covers this principal — skip continue; } if (resourcePolicyCheckAllAccounts || (accountId && rpAccountSet.has(accountId))) { principalSet.add(p); } } } return { accounts, principals: Array.from(principalSet) }; } //# sourceMappingURL=principalScope.js.map