Skip to content

Commit

Permalink
Merge pull request #2126 from AkhtarAmir/update-iamRolePolicies
Browse files Browse the repository at this point in the history
iam role policies updated
  • Loading branch information
alphadev4 authored Feb 12, 2025
2 parents 09e7fa3 + 0cdb1b2 commit 6802dd4
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 15 deletions.
212 changes: 199 additions & 13 deletions plugins/aws/iam/iamRolePolicies.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ module.exports = {
description: 'Enable this setting to ignore resource wildcards i.e. \'"Resource": "*"\' in the IAM policy, which by default, are being flagged.',
regex: '^(true|false)$',
default: 'false'
},
iam_policy_message_format: {
name: 'IAM Policy Message Format',
description: 'Enable this setting to include policy names in the failure messages',
regex: '^(true|false)$',
default: 'false'
}
},
realtime_triggers: ['iam:CreateRole','iam:DeleteRole','iam:AttachRolePolicy','iam:DetachRolePolicy','iam:PutRolePolicy','iam:DeleteRolePolicy'],
Expand All @@ -94,15 +100,16 @@ module.exports = {
ignore_customer_managed_iam_policies: settings.ignore_customer_managed_iam_policies || this.settings.ignore_customer_managed_iam_policies.default,
iam_role_policies_ignore_tag: settings.iam_role_policies_ignore_tag || this.settings.iam_role_policies_ignore_tag.default,
iam_policy_resource_specific_wildcards: settings.iam_policy_resource_specific_wildcards || this.settings.iam_policy_resource_specific_wildcards.default,
ignore_iam_policy_resource_wildcards: settings.ignore_iam_policy_resource_wildcards || this.settings.ignore_iam_policy_resource_wildcards.default
ignore_iam_policy_resource_wildcards: settings.ignore_iam_policy_resource_wildcards || this.settings.ignore_iam_policy_resource_wildcards.default,
iam_policy_message_format: settings.iam_policy_message_format || this.settings.iam_policy_message_format.default
};

config.ignore_service_specific_wildcards = (config.ignore_service_specific_wildcards === 'true');
config.ignore_identity_federation_roles = (config.ignore_identity_federation_roles === 'true');
config.ignore_aws_managed_iam_policies = (config.ignore_aws_managed_iam_policies === 'true');
config.ignore_customer_managed_iam_policies = (config.ignore_customer_managed_iam_policies === 'true');
config.ignore_iam_policy_resource_wildcards = (config.ignore_iam_policy_resource_wildcards === 'true');

config.iam_policy_message_format = (config.iam_policy_message_format === 'true');


var allowedRegex = RegExp(config.iam_policy_resource_specific_wildcards);
Expand Down Expand Up @@ -196,15 +203,20 @@ module.exports = {
return cb();
}

var roleFailures = [];
var roleFailures = config.iam_policy_message_format ? {} : [];


// See if role has admin managed policy
if (listAttachedRolePolicies.data &&
listAttachedRolePolicies.data.AttachedPolicies) {

for (var policy of listAttachedRolePolicies.data.AttachedPolicies) {
if (policy.PolicyArn === managedAdminPolicy) {
roleFailures.push('Role has managed AdministratorAccess policy');
if (config.iam_policy_message_format) {
roleFailures.admin = 'managedAdminPolicy';
} else {
roleFailures.push('Role has managed AdministratorAccess policy');
}
break;
}

Expand All @@ -230,7 +242,11 @@ module.exports = {
getPolicyVersion.data.PolicyVersion.Document);
if (!statements) break;

addRoleFailures(roleFailures, statements, 'managed', config.ignore_service_specific_wildcards, allowedRegex, config.ignore_iam_policy_resource_wildcards);
if (config.iam_policy_message_format) {
addRoleFailuresPolicyName(roleFailures, statements, 'managed', policy.PolicyName, config.ignore_service_specific_wildcards, allowedRegex, config.ignore_iam_policy_resource_wildcards);
} else {
addRoleFailures(roleFailures, statements, 'managed', config.ignore_service_specific_wildcards, allowedRegex, config.ignore_iam_policy_resource_wildcards);
}
}
}
}
Expand All @@ -249,21 +265,22 @@ module.exports = {

var statements = getRolePolicy[policyName].data.PolicyDocument;
if (!statements) break;
addRoleFailures(roleFailures, statements, 'inline', config.ignore_service_specific_wildcards, allowedRegex, config.ignore_iam_policy_resource_wildcards);
if (config.iam_policy_message_format) {
addRoleFailuresPolicyName(roleFailures, statements, 'inline', policyName, config.ignore_service_specific_wildcards, allowedRegex, config.ignore_iam_policy_resource_wildcards);
} else {
addRoleFailures(roleFailures, statements, 'inline', config.ignore_service_specific_wildcards, allowedRegex, config.ignore_iam_policy_resource_wildcards);
}
}
}
}

if (roleFailures.length) {
helpers.addResult(results, 2,
roleFailures.join(', '),
'global', role.Arn, custom);
if (config.iam_policy_message_format) {
compileFormattedResults(roleFailures, role, results, custom);
} else {
helpers.addResult(results, 0,
'Role does not have overly-permissive policy',
'global', role.Arn, custom);
compileSimpleResults(roleFailures, role, results, custom);
}


cb();
}, function(){
callback(null, results, source);
Expand Down Expand Up @@ -308,4 +325,173 @@ function addRoleFailures(roleFailures, statements, policyType, ignoreServiceSpec
if (failMsg && roleFailures.indexOf(failMsg) === -1) roleFailures.push(failMsg);
}
}
}

function addRoleFailuresPolicyName(roleFailures, statements, policyType, policyName, ignoreServiceSpecific, regResource, ignoreResourceSpecific) {
// Initialize roleFailures as an object for the first time
if (!roleFailures.managed) {
roleFailures.managed = {
allActionsAllResources: [],
allActionsSelectedResources: [],
actionsAllResources: [],
wildcardActions: {},
regexMismatch: {}
};
}
if (!roleFailures.inline) {
roleFailures.inline = {
allActionsAllResources: [],
allActionsSelectedResources: [],
actionsAllResources: [],
wildcardActions: {},
regexMismatch: {}
};
}
if (!roleFailures.admin) roleFailures.admin = false;

for (var statement of statements) {
if (statement.Effect === 'Allow' && !statement.Condition) {
let targetObj = roleFailures[policyType];

if (statement.Action &&
statement.Action.indexOf('*') > -1 &&
statement.Resource &&
statement.Resource.indexOf('*') > -1) {
targetObj.allActionsAllResources.push(policyName);
} else if (statement.Action.indexOf('*') > -1) {
targetObj.allActionsSelectedResources.push(policyName);
} else if (!ignoreResourceSpecific && statement.Resource && statement.Resource == '*') {
targetObj.actionsAllResources.push(policyName);
} else if (!ignoreServiceSpecific && statement.Action && statement.Action.length) {
// Check each action for wildcards
let wildcards = [];
for (var a in statement.Action) {
if (/^.+:[a-zA-Z]?\*.?$/.test(statement.Action[a])) {
wildcards.push(statement.Action[a]);
}
}
if (wildcards.length) {
if (!targetObj.wildcardActions[wildcards.join(', ')]) {
targetObj.wildcardActions[wildcards.join(', ')] = [];
}
if (!targetObj.wildcardActions[wildcards.join(', ')].includes(policyName)) {
targetObj.wildcardActions[wildcards.join(', ')].push(policyName);
}
}
} else if (statement.Resource && statement.Resource.length) {
// Check each resource for wildcard
let wildcards = [];
for (var resource of statement.Resource) {
if (!regResource.test(resource)) {
wildcards.push(resource);
}
}
if (wildcards.length) {
if (!targetObj.regexMismatch[wildcards.join(', ')]) {
targetObj.regexMismatch[wildcards.join(', ')] = [];
}
if (!targetObj.regexMismatch[wildcards.join(', ')].includes(policyName)) {
targetObj.regexMismatch[wildcards.join(', ')].push(policyName);
}
}
}
}
}
}

function hasFailures(roleFailures) {
if (roleFailures.admin) return true;

if (roleFailures.managed) {
if (roleFailures.managed.allActionsAllResources.length) return true;
if (roleFailures.managed.allActionsSelectedResources.length) return true;
if (roleFailures.managed.actionsAllResources.length) return true;
if (Object.keys(roleFailures.managed.wildcardActions).length) return true;
if (roleFailures.managed.regexMismatch.length) return true;
}

if (roleFailures.inline) {
if (roleFailures.inline.allActionsAllResources.length) return true;
if (roleFailures.inline.allActionsSelectedResources.length) return true;
if (roleFailures.inline.actionsAllResources.length) return true;
if (Object.keys(roleFailures.inline.wildcardActions).length) return true;
if (roleFailures.inline.regexMismatch.length) return true;
}

return false;
}

function formatPolicyNames(policyArray) {
if (policyArray.length <= 5) {
return [...new Set(policyArray)].join('", "');
}
return [...new Set(policyArray)].slice(0, 5).join('", "') + '" and so on...';
}

function compileSimpleResults(roleFailures, role, results, custom) {
if (roleFailures.length) {
helpers.addResult(results, 2,
roleFailures.join(', '),
'global', role.Arn, custom);
} else {
helpers.addResult(results, 0,
'Role does not have overly-permissive policy',
'global', role.Arn, custom);
}
}

function compileFormattedResults(roleFailures, role, results, custom) {
if (hasFailures(roleFailures)) {
let messages = [];

if (roleFailures.admin == 'managedAdminPolicy') {
messages.push('Role has managed AdministratorAccess policy');
}

// Format managed policies
if (roleFailures.managed) {
if (roleFailures.managed.allActionsAllResources.length) {
messages.push(`Role managed policy "${formatPolicyNames(roleFailures.managed.allActionsAllResources)}" allows all actions on all resources`);
}
if (roleFailures.managed.allActionsSelectedResources.length) {
messages.push(`Role managed policy "${formatPolicyNames(roleFailures.managed.allActionsSelectedResources)}" allows all actions on selected resources`);
}
if (roleFailures.managed.actionsAllResources.length) {
messages.push(`Role managed policy "${formatPolicyNames(roleFailures.managed.actionsAllResources)}" allows actions on all resources`);
}
for (let action in roleFailures.managed.wildcardActions) {
messages.push(`Role managed policy "${roleFailures.managed.wildcardActions[action].join('", "')}" allows wildcard actions: ${action}`);
}
for (let resource in roleFailures.managed.regexMismatch) {
messages.push(`Role managed policy "${roleFailures.managed.regexMismatch[resource].join('", "')}" does not match provided regex: ${resource}`);
}
}

// Format inline policies
if (roleFailures.inline) {
if (roleFailures.inline.allActionsAllResources.length) {
messages.push(`Role inline policy "${formatPolicyNames(roleFailures.inline.allActionsAllResources)}" allows all actions on all resources`);
}
if (roleFailures.inline.allActionsSelectedResources.length) {
messages.push(`Role inline policy "${formatPolicyNames(roleFailures.inline.allActionsSelectedResources)}" allows all actions on selected resources`);
}
if (roleFailures.inline.actionsAllResources.length) {
messages.push(`Role inline policy "${formatPolicyNames(roleFailures.inline.actionsAllResources)}" allows actions on all resources`);
}
for (let action in roleFailures.inline.wildcardActions) {
messages.push(`Role inline policy "${roleFailures.inline.wildcardActions[action].join('", "')}" allows wildcard actions: ${action}`);
}
for (let resource in roleFailures.inline.regexMismatch) {
messages.push(`Role inline policy "${roleFailures.inline.regexMismatch[resource].join('", "')}" does not match provided regex: ${resource}`);
}
}

helpers.addResult(results, 2,
messages.join('\n'),
'global', role.Arn, custom);
} else {
helpers.addResult(results, 0,
'Role does not have overly-permissive policy',
'global', role.Arn, custom);
}
}
4 changes: 2 additions & 2 deletions plugins/aws/iam/iamRolePolicies.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ describe('iamRolePolicies', function () {
const cache = createCache([listRoles[0]],getRole[0], {}, listRolePolicies[1], getRolePolicy[4]);
iamRolePolicies.run(cache, {}, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].message).to.include('policy allows all actions on selected resources');
expect(results[0].message).to.include('allows all actions on selected resources');
expect(results[0].status).to.equal(2);
done();
});
Expand All @@ -403,7 +403,7 @@ describe('iamRolePolicies', function () {
const cache = createCache([listRoles[1]],getRole[0], {}, listRolePolicies[1], getRolePolicy[3]);
iamRolePolicies.run(cache, {}, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].message).to.include('policy allows all actions on all resources');
expect(results[0].message).to.include('allows all actions on all resources');
expect(results[0].status).to.equal(2);
done();
});
Expand Down

0 comments on commit 6802dd4

Please sign in to comment.