Skip to content

Commit 44f9aff

Browse files
author
Rory McCune
committed
clairautoanalyzer added
1 parent 9b5667c commit 44f9aff

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed

Diff for: clairautoanalyzer.rb

+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env ruby
2+
# == Synopsis
3+
# This script is designed to co-ordinate parsing of Clair Vulnerability scanner JSON files and production of a concise set findings.
4+
#
5+
# WARNING This isn't ready for use yet, don't do it, you'll be sorry!
6+
#
7+
# There are 2 modes of operation.
8+
#
9+
# Directory mode just takes a parameter of the directory containing the xml files and goes and parses any files found there
10+
#
11+
# File mode takes a parameter of a single file and parses that
12+
#
13+
#TODO:
14+
# == Author
15+
# Author:: Rory McCune
16+
# Copyright:: Copyright (c) 2018 Rory Mccune
17+
# License:: GPLv3
18+
#
19+
# This program is free software: you can redistribute it and/or modify
20+
# it under the terms of the GNU General Public License as published by
21+
# the Free Software Foundation, either version 3 of the License, or
22+
# (at your option) any later version.
23+
#
24+
# This program is distributed in the hope that it will be useful,
25+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27+
# GNU General Public License for more details.
28+
#
29+
# You should have received a copy of the GNU General Public License
30+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
31+
#
32+
# == Options
33+
# -h, --help Displays help message
34+
# -v, --version Display the version, then exit
35+
# -d <dir>, --directory <dir> Only needed in directory mode name of the directory to scan
36+
# -f <file>, --file <file> Only needed in file mode, name of the file to parse
37+
# -r <file>, --report <file> Name of file for reporting
38+
# -l <file> Log debug messages to a file.
39+
# --reportDirectory <dir> Place the report in a different directory
40+
#
41+
# == Usage
42+
#
43+
# Directory Mode
44+
# clairautoanalyzer.rb -m directory -d <directoryname> -r <reportfile>
45+
# File Mode
46+
# clairautoanalyzer.rb -m file -f <filename> -r <reportfile>
47+
48+
49+
50+
class ClairAutoAnalyzer
51+
VERSION='0.0.1'
52+
53+
def initialize(commandlineopts)
54+
#This is StdLib so shouldn't need a rescue for failed require
55+
require 'json'
56+
require 'logger'
57+
58+
@options = commandlineopts
59+
@base_dir = @options.report_directory
60+
@scan_dir = @options.scan_directory
61+
if !File.exists?(@base_dir)
62+
Dir.mkdirs(@base_dir)
63+
end
64+
65+
if @options.logger
66+
@log = Logger.new(@base_dir + '/' + @options.logger)
67+
else
68+
@log = Logger.new(STDOUT)
69+
end
70+
#Change this to Logger::DEBUG if you want to debug stuff
71+
@log.level = Logger::DEBUG
72+
73+
@log.debug("Log created at " + Time.now.to_s)
74+
@log.debug("Scan type is : #{@options.scan_type}")
75+
@log.debug("Directory being scanned is : #{@options.scan_directory}") if @options.scan_type == :directory
76+
@log.debug("File being scanned is : #{@options.scan_file}") if @options.scan_type == :file
77+
end
78+
79+
def run
80+
case @options.scan_type
81+
when :directory
82+
scan_dirs
83+
parse_files
84+
excel_report
85+
when :file
86+
@scan_files = Array.new
87+
@scan_files << @options.scan_file
88+
parse_files
89+
excel_report
90+
end
91+
end
92+
93+
def scan_dirs
94+
@scan_files = Array.new
95+
@log.debug("Scan directory is #{@scan_dir}")
96+
Dir.entries(@scan_dir).each do |scan|
97+
next if File.directory?(@scan_dir + '/' + scan)
98+
@scan_files << @scan_dir + '/' + scan
99+
end
100+
end
101+
102+
def parse_files
103+
@image_results = Hash.new
104+
@log.debug("Files to be looked at : #{@scan_files.join(', ')}")
105+
@scan_files.each do |file|
106+
file_content = File.open(file,'r').read
107+
begin
108+
@log.debug("File name is " + file)
109+
doc = JSON.parse(file_content)
110+
rescue JSON::ParserError => e
111+
@log.warn("We got a parser error on #{file}")
112+
next
113+
end
114+
115+
begin
116+
@log.debug("Got a valid JSON file called #{file}, processing...")
117+
parse_file(doc)
118+
rescue Exception => e
119+
@log.warn("We got a parsing error on a valid JSON file #{file}")
120+
@log.warn(e)
121+
end
122+
end
123+
end
124+
125+
def parse_file(doc)
126+
image = doc['image']
127+
@image_results[image] = Hash.new
128+
doc['vulnerabilities'].each do |vuln|
129+
@image_results[image][vuln['vulnerability']] = [vuln['severity'], vuln['featurename']]
130+
end
131+
end
132+
133+
def excel_report
134+
begin
135+
require 'rubyXL'
136+
rescue LoadError
137+
puts "Excel report needs the rubyXL gem"
138+
exit
139+
end
140+
workbook = RubyXL::Workbook.new
141+
vuln_sheet = workbook.worksheets[0]
142+
vuln_sheet.sheet_name = "Docker Image Vulnerabilities"
143+
vuln_sheet.add_cell(0,0,"Image Name")
144+
vuln_sheet.add_cell(0,1,"CVE ID")
145+
vuln_sheet.add_cell(0,2,"Severity")
146+
vuln_sheet.add_cell(0,3,"Affected Package")
147+
row_count = 1
148+
@image_results.each do |image, results|
149+
results.each do |cve, data|
150+
vuln_sheet.add_cell(row_count,0,image)
151+
vuln_sheet.add_cell(row_count,1,cve)
152+
vuln_sheet.add_cell(row_count,2,data[0])
153+
vuln_sheet.add_cell(row_count,3,data[1])
154+
row_count = row_count + 1
155+
end
156+
end
157+
workbook.write(@options.report_file + '.xlsx')
158+
end
159+
end
160+
161+
162+
163+
if __FILE__ == $0
164+
require 'ostruct'
165+
require 'optparse'
166+
options = OpenStruct.new
167+
168+
#Set some defaults in the options hash
169+
options.report_directory = Dir.pwd
170+
options.report_file = 'testssl-parse-report'
171+
options.scan_directory = Dir.pwd
172+
options.scan_file = ''
173+
options.scan_type = :notset
174+
175+
176+
opts = OptionParser.new do |opts|
177+
opts.banner = "Clair Auto analyzer #{ClairAutoAnalyzer::VERSION}"
178+
179+
opts.on("-d", "--directory [DIRECTORY]", "Directory to scan for Clair json files") do |dir|
180+
options.scan_directory = dir
181+
options.scan_type = :directory
182+
end
183+
184+
opts.on("-f", "--file [FILE]", "File to analyze including path") do |file|
185+
options.scan_file = file
186+
options.scan_type = :file
187+
end
188+
189+
opts.on("-r", "--report [REPORT]", "Base Report Name") do |rep|
190+
options.report_file = rep
191+
end
192+
193+
opts.on("--reportDirectory [REPORTDIRECTORY]", "Directory to output reports to") do |repdir|
194+
options.report_directory = repdir
195+
end
196+
197+
opts.on("-l", "--log [LOGGER]", "Log debugging messages to a file") do |logger|
198+
options.logger = logger
199+
end
200+
201+
opts.on("-h", "--help", "-?", "--?", "Get Help") do |help|
202+
puts opts
203+
exit
204+
end
205+
206+
opts.on("-v", "--version", "Get Version") do |ver|
207+
puts "Clair Analyzer Version #{ClairAutoAnalyzer::VERSION}"
208+
exit
209+
end
210+
211+
end
212+
213+
opts.parse!(ARGV)
214+
215+
#Check for missing required options
216+
unless (options.scan_type == :file || options.scan_type == :directory)
217+
puts "didn't get any arguments or missing scan type"
218+
puts opts
219+
exit
220+
end
221+
222+
analysis = ClairAutoAnalyzer.new(options)
223+
analysis.run
224+
end

0 commit comments

Comments
 (0)