Top Level Namespace

Defined Under Namespace

Modules: CemAcpt Classes: IO, RunLogs

Constant Summary collapse

CONFIG_PATH =
'/opt/cem_acpt/scan/scan_config.json'
REPORT_DIR =
'/opt/cem_acpt/scan/reports'

Instance Method Summary collapse

Instance Method Details

#load_configObject



25
26
27
28
29
# File 'lib/terraform/gcp/linux/scan/scan_service.rb', line 25

def load_config
  JSON.parse(File.read(CONFIG_PATH))
rescue StandardError => e
  { 'error' => "Cannot read #{CONFIG_PATH}: #{e.class}: #{e.message}" }
end

#parse_ciscat_json(path) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/terraform/gcp/linux/scan/scan_service.rb', line 94

def parse_ciscat_json(path)
  return { 'error' => "Result file missing: #{path}" } unless File.exist?(path)

  raw = JSON.parse(File.read(path))
  results = (raw['rules'] || []).map do |r|
    {
      'id' => r['rule-id'],
      'result' => r['result'],
    }
  end
  score = (raw['score'] || 0.0).to_f
  counts = results.group_by { |r| (r['result'] || '').to_s.downcase }.transform_values(&:size)
  {
    'score' => score,
    'passed_count' => counts['pass'] || 0,
    'failed_count' => counts['fail'] || 0,
    'not_applicable_count' => (counts['notchecked'] || 0) + (counts['notapplicable'] || 0),
    'error_count' => (counts['error'] || 0) + (counts['informational'] || 0),
    'rules' => results,
  }
end

#parse_openscap_xml(path) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/terraform/gcp/linux/scan/scan_service.rb', line 50

def parse_openscap_xml(path)
  return { 'error' => "Result file missing: #{path}" } unless File.exist?(path)

  doc = REXML::Document.new(File.read(path))
  results = REXML::XPath.match(doc, '//rule-result').map do |r|
    {
      'id' => r.attributes['idref'],
      'severity' => r.attributes['severity'],
      'result' => REXML::XPath.first(r, 'result')&.text,
    }
  end
  score_el = REXML::XPath.first(doc, '//score')
  score = score_el ? score_el.text.to_f : 0.0
  counts = results.group_by { |r| r['result'] }.transform_values(&:size)
  {
    'score' => score,
    'passed_count' => counts['pass'] || 0,
    'failed_count' => counts['fail'] || 0,
    'not_applicable_count' => (counts['notapplicable'] || 0) + (counts['notselected'] || 0),
    'error_count' => (counts['error'] || 0) + (counts['unknown'] || 0),
    'rules' => results,
  }
end

#perform_scanObject



116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/terraform/gcp/linux/scan/scan_service.rb', line 116

def perform_scan
  cfg = load_config
  return cfg if cfg['error']

  case cfg['scanner']
  when 'openscap'
    run_openscap(cfg['profile'], cfg['datastream'])
  when 'ciscat'
    run_ciscat(cfg['profile'], cfg['benchmark'])
  else
    { 'error' => "Unknown scanner '#{cfg['scanner']}'" }
  end
end

#run_ciscat(profile, benchmark) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/terraform/gcp/linux/scan/scan_service.rb', line 74

def run_ciscat(profile, benchmark)
  json_out = File.join(REPORT_DIR, 'ciscat-results.json')
  cmd = [
    'sudo', '/opt/cis-cat-pro/Assessor-CLI.sh',
    '-rd', REPORT_DIR,
    '-rp', 'ciscat-results',
    '-nts',
    '-json',
    '-p', profile,
  ]
  cmd += ['-b', "/opt/cis-cat-pro/benchmarks/#{benchmark}"] if benchmark
  _stdout, stderr, status = Open3.capture3(*cmd)
  parsed = parse_ciscat_json(json_out)
  unless status.success? || File.exist?(json_out)
    assessor_msg = "Assessor-CLI exited #{status.exitstatus}: #{stderr.strip}"
    parsed['error'] = parsed['error'] ? "#{assessor_msg}; #{parsed['error']}" : assessor_msg
  end
  parsed
end

#run_openscap(profile, datastream) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/terraform/gcp/linux/scan/scan_service.rb', line 31

def run_openscap(profile, datastream)
  xml_out = File.join(REPORT_DIR, 'openscap-results.xml')
  html_out = File.join(REPORT_DIR, 'openscap-results.html')
  cmd = [
    'sudo', 'oscap', 'xccdf', 'eval',
    '--profile', profile,
    '--results', xml_out,
    '--report', html_out,
    datastream,
  ]
  _stdout, stderr, status = Open3.capture3(*cmd)
  parsed = parse_openscap_xml(xml_out)
  unless status.success? || File.exist?(xml_out)
    oscap_msg = "oscap exited #{status.exitstatus}: #{stderr.strip}"
    parsed['error'] = parsed['error'] ? "#{oscap_msg}; #{parsed['error']}" : oscap_msg
  end
  parsed
end