Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
AkhtarAmir committed Mar 14, 2021
2 parents 0bf276d + 4bcc361 commit 5271345
Show file tree
Hide file tree
Showing 42 changed files with 3,447 additions and 95 deletions.
1 change: 1 addition & 0 deletions engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ var engine = function(cloudConfig, settings) {
if (results[r].status === 2) {
var resource = results[r].resource;
var event = {};
event.region = results[r].region;
event['remediation_file'] = {};
event['remediation_file'] = initializeFile(event['remediation_file'], 'execute', key, resource);
plugin.remediate(cloudConfig, collection, event, resource, (err, result) => {
Expand Down
5 changes: 3 additions & 2 deletions helpers/google/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,14 @@ function findOpenAllPorts(ngs, location, results) {
if (sgroups.allowed && sgroups.allowed.length) {
let firewallRules = sgroups.allowed;
let sourceAddressPrefix = sgroups.sourceRanges;

if (!sourceAddressPrefix || !sourceAddressPrefix.length) continue;

for (let firewallRule of firewallRules) {
for (let protocol in protocols) {
if (sgroups['direction'] && (sgroups['direction'] === 'INGRESS') &&
firewallRule['IPProtocol'] && (firewallRule['IPProtocol'] === protocol) &&
sgroups['disabled'] && (sgroups['disabled'] === false) &&
!sgroups['disabled'] &&
sourceAddressPrefix &&
(sourceAddressPrefix.includes('*') || sourceAddressPrefix.includes('') || sourceAddressPrefix.includes('0.0.0.0/0') || sourceAddressPrefix.includes('<nw>/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('internet'))) {
if (firewallRule['ports']) {
Expand All @@ -108,7 +109,7 @@ function findOpenAllPorts(ngs, location, results) {
}
} else if (sgroups['direction'] && (sgroups['direction'] === 'INGRESS') &&
firewallRule['IPProtocol'] && (firewallRule['IPProtocol'] === 'all') &&
sgroups['disabled'] && (sgroups['disabled'] === false) &&
!sgroups['disabled'] &&
sourceAddressPrefix &&
(sourceAddressPrefix.includes('*') || sourceAddressPrefix.includes('') || sourceAddressPrefix.includes('0.0.0.0/0') || sourceAddressPrefix.includes('<nw>/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('internet'))) {
var string = 'all ports open to the public';
Expand Down
79 changes: 77 additions & 2 deletions plugins/aws/cloudfront/cloudfrontHttpsOnly.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,34 @@ module.exports = {
more_info: 'For maximum security, CloudFront distributions can be configured to only accept HTTPS connections or to redirect HTTP connections to HTTPS.',
link: 'http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudFront.html',
recommended_action: 'Remove HTTP-only listeners from distributions.',
apis: ['CloudFront:listDistributions'],
apis: ['CloudFront:listDistributions', 'CloudFront:getDistribution'],
compliance: {
hipaa: 'HIPAA requires all data to be transmitted over secure channels. ' +
'CloudFront HTTPS redirection should be used to ensure site visitors ' +
'are always connecting over a secure channel.'
},
remediation_description: 'CloudFront distribution will be configured to only accept HTTPS connections or to redirect HTTP connections to HTTPS.',
remediation_min_version: '202101041100',
apis_remediate: ['CloudFront:listDistributions', 'CloudFront:getDistribution'],
actions: {
remediate: ['CloudFront:updateDistribution'],
rollback: ['CloudFront:updateDistribution']
},
permissions: {
remediate: ['cloudfront:UpdateDistribution'],
rollback: ['cloudfront:UpdateDistribution']
},
realtime_triggers: ['cloudfront:CreateDistribution', 'cloudfront:UpdateDistribution'],
remediation_inputs: {
cdnPolicyOption: {
name: 'Viewer Protocol Policy Option',
description: 'https-only | redirect-to-https',
regex: '^(https-only|redirect-to-https)$',
required: false
}
},

run: function(cache, settings, callback) {

var results = [];
var source = {};

Expand Down Expand Up @@ -52,5 +71,61 @@ module.exports = {
});

callback(null, results, source);
},
remediate: function(config, cache, settings, resource, callback) {
var putCall = this.actions.remediate;
var pluginName = 'cloudfrontHttpsOnly';
var distributionNameArr = resource.split(':');
var distributionName = distributionNameArr[distributionNameArr.length - 1].split('/');
var cdnId = distributionName[1];
var distributionLocation = helpers.defaultRegion(settings);

var getDistribution = helpers.addSource(cache, {},
['cloudfront', 'getDistribution', distributionLocation, cdnId]);

var params = {};

if (getDistribution &&
getDistribution.data &&
getDistribution.data.ETag &&
getDistribution.data.Distribution &&
getDistribution.data.Distribution.DistributionConfig) {
params['DistributionConfig'] = getDistribution.data.Distribution.DistributionConfig;

if (settings.input && settings.input.cdnPolicyOption) params['DistributionConfig']['ViewerProtocolPolicy'] = settings.input.cdnPolicyOption;
else params['DistributionConfig']['DefaultCacheBehavior']['ViewerProtocolPolicy'] = 'redirect-to-https';

params['Id'] = cdnId;
params['IfMatch'] = getDistribution.data.ETag;
} else {
return callback('Unable to get CloudFront distribution', null);
}

config.region = distributionLocation;

var remediation_file = settings.remediation_file;
remediation_file['pre_remediate']['actions'][pluginName][resource] = {
'HTTPSOnly': 'Disabled',
'CloudFront': resource
};
// passes the config, put call, and params to the remediate helper function
helpers.remediatePlugin(config, putCall[0], params, function(err) {
if (err) {
remediation_file['remediate']['actions'][pluginName]['error'] = err;
return callback(err, null);
}

let action = params;
action.action = putCall;

remediation_file['post_remediate']['actions'][pluginName][resource] = action;
remediation_file['remediate']['actions'][pluginName][resource] = {
'Action': 'HTTPSOnly',
'CloudTrail': cdnId
};

settings.remediation_file = remediation_file;
return callback(null, action);
});
}
};
2 changes: 1 addition & 1 deletion plugins/aws/cloudwatchlogs/logRetentionPeriod.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module.exports = {
logGroup.arn);
}
} else {
helpers.addResult(results, 2, 'Log group does not have a retention period', region, logGroup.arn);
helpers.addResult(results, 0, 'Log group retention period is set to never expire', region, logGroup.arn);
}
}

Expand Down
4 changes: 2 additions & 2 deletions plugins/aws/cloudwatchlogs/logRetentionPeriod.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ describe('CloudWatch Log Retention Period', function () {
logs.run(cache, { minimum_log_retention_period: 10 }, callback);
});

it('should FAIL if a CloudWatch Logs log group does not have retention rate', function (done) {
it('should PASS if a Log group retention period is set to never expire', function (done) {
const callback = (err, results) => {
expect(results[0].status).to.equal(2);
expect(results[0].status).to.equal(0);
done()
};

Expand Down
2 changes: 1 addition & 1 deletion plugins/aws/dynamodb/daxClusterEncryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module.exports = {

if (cluster.SSEDescription &&
cluster.SSEDescription.Status &&
cluster.SSEDescription.Status === 'ENABLED') {
cluster.SSEDescription.Status.toUpperCase() === 'ENABLED') {
helpers.addResult(results, 0,
'Encryption is enabled for DAX :' + cluster.ClusterName, region, resource);
} else {
Expand Down
108 changes: 96 additions & 12 deletions plugins/aws/dynamodb/dynamoKmsEncryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,37 @@ module.exports = {
more_info: 'DynamoDB tables can be encrypted using AWS-owned or customer-owned KMS keys. Customer keys should be used to ensure control over the encryption seed data.',
link: 'https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/EncryptionAtRest.html',
recommended_action: 'Create a new DynamoDB table using a CMK KMS key.',
apis: ['DynamoDB:listTables', 'DynamoDB:describeTable', 'STS:getCallerIdentity'],
apis: ['DynamoDB:listTables', 'DynamoDB:describeTable', 'STS:getCallerIdentity', 'KMS:listKeys', 'KMS:describeKey'],
remediation_description: 'The impacted DynamoDB table will be configured to use either KMS encryption with AWS managed CMK, or CMK-based encryption if a KMS key ID is provided.',
remediation_min_version: '202001121300',
apis_remediate: ['DynamoDB:listTables', 'KMS:listKeys', 'KMS:describeKey'],
actions: {
remediate: ['DynamoDB:updateTable'],
rollback: ['DynamoDB:updateTable']
},
permissions: {
remediate: ['DynamoDB:UpdateTable'],
rollback: ['DynamoDB:UpdateTable']
},
remediation_inputs: {
kmsKeyIdforDynamo: {
name: '(Optional) DynamoDB KMS Key ID',
description: 'The KMS Key ID used for encryption',
regex: '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$',
required: false
}
},
realtime_triggers: ['DynamoDB:UpdateTable', 'DynamoDB:CreateTable'],

run: function(cache, settings, callback) {

var results = [];
var source = {};
var regions = helpers.regions(settings);

var acctRegion = helpers.defaultRegion(settings);
var awsOrGov = helpers.defaultPartition(settings);
var accountId = helpers.addSource(cache, source, ['sts', 'getCallerIdentity', acctRegion, 'data']);

var regions = helpers.regions(settings);

async.each(regions.dynamodb, function(region, rcb){
var listTables = helpers.addSource(cache, source,
['dynamodb', 'listTables', region]);
Expand All @@ -44,26 +62,92 @@ module.exports = {
var describeTable = helpers.addSource(cache, source,
['dynamodb', 'describeTable', region, table]);

var arn = 'arn:' + awsOrGov + ':dynamodb:' + region + ':' + accountId + ':table/' + table;
var resource = `arn:${awsOrGov}:dynamodb:${region}:${accountId}:table/${table}`;

if (describeTable.err || !describeTable.data || !describeTable.data.Table) {
helpers.addResult(results, 3,
'Unable to describe DynamoDB table: ' + helpers.addError(describeTable), region, arn);
'Unable to describe DynamoDB table: ' + helpers.addError(describeTable), region, resource);
return rcb();
}

if (!describeTable.data.Table.SSEDescription) {
helpers.addResult(results, 1,
'Table is using default encryption with AWS-owned key', region, arn);
} else {

if (describeTable.data.Table.SSEDescription &&
describeTable.data.Table.SSEDescription.Status &&
describeTable.data.Table.SSEDescription.Status.toUpperCase() === 'ENABLED') {
helpers.addResult(results, 0,
'Table encryption is enabled with a KMS master key', region, arn);
'Table encryption is enabled with a KMS master key', region, resource);
} else {
helpers.addResult(results, 2,
'Table is using default encryption with AWS-owned key', region, resource);
}
}

rcb();
}, function(){
callback(null, results, source);
});
},
remediate: function(config, cache, settings, resource, callback) {
var putCall = this.actions.remediate;
var pluginName = 'dynamoKmsEncryption';
var tableNameArr = resource.split(':');
var tableName = tableNameArr[tableNameArr.length - 1].split('/')[1];
let defaultKeyDesc = 'Default master key that protects my DynamoDB data when no other key is defined';

// find the location of the table needing to be remediated
var tableLocation = tableNameArr[3];

// add the location of the table to the config
config.region = tableLocation;
var params = {};

// create the params necessary for the remediation
if (settings.input &&
settings.input.kmsKeyIdforDynamo) {
params = {
'TableName': tableName,
'SSESpecification': {
'Enabled': true,
'KMSMasterKeyId': settings.input.kmsKeyIdforDynamo,
'SSEType': 'KMS'
}
};
} else {
let defaultKmsKeyId = helpers.getDefaultKeyId(cache, config.region, defaultKeyDesc);
if (!defaultKmsKeyId) return callback(`No default DynamoDB key for the region ${config.region}`);
params = {
'TableName': tableName,
'SSESpecification': {
'Enabled': true,
'KMSMasterKeyId': defaultKmsKeyId,
'SSEType': 'KMS'
}
};
}

var remediation_file = settings.remediation_file;
remediation_file['pre_remediate']['actions'][pluginName][resource] = {
'Encryption': 'DEFAULT',
'DynamoDB': resource
};
// passes the config, put call, and params to the remediate helper function
helpers.remediatePlugin(config, putCall[0], params, function(err) {
if (err) {
remediation_file['remediate']['actions'][pluginName]['error'] = err;
return callback(err, null);
}

let action = params;
action.action = putCall;

remediation_file['post_remediate']['actions'][pluginName][resource] = action;
remediation_file['remediate']['actions'][pluginName][resource] = {
'Action': 'ENCRYPTED',
'DynamoDB': tableName
};

settings.remediation_file = remediation_file;
return callback(null, action);
});
}
};
};
Loading

0 comments on commit 5271345

Please sign in to comment.