foreman_cve_scanner 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4c537f1b7be3308efc867b96bb838fe5cad23d5f4994327a6ab50c1c478f1c9
4
- data.tar.gz: 3da22a4c998072bb98fbd822ecbe40c2863a3e4c8857a99d5266db1636d4f126
3
+ metadata.gz: 9eb6f60deb30166fd16ad5e1b8900650207187a7f369ec6143c3ae09c4863ba9
4
+ data.tar.gz: 5858c93d78633437209e5dfe60047f6e208de6b18643d3d6df6ac10b18c47fa2
5
5
  SHA512:
6
- metadata.gz: d7f9d8896d88d663c8b2c39a176fe21ed72837d3af69f02d7ea09c9661672fa8791840b0e69cd1529d8cadaf7afb05c7a3870de80124bc2a869dbaa029f40c67
7
- data.tar.gz: 01f880e09ae39020890b80871ceeecfdd6ea6029f4f6c9a481910839eae39ec796d313650f24fac8cf4148e45f2592d5f285d85936d9b6b193f7b87520b73954
6
+ metadata.gz: ab8170a8a127e4be11132dec5ff4325fa910380a6cdfa475feabd71dc22cbf7e48c93fd4ae2071dac78de06fced9a038847ddba4cbf36b29fc61608d3a3e55c6
7
+ data.tar.gz: b5e3bd7b539425c0c05d00041dca257bc037164a5bf9dc0d326abde21e32bc7ef99f57785f60592f448aac955ed68930575c959bb528e8ebd308a72c891ab571
data/README.md CHANGED
@@ -4,11 +4,8 @@ Plugin to
4
4
  1. install trivy/grype CVE scanners on a host using a Foreman Remote Execution (REX) job
5
5
  2. run a CVE scan using a REX job, collect the output and generate a Config Report
6
6
 
7
-
8
7
  ![image](https://github.com/ATIX-AG/foreman_cve_scanner/assets/25485845/85e3b676-7d90-41e5-bea5-7e0b5f4a685c)
9
8
 
10
- Not finished yet....
11
-
12
9
 
13
10
  *Introdction here*
14
11
 
@@ -19,11 +16,19 @@ for how to install Foreman plugins
19
16
 
20
17
  ## Usage
21
18
 
22
- *Usage here*
19
+ - Run the REX job to install trivy and/or grype
20
+ - Run the REX job to scan a host
21
+ - Go to the Config Report page for a host to view the scan report
23
22
 
24
23
  ## TODO
25
24
 
26
- *Todo list here*
25
+ - Better possiblities to filter the Config Report (maybe an extension to ConfigReport in Foreman)
26
+ - Have a scheduled REX Job to scan the hosts
27
+ - Make it visible on the Host Details page or on Foreman directly, if a high priority CVE on a host occurs
28
+ - Export a CVE scan
29
+ - Deliver trivy / grype via Katello
30
+ - More tests
31
+ - API
27
32
 
28
33
  ## Contributing
29
34
 
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env rake
2
1
  begin
3
2
  require 'bundler/setup'
4
3
  rescue LoadError
@@ -14,7 +13,7 @@ end
14
13
 
15
14
  RDoc::Task.new(:rdoc) do |rdoc|
16
15
  rdoc.rdoc_dir = 'rdoc'
17
- rdoc.title = 'ForemanPluginTemplate'
16
+ rdoc.title = 'ForemanCveScanner'
18
17
  rdoc.options << '--line-numbers'
19
18
  rdoc.rdoc_files.include('README.rdoc')
20
19
  rdoc.rdoc_files.include('lib/**/*.rb')
@@ -38,7 +37,7 @@ task default: :test
38
37
  begin
39
38
  require 'rubocop/rake_task'
40
39
  RuboCop::RakeTask.new
41
- rescue
40
+ rescue StandardError => _e
42
41
  puts 'Rubocop not loaded.'
43
42
  end
44
43
 
@@ -15,16 +15,16 @@ module Actions
15
15
 
16
16
  def finalize(*_args)
17
17
  host = Host.find(input[:host_id])
18
- if host.present?
19
- cve_scan_report = format_output(task.main_action.continuous_output.humanize)
20
- report = Hash.new
21
- report["host"] = host.name
22
- report["logs"] = []
23
- report["scan"] = cve_scan_report
24
- report["reported_at"] = Time.now.utc.to_s
25
- report['reporter'] = 'cve_scan'
26
- ConfigReportImporter::import(report)
27
- end
18
+ return if host.blank?
19
+
20
+ report = {
21
+ 'host' => host.name,
22
+ 'logs' => [],
23
+ 'scan' => format_output(task.main_action.continuous_output.humanize),
24
+ 'reported_at' => Time.now.utc.to_s,
25
+ 'reporter' => 'cve_scan'
26
+ }
27
+ ConfigReportImporter.import(report)
28
28
  end
29
29
 
30
30
  private
@@ -46,4 +46,3 @@ module Actions
46
46
  end
47
47
  end
48
48
  end
49
-
@@ -35,27 +35,21 @@ module ForemanCveScanner
35
35
  @logs
36
36
  end
37
37
 
38
- def logs
39
- @logs
40
- end
41
-
42
- def status
43
- @status
44
- end
38
+ attr_reader :logs, :status
45
39
 
46
40
  def metrics
47
41
  res = @status
48
42
  res['total'] = @status.values.sum
49
- return res
43
+ res
50
44
  end
51
45
 
52
46
  private
53
47
 
54
48
  def generate_log_from_unified(id, entry)
55
- return {
49
+ {
56
50
  'log': {
57
51
  'level': consume_severity_level(entry['severity']),
58
- 'messages': {
52
+ 'messages': {
59
53
  message: "#{id}: #{entry['title']} # url: #{entry['url']}"
60
54
  },
61
55
  sources: {
@@ -66,68 +60,71 @@ module ForemanCveScanner
66
60
  end
67
61
 
68
62
  def consume_severity_level(severity)
69
- @status[severity.downcase] = 0 unless @status.has_key?(severity.downcase)
63
+ @status[severity.downcase] = 0 unless @status.key?(severity.downcase)
70
64
  @status[severity.downcase] += 1
71
65
 
72
- log = case severity
73
- when 'CRITICAL'
74
- 'err'
75
- when 'HIGH'
76
- 'warning'
77
- when 'MEDIUM'
78
- 'info'
79
- when 'LOW'
80
- 'debug'
81
- else
82
- 'info'
83
- end
84
- return log
66
+ case severity
67
+ when 'CRITICAL'
68
+ 'err'
69
+ when 'HIGH'
70
+ 'warning'
71
+ when 'MEDIUM'
72
+ 'info'
73
+ when 'LOW'
74
+ 'debug'
75
+ else
76
+ 'info'
77
+ end
85
78
  end
86
79
 
87
80
  def generate_grype_entry(entry)
88
- unified = {}
89
- unified['name'] = entry['artifact']['name']
90
- unified['version'] = entry['artifact']['version']
91
- unified['title'] = entry['vulnerability']['description'].gsub(/[\[\]"\\]/, "")
92
- unified['severity'] = entry['vulnerability']['severity']
93
- unified['url'] = entry['vulnerability']['dataSource']
94
- return unified
81
+ {
82
+ 'name' => entry['artifact']['name'],
83
+ 'version' => entry['artifact']['version'],
84
+ 'title' => entry['vulnerability']['description'].gsub(/[\[\]"\\]/, ''),
85
+ 'severity' => entry['vulnerability']['severity'],
86
+ 'url' => entry['vulnerability']['dataSource']
87
+ }
95
88
  end
96
89
 
97
90
  def generate_trivy_entry(entry)
98
- unified = {}
99
- unified['name'] = entry['PkgName']
100
- unified['version'] = entry['InstalledVersion']
101
- unified['title'] = entry['Title'].gsub(/[\[\]"\\]/, "")
102
- unified['severity'] = entry['Severity']
103
- unified['url'] = entry['PrimaryURL']
104
- unified['status'] = entry['Status']
105
- unified['fixed'] = entry['FixedVersion'] || 'open'
106
- unified['published'] = entry['PublishedDate'] if entry.has_key?('PublishedDate')
107
- return unified
91
+ unified = {
92
+ 'name' => entry['PkgName'],
93
+ 'version' => entry['InstalledVersion'],
94
+ 'title' => entry['Title'].gsub(/[\[\]"\\]/, ''),
95
+ 'severity' => entry['Severity'],
96
+ 'url' => entry['PrimaryURL'],
97
+ 'status' => entry['Status'],
98
+ 'fixed' => entry['FixedVersion'] || 'open'
99
+ }
100
+ unified['published'] = entry['PublishedDate'] if entry.key?('PublishedDate')
101
+ unified
108
102
  end
109
103
 
104
+ # rubocop:disable Metrics/AbcSize
110
105
  def generate_unified_vuls
111
- j = @raw_data['scan']
106
+ raise ::Foreman::Exception, _('Invalid CVE scanner report') unless @raw_data.key?('scan')
112
107
 
108
+ j = @raw_data['scan']
113
109
  vuls = {}
114
- if j.has_key?('matches') # Grype
110
+ if j.key?('matches') # Grype
115
111
  j['matches'].each do |vul|
116
112
  vuls[vul['vulnerability']['id']] = generate_grype_entry(vul)
117
113
  end
118
- elsif j.has_key?('Results') # Trivy
114
+ elsif j.key?('Results') # Trivy
119
115
  j['Results'].each do |r|
120
- next unless r.has_key? 'Vulnerabilities'
116
+ next unless r.key? 'Vulnerabilities'
121
117
  r['Vulnerabilities'].each do |vul|
122
118
  vuls[vul['VulnerabilityID']] = generate_trivy_entry(vul)
123
119
  end
124
- end
120
+ end
125
121
  else
126
- Rails.logger.error "Unsupported cve scanner report format"
127
- raise ::Foreman::Exception.new(_('Invalid report'))
122
+ Rails.logger.error 'Unsupported cve scanner report format'
123
+ raise ::Foreman::Exception, _('Unsupported cve scanner report format')
128
124
  end
129
125
 
130
126
  vuls
131
127
  end
128
+ # rubocop:enable Metrics/AbcSize
132
129
  end
133
130
  end
@@ -7,27 +7,65 @@ job_category: Security
7
7
  provider_type: script
8
8
  template_inputs:
9
9
  - name: trivy_version
10
+ description: 'Trivy version to install'
10
11
  required: true
11
12
  input_type: user
12
13
  advanced: false
13
- value_type: plain
14
- default: 0.48.1
15
- hidden_value: false
14
+ default: 0.53.0
16
15
  - name: grype_version
16
+ description: 'Grype version to install'
17
17
  required: true
18
18
  input_type: user
19
19
  advanced: false
20
- value_type: plain
21
- default: 0.73.5
22
- hidden_value: false
20
+ default: 0.79.3
21
+ - name: scanner_to_install
22
+ description: 'Choose which scanner to install (Default: both)'
23
+ required: false
24
+ input_type: user
25
+ advanced: false
26
+ default: both
27
+ options: "both\r\ntrivy\r\ngrype"
23
28
  %>
24
29
  # Install CVE scanners from github
25
30
  <%
26
31
  trivy_version = input('trivy_version')
27
32
  grype_version = input('grype_version')
28
33
 
29
- trivy_url = "https://github.com/aquasecurity/trivy/releases/download/v#{trivy_version}/trivy_#{trivy_version}_Linux-64bit.rpm"
30
- grype_url = "https://github.com/anchore/grype/releases/download/v#{grype_version}/grype_#{grype_version}_linux_amd64.rpm"
34
+ case @host.operatingsystem.family.to_s
35
+ when 'Debian'
36
+ pkg = 'deb'
37
+ when 'Redhat', 'Suse'
38
+ pkg = 'rpm'
39
+ else
40
+ raise("OS '#{@host.operatingsystem.family}' not supported by template #{template_name}")
41
+ end
42
+
43
+ case @host.architecture.to_s
44
+ when 'x86_64'
45
+ trivy_arch = 'Linux-64bit'
46
+ grype_arch = 'linux-amd64'
47
+ when 'ppc64le'
48
+ trivy_arch = 'Linux-PPC64LE'
49
+ grype_arch = 'linux-ppc64le'
50
+ when 'aarch64'
51
+ trivy_arch = 'Linux-ARM64'
52
+ grype_arch = 'linux-arm64'
53
+ else
54
+ raise("Architecture '#{@host.architecture}' not supported by template #{template_name}")
55
+ end
56
+
57
+ trivy_url = "https://github.com/aquasecurity/trivy/releases/download/v#{trivy_version}/trivy_#{trivy_version}_#{trivy_arch}.#{pkg}"
58
+ grype_url = "https://github.com/anchore/grype/releases/download/v#{grype_version}/grype_#{grype_version}_#{grype_arch}.#{pkg}"
59
+
60
+ case @host.operatingsystem.family
61
+ when 'Debian'
62
+ trivy_install_cmd = "wget -o /tmp/outfile.deb #{trivy_url} && dpkg -i /tmp/outfile.deb; rm -f /tmp/outfile.deb"
63
+ grype_install_cmd = "wget -o /tmp/outfile.deb #{grype_url} && dpkg -i /tmp/outfile.deb; rm -f /tmp/outfile.deb"
64
+ when 'Redhat', 'Suse'
65
+ trivy_install_cmd = "rpm -ivh #{trivy_url}"
66
+ grype_install_cmd = "rpm -ivh #{grype_url}"
67
+ end
31
68
  -%>
32
69
 
33
- yum install --assumeyes <%= trivy_url %> <%= grype_url %>
70
+ <%= trivy_install_cmd if input('scanner_to_install') == 'both' || input('scanner_to_install') == 'trivy' %>
71
+ <%= grype_install_cmd if input('scanner_to_install') == 'both' || input('scanner_to_install') == 'grype' %>
@@ -54,7 +54,7 @@ scanners = {
54
54
 
55
55
  if scanner == 'trivy'
56
56
  cmd = "#{scanners[:trivy][target]} #{path}"
57
- options += " --scanners vuln --quiet --format json"
57
+ options += " --exit-code 0 --scanners vuln --quiet --format json"
58
58
  elsif scanner == 'grype'
59
59
  cmd = "#{scanners[:grype][target]}:#{path}"
60
60
  options += " --quiet --quiet --output json"
@@ -1,15 +1,16 @@
1
+ require 'foreman_remote_execution'
2
+
1
3
  module ForemanCveScanner
2
4
  class Engine < ::Rails::Engine
3
5
  engine_name 'foreman_cve_scanner'
4
6
 
5
- config.autoload_paths += Dir["#{config.root}/app/services"]
6
- config.autoload_paths += Dir["#{config.root}/app/lib"]
7
-
8
- initializer 'foreman_cve_scanner.register_plugin', :before => :finisher_hook do |_app|
9
- Foreman::Plugin.register :foreman_cve_scanner do
10
- requires_foreman '>= 3.5.0'
11
- register_report_scanner ForemanCveScanner::CveReportScanner
12
- register_report_origin 'CveScanner', 'ConfigReport'
7
+ initializer 'foreman_cve_scanner.register_plugin', :before => :finisher_hook do |app|
8
+ app.reloader.to_prepare do
9
+ Foreman::Plugin.register :foreman_cve_scanner do
10
+ requires_foreman '>= 3.13'
11
+ register_report_scanner ForemanCveScanner::CveReportScanner
12
+ register_report_origin 'CveScanner', 'ConfigReport'
13
+ end
13
14
  end
14
15
  end
15
16
 
@@ -1,3 +1,3 @@
1
1
  module ForemanCveScanner
2
- VERSION = '0.0.1'.freeze
2
+ VERSION = '0.0.2'.freeze
3
3
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tests
4
+ namespace :test do
5
+ desc 'Foreman Cve Scanner plugin tests'
6
+ Rake::TestTask.new(:foreman_cve_scanner) do |t|
7
+ test_dir = File.join(__dir__, '..', '..', 'test')
8
+ t.libs << ['test', test_dir]
9
+ t.pattern = "#{test_dir}/**/*_test.rb"
10
+ t.test_files = [Rails.root.join('test/unit/foreman/access_permissions_test.rb')]
11
+ t.verbose = false
12
+ t.warning = false
13
+ end
14
+ end
15
+
16
+ Rake::Task[:test].enhance ['test:foreman_cve_scanner']