Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/plugin/ses validated domains #253

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
6 changes: 6 additions & 0 deletions collectors/aws/collector.js
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,12 @@ var postcalls = [
reliesOnCall: 'listIdentities',
override: true,
rateLimit: 1000
},
getIdentityVerificationAttributes: {
reliesOnService: 'ses',
reliesOnCall: 'listIdentities',
override: true,
rateLimit: 1000
}
},
SNS: {
Expand Down
15 changes: 15 additions & 0 deletions collectors/aws/ses/getIdentityVerificationAttributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var AWS = require('aws-sdk');

module.exports = function(AWSConfig, collection, callback) {
var ses = new AWS.SES(AWSConfig);

ses.getIdentityVerificationAttributes({Identities: collection.ses.listIdentities[AWSConfig.region].data}, function(err, data){
if (err) {
collection.ses.getIdentityVerificationAttributes[AWSConfig.region].err = err;
}

collection.ses.getIdentityVerificationAttributes[AWSConfig.region].data = data;

callback();
});
};
1 change: 1 addition & 0 deletions exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ module.exports = {
'notebookDirectInternetAccess' : require(__dirname + '/plugins/aws/sagemaker/notebookDirectInternetAccess.js'),

'dkimEnabled' : require(__dirname + '/plugins/aws/ses/dkimEnabled.js'),
'sesValidatedDomains' : require(__dirname + '/plugins/aws/ses/sesValidatedDomains.js'),

'topicPolicies' : require(__dirname + '/plugins/aws/sns/topicPolicies.js'),
'sqsCrossAccount' : require(__dirname + '/plugins/aws/sqs/sqsCrossAccount.js'),
Expand Down
74 changes: 74 additions & 0 deletions plugins/aws/ses/sesValidatedDomains.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
var async = require('async');
var helpers = require('../../../helpers/aws');

module.exports = {
title: 'SES Validated Domains Flagged',
category: 'SES',
description: 'Ensures that only Email Identities are used for verification of identities.',
more_info: 'SES allows for either domains or email addresses to be valid identities. This checks to ensure that only emails have been verified.',
recommended_action: 'Remove any domains',
link: 'https://docs.aws.amazon.com/ses/latest/DeveloperGuide/remove-verified-domain.html',
apis: ['SES:listIdentities', 'SES:getIdentityVerificationAttributes'],

run: function(cache, settings, callback) {
var results = [];
var source = {};
var regions = helpers.regions(settings);

async.each(regions.ses, function(region, rcb){
var listIdentities = helpers.addSource(cache, source,
['ses', 'listIdentities', region]);

if (!listIdentities) return rcb();

if (listIdentities.err || !listIdentities.data) {
helpers.addResult(results, 3,
'Unable to query for SES identities: ' + helpers.addError(listIdentities), region);
return rcb();
}

if (!listIdentities.data.length) {
helpers.addResult(results, 0, 'No SES domain identities found', region);
return rcb();
}

var getIdentityVerificationAttributes = helpers.addSource(cache, source,
['ses', 'getIdentityVerificationAttributes', region]);

if (!getIdentityVerificationAttributes ||
getIdentityVerificationAttributes.err ||
!getIdentityVerificationAttributes.data) {
helpers.addResult(results, 3,
'Unable to get SES Verification attributes: ' + helpers.addError(getIdentityVerificationAttributes), region);
return rcb();
}

for (i in getIdentityVerificationAttributes.data.VerificationAttributes) {
var identity = getIdentityVerificationAttributes.data.VerificationAttributes[i];

if (!identity.VerificationStatus) {
helpers.addResult(results, 2, 'Domain identity exists with unknown status', region, i);
} else if (identity.VerificationStatus == 'Success') {
helpers.addResult(results, 2,
'Domain identity exists and is verified', region, i);
} else if (identity.VerificationStatus == 'Pending') {
helpers.addResult(results, 2,
'Domain identity exists and has verification that is pending', region, i);
} else if (identity.VerificationStatus == 'Failed') {
helpers.addResult(results, 2,
'Domain identity exists but verification failed', region, i);
} else if (identity.VerificationStatus == 'TemporaryFailure') {
helpers.addResult(results, 2,
'Domain identity exists but verification temporarily failed', region, i);
} else {
helpers.addResult(results, 2, 'Domain identity exists with unknown status', region, i);

}
}

rcb();
}, function(){
callback(null, results, source);
});
}
};
126 changes: 126 additions & 0 deletions plugins/aws/ses/sesValidatedDomains.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
var assert = require('assert');
var expect = require('chai').expect;
var ses = require('./sesValidatedDomains');

const createCache = (lData, gData) => {
return {
ses: {
listIdentities: {
'us-east-1': {
err: null,
data: lData
}
},
getIdentityVerificationAttributes: {
'us-east-1': {
err: null,
data: gData
}
}

}
}
};

describe('sesValidatedDomains', function () {
describe('run', function () {
it('should give passing result if no domain identities are found', function (done) {
const callback = (err, results) => {
expect(results.length).to.equal(1)
expect(results[0].status).to.equal(0)
expect(results[0].message).to.include('No SES domain identities found')
done()
};

const cache = createCache(
[],
[]
);

ses.run(cache, {}, callback);
})
});

describe('run', function () {
it('should give error result if verified domain identities exist', function (done) {
const callback = (err, results) => {
expect(results.length).to.equal(1)
expect(results[0].status).to.equal(2)
expect(results[0].message).to.include('Domain identity exists and is verified')
done()
};

const cache = createCache(
[{
"Identities": [
"test@test.com"
]
}],
{
"VerificationAttributes": [
{
"VerificationStatus": "Success"
}
]
}
);

ses.run(cache, {}, callback);
})
})

describe('run', function () {
it('should give warning result if verified domain identities are pending', function (done) {
const callback = (err, results) => {
expect(results.length).to.equal(1)
expect(results[0].status).to.equal(2)
expect(results[0].message).to.include('Domain identity exists and has verification that is pending')
done()
};

const cache = createCache(
[{
"Identities": [
"test@test.com"
]
}],
{
"VerificationAttributes": [
{
"VerificationStatus": "Pending"
}
]
}
);

ses.run(cache, {}, callback);
})
})

describe('run', function () {
it('should give warning result if domain identity exists, but verification has not yet been requested', function (done) {
const callback = (err, results) => {
expect(results.length).to.equal(1)
expect(results[0].status).to.equal(2)
expect(results[0].message).to.include('Domain identity exists with unknown status')
done()
};

const cache = createCache(
[{
"Identities": [
"test@test.com"
]
}],
{
"VerificationAttributes": [
{}
]
}
);

ses.run(cache, {}, callback);
})
})

})