From 9ed8095e75f99a556a6f489f6f856cb4ef93da6f Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 20 Apr 2021 22:08:56 +0500 Subject: [PATCH] feature-iam-policies-present: Added AWS IAM Policies Present plugin and test cases (#648) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Added AWS IAM Policies Present plugin and test cases * Update plugins/aws/iam/iamPoliciesPresent.js * Update plugins/aws/iam/iamPoliciesPresent.js Co-authored-by: Gio Rodriguez --- exports.js | 1 + plugins/aws/iam/iamPoliciesPresent.js | 105 ++++++++++++++ plugins/aws/iam/iamPoliciesPresent.spec.js | 159 +++++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 plugins/aws/iam/iamPoliciesPresent.js create mode 100644 plugins/aws/iam/iamPoliciesPresent.spec.js diff --git a/exports.js b/exports.js index 322aa84cad..1eaca67d0e 100644 --- a/exports.js +++ b/exports.js @@ -241,6 +241,7 @@ module.exports = { 'kmsKeyPolicy' : require(__dirname + '/plugins/aws/kms/kmsKeyPolicy.js'), 'kmsDefaultKeyUsage' : require(__dirname + '/plugins/aws/kms/kmsDefaultKeyUsage.js'), 'kmsAppTierCmk' : require(__dirname + '/plugins/aws/kms/kmsAppTierCmk.js'), + 'iamPoliciesPresent' : require(__dirname + '/plugins/aws/iam/iamPoliciesPresent.js'), 'iamDbAuthenticationEnabled' : require(__dirname + '/plugins/aws/rds/iamDbAuthenticationEnabled.js'), 'rdsAutomatedBackups' : require(__dirname + '/plugins/aws/rds/rdsAutomatedBackups.js'), diff --git a/plugins/aws/iam/iamPoliciesPresent.js b/plugins/aws/iam/iamPoliciesPresent.js new file mode 100644 index 0000000000..a1c4335d2c --- /dev/null +++ b/plugins/aws/iam/iamPoliciesPresent.js @@ -0,0 +1,105 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'IAM Policies Present', + category: 'IAM', + description: 'Ensure that required policies are present in all IAM roles.', + more_info: 'Validate the presence of required policies in IAM roles in order to follow your organizations\'s security and compliance requirements.', + link: 'https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html', + recommended_action: 'Modify IAM roles to attach required policies', + apis: ['IAM:listRoles', 'IAM:listRolePolicies', 'IAM:listAttachedRolePolicies'], + settings: { + iam_required_policy_names: { + name: 'IAM Required Policy Names', + description: 'A comma separated list of IAM policy names that all IAM roles should have', + regex: '^.*$', + default: '' + } + }, + + run: function(cache, settings, callback) { + var config = { + iam_required_policy_names: settings.iam_required_policy_names || this.settings.iam_required_policy_names.default + }; + + if (!config.iam_required_policy_names.length) return callback(null, results, source); + + config.iam_required_policy_names = config.iam_required_policy_names.split(','); + + var results = []; + var source = {}; + + var region = helpers.defaultRegion(settings); + + var listRoles = helpers.addSource(cache, source, + ['iam', 'listRoles', region]); + + if (!listRoles) return callback(null, results, source); + + if (listRoles.err || !listRoles.data) { + helpers.addResult(results, 3, + 'Unable to query for IAM roles: ' + helpers.addError(listRoles)); + return callback(null, results, source); + } + + if (!listRoles.data.length) { + helpers.addResult(results, 0, 'No IAM roles found'); + return callback(null, results, source); + } + + async.each(listRoles.data, function(role, cb){ + if (!role.RoleName) return cb(); + + // Get managed policies attached to role + var listAttachedRolePolicies = helpers.addSource(cache, source, + ['iam', 'listAttachedRolePolicies', region, role.RoleName]); + + // Get inline policies attached to role + var listRolePolicies = helpers.addSource(cache, source, + ['iam', 'listRolePolicies', region, role.RoleName]); + + if (!listAttachedRolePolicies || listAttachedRolePolicies.err) { + helpers.addResult(results, 3, + 'Unable to query for IAM attached policy for role: ' + role.RoleName + ': ' + helpers.addError(listAttachedRolePolicies), region, role.Arn); + return cb(); + } + + if (!listRolePolicies || listRolePolicies.err) { + helpers.addResult(results, 3, + 'Unable to query for IAM role policy for role: ' + role.RoleName + ': ' + helpers.addError(listRolePolicies), region, role.Arn); + return cb(); + } + + var attachedPolicies = []; + var difference = []; + + // See if role has admin managed policy + if (listAttachedRolePolicies.data && + listAttachedRolePolicies.data.AttachedPolicies) { + + for (let policy of listAttachedRolePolicies.data.AttachedPolicies) { + attachedPolicies.push(policy.PolicyName); + } + } + + if (listRolePolicies.data && listRolePolicies.data.PolicyNames) attachedPolicies = attachedPolicies.concat(listRolePolicies.data.PolicyNames); + + for (let policy of config.iam_required_policy_names) { + if (!attachedPolicies.includes(policy)) difference.push(policy); + } + + if (difference.length) { + helpers.addResult(results, 2, + `IAM role does not have these required policies attached: ${difference.join(', ')}`, region, role.Arn); + } else { + helpers.addResult(results, 0, + 'IAM role has all required policies attached', region, role.Arn); + } + + cb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/aws/iam/iamPoliciesPresent.spec.js b/plugins/aws/iam/iamPoliciesPresent.spec.js new file mode 100644 index 0000000000..afbe9b4628 --- /dev/null +++ b/plugins/aws/iam/iamPoliciesPresent.spec.js @@ -0,0 +1,159 @@ +const expect = require('chai').expect; +var iamPoliciesPresent = require('./iamPoliciesPresent'); + + +const listRoles = [ + { + "Path": "/", + "RoleName": "test-role-1", + "RoleId": "AROAYE32SRU5VIMXXL3BH", + "Arn": "arn:aws:iam::000011112222:role/test-role-1", + "CreateDate": "2020-11-21T23:56:33Z", + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::000011112222:root" + }, + "Action": "sts:AssumeRoleWithSAML", + "Condition": {} + } + ] + }, + "MaxSessionDuration": 3600 + } +]; + +const listRolePolicies = [ + { + "PolicyNames": [ + "S3-Full" + ] + } +]; + +const listAttachedRolePolicies = [ + { + "ResponseMetadata": { + "RequestId": 'f7d427cc-970b-47af-9b7d-3e06121f83da' + }, + "AttachedPolicies": [ + { + "PolicyName": 'AdministratorAccess', + "PolicyArn": 'arn:aws:iam::aws:policy/AdministratorAccess' + } + ], + "IsTruncated": false + } +]; + +const createCache = (listRoles, listAttachedRolePolicies, listRolePolicies, listRolesErr, listAttachedRolePoliciesErr, listRolePoliciesErr) => { + var roleName = (listRoles && listRoles.length) ? listRoles[0].RoleName : null; + return { + iam: { + listRoles: { + 'us-east-1': { + err: listRolesErr, + data: listRoles + } + }, + listAttachedRolePolicies: { + 'us-east-1': { + [roleName]: { + err: listAttachedRolePoliciesErr, + data: listAttachedRolePolicies + } + } + }, + listRolePolicies: { + 'us-east-1': { + [roleName]: { + err: listRolePoliciesErr, + data: listRolePolicies + } + } + } + } + }; +}; + +const createNullCache = () => { + return { + lambda: { + listRoles: { + 'us-east-1': null + } + } + }; +}; + +describe('iamPoliciesPresent', function () { + describe('run', function () { + + it('should PASS if IAM role has all required policies attached', function (done) { + const cache = createCache([listRoles[0]], listAttachedRolePolicies[0], listRolePolicies[0]); + iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full,AdministratorAccess' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should FAIL if IAM role does not have required policies attached', function (done) { + const cache = createCache([listRoles[0]], {}, listRolePolicies[0]); + iamPoliciesPresent.run(cache, { iam_required_policy_names: 'AdministratorAccess' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should PASS if on IAM roles found', function (done) { + const cache = createCache([]); + iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should UNKNOWN if unable to list IAM roles', function (done) { + const cache = createCache(null, null, null, { message: 'Unable to list IAM roles'}); + iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should UNKNOWN if unable to list attached role policies', function (done) { + const cache = createCache([listRoles[0]], {}, null, { message: 'Unable to list attached role policies'}); + iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should UNKNOWN if unable to list role policies', function (done) { + const cache = createCache([listRoles[0]], listAttachedRolePolicies[0], {}, null, null, { message: 'Unable to query role policies'}); + iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should not return anything if list roles response not found', function (done) { + const cache = createNullCache(); + iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => { + expect(results.length).to.equal(0); + done(); + }); + }); + }); +}); \ No newline at end of file