@cloud-copilot/iam-lens
Version:
Visibility in IAM in and across AWS accounts
104 lines • 4.74 kB
JavaScript
;
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