Class: HeimdallTools::SecurityHub

Inherits:
Object
  • Object
show all
Defined in:
lib/heimdall_tools/asff_compatible_products/securityhub.rb

Class Method Summary collapse

Class Method Details

.finding_id(finding, encode:, controls: nil) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/heimdall_tools/asff_compatible_products/securityhub.rb', line 28

def self.finding_id(finding, *, encode:, controls: nil, **)
  ret = if !controls.nil? && !(control = corresponding_control(controls, finding)).nil?
          control['ControlId']
        elsif finding['ProductFields'].member?('ControlId') # check if aws
          finding['ProductFields']['ControlId']
        elsif finding['ProductFields'].member?('RuleId') # check if cis
          finding['ProductFields']['RuleId']
        else
          finding['GeneratorId'].split('/')[-1]
        end
  encode.call(ret)
end

.finding_impact(finding, controls: nil) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
# File 'lib/heimdall_tools/asff_compatible_products/securityhub.rb', line 41

def self.finding_impact(finding, *, controls: nil, **)
  if !controls.nil? && !(control = corresponding_control(controls, finding)).nil?
    imp = control['SeverityRating'].to_sym
  else
    # severity is required, but can be either 'label' or 'normalized' internally with 'label' being preferred.  other values can be in here too such as the original severity rating.
    imp = finding['Severity'].key?('Label') ? finding['Severity']['Label'].to_sym : finding['Severity']['Normalized']/100.0
    # securityhub asff file does not contain accurate severity information by setting things that shouldn't be informational to informational: when additional context, i.e. standards, is not provided, set informational to medium.
    imp = :MEDIUM if imp.is_a?(Symbol) && imp == :INFORMATIONAL
  end
  imp
end

.finding_nist_tag(finding, aws_config_mapping:) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/heimdall_tools/asff_compatible_products/securityhub.rb', line 53

def self.finding_nist_tag(finding, *, aws_config_mapping:, **)
  return {} unless finding['ProductFields']['RelatedAWSResources:0/type'] == 'AWS::Config::ConfigRule'

  entries = aws_config_mapping.select { |rule| finding['ProductFields']['RelatedAWSResources:0/name'].include? rule[:awsconfigrulename] }
  entries.map do |rule|
    tags_joined = rule[:nistid].split('|') # subheadings are joined together in the csv file
    tags_joined.map do |tag|
      if (i = tag.index('(')).nil?
        tag
      else
        tag[i..-1].scan(/\(.+?\)/).map { |subheading| "#{tag[0..i-1]}#{subheading}" }
      end
    end
  end.flatten.uniq
end

.finding_title(finding, encode:, controls: nil) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/heimdall_tools/asff_compatible_products/securityhub.rb', line 69

def self.finding_title(finding, *, encode:, controls: nil, **)
  ret = if !controls.nil? && !(control = corresponding_control(controls, finding)).nil?
          control['Title']
        else
          finding['Title']
        end
  encode.call(ret)
end

.product_name(findings, encode:) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/heimdall_tools/asff_compatible_products/securityhub.rb', line 78

def self.product_name(findings, *, encode:, **)
  # "#{findings[0]['ProductFields']['aws/securityhub/CompanyName']} #{findings[0]['ProductFields']['aws/securityhub/ProductName']}"
  # not using above due to wanting to provide the standard's name instead
  if findings[0]['Types'][0].split('/')[-1].gsub(/-/, ' ').downcase == findings[0]['ProductFields']['StandardsControlArn'].split('/')[-4].gsub(/-/, ' ').downcase
    standardname = findings[0]['Types'][0].split('/')[-1].gsub(/-/, ' ')
  else
    standardname = findings[0]['ProductFields']['StandardsControlArn'].split('/')[-4].gsub(/-/, ' ').split.map(&:capitalize).join(' ')
  end
  encode.call("#{standardname} v#{findings[0]['ProductFields']['StandardsControlArn'].split('/')[-2]}")
end

.supporting_docs(standards:) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/heimdall_tools/asff_compatible_products/securityhub.rb', line 10

def self.supporting_docs(standards:)
  begin
    controls = standards.nil? ? nil : standards.map { |s| JSON.parse(s)['Controls'] }.flatten
  rescue StandardError => e
    raise "Invalid supporting docs for Security Hub:\nException: #{e}"
  end

  begin
    resource_dir = Pathname.new(__FILE__).join('../../../data')
    aws_config_mapping_file = File.join(resource_dir, 'aws-config-mapping.csv')
    aws_config_mapping = CSV.read(aws_config_mapping_file, { encoding: 'UTF-8', headers: true, header_converters: :symbol }).map(&:to_hash)
  rescue StandardError => e
    raise "Invalid AWS Config mapping file:\nException: #{e}"
  end

  { controls: controls, aws_config_mapping: aws_config_mapping }
end