@cloud-copilot/iam-lens
Version:
Visibility in IAM in and across AWS accounts
119 lines • 4.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.statementAppliesToPrincipal = statementAppliesToPrincipal;
exports.makePrincipalOnlyPolicyFromStatement = makePrincipalOnlyPolicyFromStatement;
const iam_policy_1 = require("@cloud-copilot/iam-policy");
const iam_simulate_1 = require("@cloud-copilot/iam-simulate");
const iam_utils_1 = require("@cloud-copilot/iam-utils");
const contextKeys_js_1 = require("../../simulate/contextKeys.js");
/**
* Checks to see if a statement applies to a principal by running a simulation.
*
* If the principal is a match return 'PrincipalMatch'
* If the account is a match return 'AccountMatch'
* Otherwise return 'NoMatch'
*
* @param statement the statement to check
* @param principalArn the arn of the principal to check
* @param client the IAM collect client to use for retrieving principal information
* @returns Whether the statement applies to the principal
*/
async function statementAppliesToPrincipal(statement, principalArn, client) {
const principalAccount = (0, iam_utils_1.splitArnParts)(principalArn).accountId;
const resourcePolicy = makePrincipalOnlyPolicyFromStatement(statement);
const simulationRequest = {
principal: principalArn,
action: 'kms:DescribeKey',
resourceAccount: principalAccount,
resourceArn: undefined,
customContextKeys: {},
simulationMode: 'Strict'
};
// We use KMS, so we get kms:CallerAccount context key support
const { contextKeys } = await (0, contextKeys_js_1.createContextKeys)(client, simulationRequest, 'kms', {});
const request = {
action: 'kms:DescribeKey',
resource: {
resource: 'arn:aws:kms:us-east-1:123456789012:key/abcd1234-ab12-cd34-ef23-456789abcdef',
accountId: principalAccount
},
principal: principalArn,
contextVariables: contextKeys
};
const simulation = {
request,
identityPolicies: [],
resourcePolicy: resourcePolicy.toJSON(),
serviceControlPolicies: [],
resourceControlPolicies: []
};
const result = await (0, iam_simulate_1.runSimulation)(simulation, {
simulationMode: simulationRequest.simulationMode
});
if (result.resultType === 'error') {
return 'NoMatch';
}
const analysis = result.resultType === 'single' ? result.result.analysis : undefined;
if (analysis?.result === 'Allowed') {
return 'PrincipalMatch';
}
if (analysis?.resourceAnalysis?.result === 'AllowedForAccount') {
return 'AccountMatch';
}
return 'NoMatch';
}
const principalKeys = new Set([
'aws:PrincipalArn',
'aws:PrincipalAccount',
'aws:PrincipalOrgId',
'aws:PrincipalOrgPaths',
'aws:PrincipalType',
'aws:userid',
'aws:username',
'aws:PrincipalIsAWSService',
'kms:CallerAccount'
].map((k) => k.toLowerCase()));
/**
* Makes a policy that captures the principal and principal conditions from a statement
* and allows all actions on all resources.
*
* The conditions returned are only those that relate to the principal.
*
* @param statement the statement to extract the principal from
* @returns
*/
function makePrincipalOnlyPolicyFromStatement(statement) {
const rawStatement = structuredClone(statement.toJSON());
const rawStatementValues = {};
if (statement.isPrincipalStatement()) {
rawStatementValues.Principal = rawStatement.Principal;
}
else if (statement.isNotPrincipalStatement()) {
rawStatementValues.NotPrincipal = rawStatement.NotPrincipal;
}
if (rawStatement.Condition) {
for (const operator of Object.keys(rawStatement.Condition)) {
for (const key of Object.keys(rawStatement.Condition[operator])) {
if (!principalKeys.has(key.toLowerCase())) {
delete rawStatement.Condition[operator][key];
}
}
if (Object.keys(rawStatement.Condition[operator]).length === 0) {
delete rawStatement.Condition[operator];
}
}
if (Object.keys(rawStatement.Condition).length > 0) {
rawStatementValues.Condition = rawStatement.Condition;
}
}
return (0, iam_policy_1.loadPolicy)({
Version: '2012-10-17',
Statement: {
Effect: 'Allow',
Resource: '*',
Action: '*',
...rawStatementValues
}
});
}
//# sourceMappingURL=statements.js.map