diff --git a/lib/cfn-nag/custom_rules/DynamoDBBackupRule.rb b/lib/cfn-nag/custom_rules/DynamoDBBackupRule.rb new file mode 100644 index 00000000..711e213e --- /dev/null +++ b/lib/cfn-nag/custom_rules/DynamoDBBackupRule.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'cfn-nag/violation' +require 'cfn-nag/util/truthy' +require_relative 'base' + +class DynamoDBBackupRule < BaseRule + def rule_text + 'DynamoDB table should have backup enabled, should be set using PointInTimeRecoveryEnabled' + end + + def rule_type + Violation::WARNING + end + + def rule_id + 'W78' + end + + def audit_impl(cfn_model) + violating_ddb_tables = cfn_model.resources_by_type('AWS::DynamoDB::Table').select do |table| + table.pointInTimeRecoverySpecification.nil? || + !truthy?(table.pointInTimeRecoverySpecification['PointInTimeRecoveryEnabled'].to_s) + end + + violating_ddb_tables.map(&:logical_resource_id) + end +end diff --git a/spec/custom_rules/DynamoDBBackupRule_spec.rb b/spec/custom_rules/DynamoDBBackupRule_spec.rb new file mode 100644 index 00000000..5f51506d --- /dev/null +++ b/spec/custom_rules/DynamoDBBackupRule_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' +require 'cfn-model' +require 'cfn-nag/custom_rules/DynamoDBBackupRule' + +describe DynamoDBBackupRule do + context 'dynamodb table without backup' do + it 'returns offending logical resource id' do + cfn_model = CfnParser.new.parse read_test_template( + 'yaml/dynamodb/dynamodb_table_with_no_backup_enabled.yaml' + ) + + actual_logical_resource_ids = DynamoDBBackupRule.new.audit_impl cfn_model + expected_logical_resource_ids = %w[DynamodbNoBackupEnabled DynamodbNoBackupEnabled2] + + expect(actual_logical_resource_ids).to eq expected_logical_resource_ids + end + end + + context 'dynamodb table with backup enabled' do + it 'returns empty list' do + cfn_model = CfnParser.new.parse read_test_template( + 'yaml/dynamodb/dynamodb_table_with_backup_enabled.yaml' + ) + + actual_logical_resource_ids = DynamoDBBackupRule.new.audit_impl cfn_model + expected_logical_resource_ids = [] + + expect(actual_logical_resource_ids).to eq expected_logical_resource_ids + end + end +end diff --git a/spec/test_templates/yaml/dynamodb/dynamodb_table_with_backup_enabled.yaml b/spec/test_templates/yaml/dynamodb/dynamodb_table_with_backup_enabled.yaml new file mode 100644 index 00000000..ba0bb614 --- /dev/null +++ b/spec/test_templates/yaml/dynamodb/dynamodb_table_with_backup_enabled.yaml @@ -0,0 +1,14 @@ +--- +Resources: + DynamodbBackupEnabled: + Type: AWS::DynamoDB::Table + Properties: + KeySchema: + - KeySchema + TableName: String + PointInTimeRecoverySpecification: + PointInTimeRecoveryEnabled: True + SSESpecification: + KMSMasterKeyId: String + SSEEnabled: True + SSEType: String diff --git a/spec/test_templates/yaml/dynamodb/dynamodb_table_with_no_backup_enabled.yaml b/spec/test_templates/yaml/dynamodb/dynamodb_table_with_no_backup_enabled.yaml new file mode 100644 index 00000000..102baa93 --- /dev/null +++ b/spec/test_templates/yaml/dynamodb/dynamodb_table_with_no_backup_enabled.yaml @@ -0,0 +1,25 @@ +--- +Resources: + DynamodbNoBackupEnabled: + Type: AWS::DynamoDB::Table + Properties: + KeySchema: + - KeySchema + TableName: String + PointInTimeRecoverySpecification: + PointInTimeRecoveryEnabled: False + SSESpecification: + KMSMasterKeyId: String + SSEEnabled: True + SSEType: String + + DynamodbNoBackupEnabled2: + Type: AWS::DynamoDB::Table + Properties: + KeySchema: + - KeySchema + TableName: String + SSESpecification: + KMSMasterKeyId: String + SSEEnabled: True + SSEType: String