cfn-nag 0.5.60 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/bin/spcm_scan +69 -0
  3. data/lib/cfn-nag/cfn_nag.rb +1 -0
  4. data/lib/cfn-nag/cfn_nag_config.rb +4 -1
  5. data/lib/cfn-nag/cfn_nag_executor.rb +22 -1
  6. data/lib/cfn-nag/cli_options.rb +18 -0
  7. data/lib/cfn-nag/custom_rule_loader.rb +23 -72
  8. data/lib/cfn-nag/custom_rules/AmazonMQBrokerUsersPasswordRule.rb +2 -2
  9. data/lib/cfn-nag/custom_rules/AmplifyAppAccessTokenRule.rb +2 -1
  10. data/lib/cfn-nag/custom_rules/AmplifyAppBasicAuthConfigPasswordRule.rb +2 -1
  11. data/lib/cfn-nag/custom_rules/AmplifyAppOauthTokenRule.rb +2 -1
  12. data/lib/cfn-nag/custom_rules/AmplifyBranchBasicAuthConfigPasswordRule.rb +2 -1
  13. data/lib/cfn-nag/custom_rules/AppStreamDirectoryConfigServiceAccountCredentialsAccountPasswordRule.rb +3 -2
  14. data/lib/cfn-nag/custom_rules/CodePipelineWebhookAuthenticationConfigurationSecretTokenRule.rb +2 -1
  15. data/lib/cfn-nag/custom_rules/DMSEndpointMongoDbSettingsPasswordRule.rb +2 -1
  16. data/lib/cfn-nag/custom_rules/DMSEndpointPasswordRule.rb +2 -1
  17. data/lib/cfn-nag/custom_rules/DirectoryServiceMicrosoftADPasswordRule.rb +2 -1
  18. data/lib/cfn-nag/custom_rules/DirectoryServiceSimpleADPasswordRule.rb +2 -1
  19. data/lib/cfn-nag/custom_rules/DocDBDBClusterMasterUserPasswordRule.rb +2 -1
  20. data/lib/cfn-nag/custom_rules/EMRClusterKerberosAttributesADDomainJoinPasswordRule.rb +2 -1
  21. data/lib/cfn-nag/custom_rules/EMRClusterKerberosAttributesCrossRealmTrustPrincipalPasswordRule.rb +3 -2
  22. data/lib/cfn-nag/custom_rules/EMRClusterKerberosAttributesKdcAdminPasswordRule.rb +2 -1
  23. data/lib/cfn-nag/custom_rules/ElastiCacheReplicationGroupAuthTokenRule.rb +2 -1
  24. data/lib/cfn-nag/custom_rules/IAMUserLoginProfilePasswordRule.rb +2 -1
  25. data/lib/cfn-nag/custom_rules/KinesisFirehoseDeliveryStreamRedshiftDestinationConfigurationPasswordRule.rb +3 -2
  26. data/lib/cfn-nag/custom_rules/KinesisFirehoseDeliveryStreamSplunkDestinationConfigurationHECTokenRule.rb +3 -2
  27. data/lib/cfn-nag/custom_rules/LambdaPermissionEventSourceTokenRule.rb +2 -1
  28. data/lib/cfn-nag/custom_rules/OpsWorksAppAppSourcePasswordRule.rb +2 -1
  29. data/lib/cfn-nag/custom_rules/OpsWorksAppSslConfigurationPrivateKeyRule.rb +2 -1
  30. data/lib/cfn-nag/custom_rules/OpsWorksStackCustomCookbooksSourcePasswordRule.rb +2 -1
  31. data/lib/cfn-nag/custom_rules/OpsWorksStackRdsDbInstancesDbPasswordRule.rb +3 -2
  32. data/lib/cfn-nag/custom_rules/PinpointAPNSChannelPrivateKeyRule.rb +2 -1
  33. data/lib/cfn-nag/custom_rules/PinpointAPNSChannelTokenKeyRule.rb +2 -1
  34. data/lib/cfn-nag/custom_rules/PinpointAPNSSandboxChannelPrivateKeyRule.rb +2 -1
  35. data/lib/cfn-nag/custom_rules/PinpointAPNSSandboxChannelTokenKeyRule.rb +2 -1
  36. data/lib/cfn-nag/custom_rules/PinpointAPNSVoipChannelPrivateKeyRule.rb +2 -1
  37. data/lib/cfn-nag/custom_rules/PinpointAPNSVoipChannelTokenKeyRule.rb +2 -1
  38. data/lib/cfn-nag/custom_rules/PinpointAPNSVoipSandboxChannelPrivateKeyRule.rb +2 -1
  39. data/lib/cfn-nag/custom_rules/PinpointAPNSVoipSandboxChannelTokenKeyRule.rb +2 -1
  40. data/lib/cfn-nag/custom_rules/RDSDBClusterMasterUserPasswordRule.rb +2 -1
  41. data/lib/cfn-nag/custom_rules/RDSDBInstanceMasterUserPasswordRule.rb +2 -1
  42. data/lib/cfn-nag/custom_rules/RDSDBInstanceMasterUsernameRule.rb +2 -1
  43. data/lib/cfn-nag/custom_rules/RedshiftClusterMasterUserPasswordRule.rb +2 -1
  44. data/lib/cfn-nag/custom_rules/SPCMRule.rb +66 -0
  45. data/lib/cfn-nag/custom_rules/SecretsManagerSecretKmsKeyIdRule.rb +4 -3
  46. data/lib/cfn-nag/iam_complexity_metric/condition_metric.rb +85 -0
  47. data/lib/cfn-nag/iam_complexity_metric/html_results_renderer.rb +45 -0
  48. data/lib/cfn-nag/iam_complexity_metric/policy_document_metric.rb +11 -0
  49. data/lib/cfn-nag/iam_complexity_metric/spcm.rb +79 -0
  50. data/lib/cfn-nag/iam_complexity_metric/statement_metric.rb +104 -0
  51. data/lib/cfn-nag/iam_complexity_metric/weights.rb +22 -0
  52. data/lib/cfn-nag/metadata.rb +78 -0
  53. metadata +15 -5
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class DocDBDBClusterMasterUserPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'DocDB DB Cluster master user password must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class EMRClusterKerberosAttributesADDomainJoinPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'EMR Cluster KerberosAttributes AD Domain JoinPassword must not be a ' \
9
- 'plaintext string or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'plaintext string or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,8 +6,9 @@ require_relative 'password_base_rule'
6
6
  class EMRClusterKerberosAttributesCrossRealmTrustPrincipalPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'EMR Cluster KerberosAttributes CrossRealmTrustPrincipal Password must ' \
9
- 'not be a plaintext string or a Ref to a NoEcho Parameter with a ' \
10
- 'Default value.'
9
+ 'not be a plaintext string or a Ref to a Parameter with a ' \
10
+ 'Default value. ' \
11
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
11
12
  end
12
13
 
13
14
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class EMRClusterKerberosAttributesKdcAdminPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'EMR Cluster KerberosAttributes KdcAdmin Password must not be a ' \
9
- 'plaintext string or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'plaintext string or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class ElastiCacheReplicationGroupAuthTokenRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'ElastiCache ReplicationGroup AuthToken must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class IAMUserLoginProfilePasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'IAM User LoginProfile Password must not be a plaintext string or ' \
9
- 'a Ref to a NoEcho Parameter with a Default value.'
9
+ 'a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,8 +6,9 @@ require_relative 'password_base_rule'
6
6
  class KinesisFirehoseDeliveryStreamRedshiftDestinationConfigurationPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Kinesis Firehose DeliveryStream RedshiftDestinationConfiguration Password ' \
9
- 'must not be a plaintext string or a Ref to a NoEcho Parameter with a ' \
10
- 'Default value.'
9
+ 'must not be a plaintext string or a Ref to a Parameter with a ' \
10
+ 'Default value. ' \
11
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
11
12
  end
12
13
 
13
14
  def rule_type
@@ -6,8 +6,9 @@ require_relative 'password_base_rule'
6
6
  class KinesisFirehoseDeliveryStreamSplunkDestinationConfigurationHECTokenRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Kinesis Firehose DeliveryStream SplunkDestinationConfiguration HECToken ' \
9
- 'must not be a plaintext string or a Ref to a NoEcho Parameter with a ' \
10
- 'Default value.'
9
+ 'must not be a plaintext string or a Ref to a Parameter with a ' \
10
+ 'Default value. ' \
11
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
11
12
  end
12
13
 
13
14
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class LambdaPermissionEventSourceTokenRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Lambda Permission EventSourceToken must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class OpsWorksAppAppSourcePasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'OpsWorks App AppSource Password must not be a plaintext ' \
9
- 'string or a Ref to a NoEcho Parameter with a Default value.' \
9
+ 'string or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class OpsWorksAppSslConfigurationPrivateKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'OpsWorks App SslConfiguration PrivateKey must not be a plaintext ' \
9
- 'string or a Ref to a NoEcho Parameter with a Default value.' \
9
+ 'string or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class OpsWorksStackCustomCookbooksSourcePasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'OpsWorks Stack CustomCookbooksSource Password must not be a plaintext ' \
9
- 'string or a Ref to a NoEcho Parameter with a Default value.' \
9
+ 'string or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -5,8 +5,9 @@ require_relative 'sub_property_with_list_password_base_rule'
5
5
 
6
6
  class OpsWorksStackRdsDbInstancesDbPasswordRule < SubPropertyWithListPasswordBaseRule
7
7
  def rule_text
8
- 'OpsWorks Stack RDS DbInstance DbPassword must not be a plaintext ' \
9
- 'string or a Ref to a NoEcho Parameter with a Default value.' \
8
+ 'OpsWorks Stack RDS DbInstance DbPassword must not be a plaintext string '\
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSChannelPrivateKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSChannel PrivateKey must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSChannelTokenKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSChannel TokenKey must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSSandboxChannelPrivateKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSSandboxChannel PrivateKey must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSSandboxChannelTokenKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSSandboxChannel TokenKey must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSVoipChannelPrivateKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSVoipChannel PrivateKey must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSVoipChannelTokenKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSChannel TokenKey must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSVoipSandboxChannelPrivateKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSVoipSandboxChannel PrivateKey must not be a plaintext ' \
9
- 'string or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'string or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class PinpointAPNSVoipSandboxChannelTokenKeyRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Pinpoint APNSVoipSandboxChannel TokenKey must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class RDSDBClusterMasterUserPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'RDS DB Cluster master user password must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class RDSDBInstanceMasterUserPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'RDS instance master user password must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -7,7 +7,8 @@ require_relative 'password_base_rule'
7
7
  class RDSDBInstanceMasterUsernameRule < PasswordBaseRule
8
8
  def rule_text
9
9
  'RDS instance master username must not be a plaintext string ' \
10
- 'or a Ref to a NoEcho Parameter with a Default value.'
10
+ 'or a Ref to a Parameter with a Default value. ' \
11
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.'
11
12
  end
12
13
 
13
14
  def rule_type
@@ -6,7 +6,8 @@ require_relative 'password_base_rule'
6
6
  class RedshiftClusterMasterUserPasswordRule < PasswordBaseRule
7
7
  def rule_text
8
8
  'Redshift Cluster master user password must not be a plaintext string ' \
9
- 'or a Ref to a NoEcho Parameter with a Default value.'
9
+ 'or a Ref to a Parameter with a Default value. ' \
10
+ 'Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.'
10
11
  end
11
12
 
12
13
  def rule_type
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cfn-nag/violation'
4
+ require 'cfn-nag/iam_complexity_metric/spcm'
5
+ require_relative 'base'
6
+
7
+ class SPCMRule < BaseRule
8
+ attr_accessor :spcm_threshold
9
+ DEFAULT_THRESHOLD = 25
10
+
11
+ def rule_text
12
+ "SPCM for IAM policy document is higher than #{spcm_threshold || DEFAULT_THRESHOLD}"
13
+ end
14
+
15
+ def rule_type
16
+ Violation::WARNING
17
+ end
18
+
19
+ def rule_id
20
+ 'W76'
21
+ end
22
+
23
+ def audit_impl(cfn_model)
24
+ logical_resource_ids = []
25
+ begin
26
+ policy_documents = SPCM.new.metric_impl(cfn_model)
27
+ rescue StandardError => catch_all_exception
28
+ puts "Experimental SPCM rule is failing. Please report #{catch_all_exception} with the violating template"
29
+ policy_documents = {}
30
+ end
31
+
32
+ threshold = spcm_threshold.nil? ? DEFAULT_THRESHOLD : spcm_threshold.to_i
33
+ logical_resource_ids += violating_policy_resources(policy_documents, threshold)
34
+ logical_resource_ids += violating_role_resources(policy_documents, threshold)
35
+
36
+ logical_resource_ids
37
+ end
38
+
39
+ private
40
+
41
+ def violating_role_resources(policy_documents, threshold)
42
+ logical_resource_ids = []
43
+
44
+ # unfortunately the line numbers will break if we don't return
45
+ # the logical resource id - so there isn't a good way to communicate
46
+ # the specific policy within the role that is offending
47
+ policy_documents['AWS::IAM::Role'].each do |logical_resource_id, policies|
48
+ policies.each do |_, metric|
49
+ if metric >= threshold
50
+ logical_resource_ids << logical_resource_id
51
+ end
52
+ end
53
+ end
54
+ logical_resource_ids
55
+ end
56
+
57
+ def violating_policy_resources(policy_documents, threshold)
58
+ logical_resource_ids = []
59
+ policy_documents['AWS::IAM::Policy'].each do |logical_resource_id, metric|
60
+ if metric >= threshold
61
+ logical_resource_ids << logical_resource_id
62
+ end
63
+ end
64
+ logical_resource_ids
65
+ end
66
+ end
@@ -5,15 +5,16 @@ require_relative 'boolean_base_rule'
5
5
 
6
6
  class SecretsManagerSecretKmsKeyIdRule < BooleanBaseRule
7
7
  def rule_text
8
- 'Secrets Manager Secret should explicitly specify KmsKeyId'
8
+ 'Secrets Manager Secret should explicitly specify KmsKeyId.' \
9
+ ' Besides control of the key this will allow the secret to be shared cross-account'
9
10
  end
10
11
 
11
12
  def rule_type
12
- Violation::FAILING_VIOLATION
13
+ Violation::WARNING
13
14
  end
14
15
 
15
16
  def rule_id
16
- 'F81'
17
+ 'W77'
17
18
  end
18
19
 
19
20
  def resource_type
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'weights'
4
+ require 'set'
5
+
6
+ class ConditionMetric
7
+ include Weights
8
+
9
+ # rubocop:disable Metrics/AbcSize
10
+ def metric(statement)
11
+ return 0 if statement.condition.nil?
12
+
13
+ aggregate = 0
14
+ aggregate += statement.condition.size * weights[:Condition]
15
+ aggregate += confusing_value_operators(statement.condition)
16
+ aggregate += if_exists_operators(statement.condition)
17
+ aggregate += weights[:Null] if null_operator?(statement.condition)
18
+ aggregate += values_with_policy_tags(statement.condition)
19
+ aggregate
20
+ end
21
+ # rubocop:enable Metrics/AbcSize
22
+
23
+ private
24
+
25
+ def values_with_policy_tags(conditions)
26
+ all_values(conditions).reduce(0) do |aggregate, value|
27
+ aggregate + (contains_policy_tag?(value) ? weights[:PolicyVariables] : 0)
28
+ end
29
+ end
30
+
31
+ def contains_policy_tag?(value)
32
+ strip_special_characters(value).match(/.*\$\{.+\}.*/)
33
+ end
34
+
35
+ def strip_special_characters(value)
36
+ special_characters.each do |special_character|
37
+ value = value.gsub("${#{special_character}}", '')
38
+ end
39
+ value
40
+ end
41
+
42
+ def all_values(conditions)
43
+ result = []
44
+ conditions.each do |_, expression|
45
+ expression.each do |_, value|
46
+ if value.is_a? String
47
+ result << value
48
+ elsif value.is_a? Array
49
+ result += value
50
+ end
51
+ end
52
+ end
53
+ result
54
+ end
55
+
56
+ def special_characters
57
+ %w[$ * ?]
58
+ end
59
+
60
+ def null_operator?(conditions)
61
+ conditions.find { |operator, _| operator == 'Null' }
62
+ end
63
+
64
+ def if_exists_operators(conditions)
65
+ conditions.reduce(0) do |aggregate, condition|
66
+ operator = condition[0]
67
+ aggregate + (if_exists_operator?(operator) ? weights[:IfExists] : 0)
68
+ end
69
+ end
70
+
71
+ def if_exists_operator?(operator)
72
+ operator.end_with? 'IfExists'
73
+ end
74
+
75
+ def confusing_value_operators(conditions)
76
+ conditions.reduce(0) do |aggregate, condition|
77
+ operator = condition[0]
78
+ aggregate + (confusing_value_operator?(operator) ? weights[:Condition] : 0)
79
+ end
80
+ end
81
+
82
+ def confusing_value_operator?(operator)
83
+ %w[ForAllValues ForAnyValues].find { |prefix| operator.start_with? prefix }
84
+ end
85
+ end