nessana 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 841b59ccd2b8e575f45f778d97a1b5c7b158c13fca2bf3bf554792ef25d931b7
4
+ data.tar.gz: 056feefeef224de161623a01593d0620def012e44346ab9d5cdae79174d41e29
5
+ SHA512:
6
+ metadata.gz: 9b1d6e33a8fccc0a1b6685c5ee6f4d98ef24a1084bc7066138a851c700b886c422c79858cb80dc33eb45ae779ee50a2a09eb234e0eb683fd7ff78105addbaaed
7
+ data.tar.gz: 2012a1a887a6b291c1b82636504104808335f6901beb7deca7ed3acf8dc38b220a39bc251df8b49ba6ec8e5d50eb693dada3e4e99343c02ff5885b9544f9c2e7
@@ -0,0 +1,13 @@
1
+ # nessana
2
+
3
+ Parse Nessus dumps and intelligently create Asana tasks.
4
+
5
+ ## Usage
6
+
7
+ ```console
8
+ $ nessana -c config.yml scan.csv
9
+ ```
10
+
11
+ ## Contributing
12
+
13
+ See [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'nessana/executor'
4
+
5
+ Nessana::Executor.execute!
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'asana'
4
+ require 'csv'
5
+ require 'json'
6
+ require 'logger'
7
+ require 'pp'
8
+ require 'pry'
9
+ require 'ruby-prof'
10
+ require 'ruby-prof-flamegraph'
11
+ require 'yaml'
12
+
13
+ require 'nessana/vulnerability'
14
+ require 'nessana/vulnerability_list'
15
+
16
+ result = RubyProf.profile do
17
+
18
+ $logger = Logger.new(STDOUT)
19
+ $logger.level = Logger::DEBUG
20
+
21
+ $file_contents = open('secrets.yml', 'rb') do |io|
22
+ io.read
23
+ end
24
+
25
+ $asana_access_token = YAML.load($file_contents)['ASANA_PAT']
26
+
27
+ $client = Asana::Client.new do |c|
28
+ c.authentication :access_token, $asana_access_token
29
+ end
30
+
31
+ workspace = $client.workspaces.find_all.select do |workspace|
32
+ workspace.name == 'stolaf.org'
33
+ end.first
34
+
35
+ project = $client.projects.find_all(workspace: workspace.id).select do |project|
36
+ project.name == '[Sys] Security'
37
+ end.first
38
+
39
+ tag = $client.tags.find_all(workspace: workspace.id).select do |tag|
40
+ tag.name == 'Automated [Nessana]'
41
+ end.first
42
+
43
+ pp project.sections
44
+
45
+ tasks = $client.tasks.find_by_tag(tag: tag.id).map do |flat_task|
46
+ $logger.debug "Fetching task with id=#{flat_task.id}"
47
+ $client.tasks.find_by_id(flat_task.id).to_h
48
+ end.select do |task_hash|
49
+ !task_hash["completed"]
50
+ end
51
+
52
+ pp tasks
53
+
54
+ end
55
+
56
+ printer = RubyProf::FlameGraphPrinter.new(result)
57
+ printer.print(STDERR, {})
58
+
59
+ __END__
60
+
61
+ output = nil
62
+
63
+ result = RubyProf.profile do
64
+ vulnerabilities = VulnerabilityList.from_csv(ARGV[0])
65
+
66
+ $vulnerabilities = vulnerabilities.filter_risks.filter_not_accessible
67
+
68
+ # vuln_plugin_mapping = $vulnerabilities.each_with_object({}) do |vuln, hash|
69
+ # cve_string = vuln.cve ? " (#{vuln.cve})" : ""
70
+ # puts "Adding entry for #{vuln.plugin_id}#{cve_string} on host #{vuln.host}:#{vuln.port} (#{vuln.protocol})"
71
+ # hash[vuln.plugin_id] ||= []
72
+ # hash[vuln.plugin_id].push vuln
73
+ # end
74
+
75
+ vulns_by_plugin = $vulnerabilities.each_with_object({}) do |vuln, hash|
76
+ hash[vuln.plugin_id] ||= []
77
+ hash[vuln.plugin_id] << vuln
78
+ end
79
+
80
+ reports = vulns_by_plugin.map do |plugin_id, vulns|
81
+ uniqued_titles = vulns.map do |vuln|
82
+ vuln.name
83
+ end.uniq
84
+
85
+ uniqued_cves = vulns.map do |vuln|
86
+ vuln.cve
87
+ end.uniq
88
+
89
+ uniqued_cvsss = vulns.map do |vuln|
90
+ vuln.cvss
91
+ end.uniq
92
+
93
+ uniqued_risks = vulns.map do |vuln|
94
+ vuln.risk
95
+ end.uniq
96
+
97
+ throw "Plugin #{plugin_id} produced #{uniqued_titles.count} != 1 unique titles!" unless uniqued_titles.count == 1
98
+ throw "Plugin #{plugin_id} produced #{uniqued_cvsss.count} != 1 unique CVSS's!" unless uniqued_cvsss.count == 1
99
+ throw "Plugin #{plugin_id} produced #{uniqued_risks.count} != 1 unique risks!" unless uniqued_risks.count == 1
100
+
101
+ uniqued_hosts = vulns.map do |vuln|
102
+ vuln.readable_host
103
+ end.uniq
104
+
105
+ uniqued_synopses = vulns.map do |vuln|
106
+ vuln.synopsis
107
+ end.uniq
108
+
109
+ throw "More than one unique synopsis given?" unless uniqued_synopses.count == 1
110
+
111
+ uniqued_descriptions = vulns.map do |vuln|
112
+ vuln.description
113
+ end.uniq
114
+
115
+ throw "More than one unique description given?" unless uniqued_descriptions.count == 1
116
+
117
+ uniqued_solutions = vulns.map do |vuln|
118
+ vuln.solution
119
+ end.uniq
120
+
121
+ throw "More than one unique solution given?" unless uniqued_solutions.count == 1
122
+
123
+ {
124
+ cvss: uniqued_cvsss.first,
125
+ title: "[Nessus #{plugin_id}] #{uniqued_titles.join(', ')}",
126
+ body: "CVE: #{uniqued_cves.first || 'N/A'}\nCVSS: #{uniqued_cvsss.first || 'N/A'}\nRisk: #{uniqued_risks.first || 'N/A'}\n\nSYNOPSIS\n\n#{uniqued_synopses.first}\n\nDESCRIPTION\n\n#{uniqued_descriptions.first.join("\n\n")}\n\nSOLUTION\n\n#{uniqued_solutions.first.join("\n\n")}\n\nThis issue was detected on #{uniqued_hosts.count} hosts: #{uniqued_hosts.join(', ')}",
127
+ hosts: uniqued_hosts
128
+ }
129
+ end
130
+
131
+ output = reports.sort do |report_a, report_b|
132
+ report_b[:cvss] <=> report_a[:cvss]
133
+ end.map do |report|
134
+ [report[:title], report[:body]]
135
+ end.to_a
136
+ end
137
+
138
+ printer = RubyProf::GraphPrinter.new(result)
139
+ printer.print(STDOUT, {})
140
+
141
+ CSV.open(ARGV[1], 'wb') do |csv|
142
+ output.each do |row|
143
+ csv << row
144
+ end
145
+ end
@@ -0,0 +1,6 @@
1
+ require 'nessana/detection'
2
+ require 'nessana/dump'
3
+ require 'nessana/executor'
4
+ require 'nessana/filter'
5
+ require 'nessana/version'
6
+ require 'nessana/vulnerability'
@@ -0,0 +1,45 @@
1
+ module Nessana
2
+ class Detection
3
+ attr_reader :host, :protocol, :port
4
+ attr_accessor :status
5
+
6
+ def initialize(host, protocol, port, status = nil)
7
+ @host = host
8
+ @protocol = protocol
9
+ @port = port
10
+ @status = status
11
+ end
12
+
13
+ def to_s
14
+ "#{@status ? status_prefix : ''}#{@host}:#{@port}/#{@protocol}"
15
+ end
16
+
17
+ def hash
18
+ "#{@host}:#{@port}/#{@protocol}".hash
19
+ end
20
+
21
+ def ==(other)
22
+ @host == other.host &&
23
+ @protocol == other.protocol &&
24
+ @port == other.port &&
25
+ @status == other.status || false
26
+ end
27
+
28
+ alias eql? ==
29
+
30
+ protected
31
+
32
+ def status_prefix
33
+ case @status
34
+ when :added
35
+ '+ '
36
+ when :removed
37
+ '- '
38
+ when :present
39
+ ' '
40
+ else
41
+ '? '
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,176 @@
1
+ require 'time'
2
+
3
+ require 'csv'
4
+ require 'fastcsv'
5
+ require 'tty-spinner'
6
+
7
+ require 'nessana/detection'
8
+ require 'nessana/vulnerability'
9
+
10
+ module Nessana
11
+ class Dump < Hash
12
+ attr_reader :filters
13
+ attr_reader :filename
14
+
15
+ def initialize(filename = nil, filters = [])
16
+ @filename, @filters = filename, filters
17
+
18
+ if @filename
19
+ if File.readable?(@filename)
20
+ spinner_options = {
21
+ success_mark: "\u2713".encode('utf-8'),
22
+ format: :dots_3
23
+ }
24
+ spinner = TTY::Spinner.new("[:spinner] Loading #{@filename}...", **spinner_options)
25
+ spinner.auto_spin
26
+
27
+ read_csv!
28
+
29
+ spinner.success('done!')
30
+ else
31
+ throw 'file not readable; sad face'
32
+ end
33
+ end
34
+ end
35
+
36
+ def -(other)
37
+ spinner_options = {
38
+ success_mark: "\u2713".encode('utf-8'),
39
+ format: :dots_3
40
+ }
41
+
42
+ spinner = TTY::Spinner.new('[:spinner] :action...', **spinner_options)
43
+ spinner.update(action: 'Generating detections...')
44
+
45
+ other_plugin_ids = other.keys
46
+ self_plugin_ids = keys
47
+
48
+ spinner.update(action: 'Finding L detections')
49
+
50
+ other_detection_pairs = other.map do |plugin_id, vulnerability|
51
+ spinner.update(action: "Finding L detections (#{plugin_id})")
52
+ spinner.auto_spin
53
+
54
+ vulnerability.detections.map do |detection|
55
+ { plugin_id => detection }
56
+ end
57
+ end
58
+
59
+ other_detections = Set.new(other_detection_pairs.flatten)
60
+
61
+ spinner.update(action: 'Finding R detections')
62
+
63
+ detection_pairs = map do |plugin_id, vulnerability|
64
+ spinner.update(action: "Finding R detections (#{plugin_id})")
65
+ spinner.auto_spin
66
+
67
+ vulnerability.detections.map do |detection|
68
+ { plugin_id => detection }
69
+ end
70
+ end
71
+
72
+ self_detections = Set.new(detection_pairs.flatten)
73
+
74
+ spinner.update(action: 'Joining detection sets')
75
+ spinner.auto_spin
76
+
77
+ detections = Set.new([other_detections, self_detections]).flatten
78
+
79
+ spinner.update(action: 'Processing detections')
80
+ spinner.auto_spin
81
+
82
+ detections.each do |detection_entry|
83
+ in_self = self_detections.include? detection_entry
84
+ in_other = other_detections.include? detection_entry
85
+
86
+ detection = detection_entry.values.first
87
+
88
+ if in_self && in_other
89
+ detection.status = :present
90
+ elsif !in_self && in_other
91
+ detection.status = :removed
92
+ elsif in_self && !in_other
93
+ detection.status = :added
94
+ else
95
+ detection.status = true
96
+ end
97
+ end
98
+
99
+ spinner.success('done!')
100
+
101
+ added_plugin_ids = self_plugin_ids - other_plugin_ids
102
+ deleted_plugin_ids = other_plugin_ids - self_plugin_ids
103
+ all_plugin_ids = other_plugin_ids + added_plugin_ids
104
+
105
+ all_vulnerabilities = all_plugin_ids.map do |plugin_id|
106
+ vulnerability = nil
107
+
108
+ if !self[plugin_id]
109
+ vulnerability = other[plugin_id].clone
110
+ else
111
+ vulnerability = self[plugin_id].clone
112
+ end
113
+
114
+ plugin_detections = detections.select do |detection_entry|
115
+ detection_entry.keys.first == vulnerability.plugin_id
116
+ end.map do |detection_entry|
117
+ detection_entry.values.first
118
+ end
119
+
120
+ vulnerability.detections = plugin_detections.map do |detection|
121
+ detection.dup
122
+ end
123
+
124
+ vulnerability
125
+ end
126
+
127
+ all_vulnerabilities
128
+ end
129
+
130
+ def read_csv!
131
+ data = read_csv(filename)
132
+
133
+ filtered_data = data.select do |_, vulnerability|
134
+ !vulnerability.matches?(@filters)
135
+ end
136
+
137
+ merge!(filtered_data)
138
+ end
139
+
140
+ protected
141
+
142
+ def read_csv(filename)
143
+ dump_data = {}
144
+
145
+ first_row = true
146
+
147
+ File.open(filename, 'rb') do |io|
148
+ io.advise(:willneed)
149
+ io.advise(:noreuse)
150
+ io.advise(:sequential)
151
+
152
+ FastCSV.raw_parse(io) do |row|
153
+ if first_row
154
+ first_row = false
155
+ next
156
+ end
157
+
158
+ row_nessus_data = row[0..3] + row[7..-1]
159
+ row_detection_data = row[4..6]
160
+
161
+ plugin_id = row[0]
162
+
163
+ unless dump_data[plugin_id]
164
+ dump_data[plugin_id] = Vulnerability.new(*row_nessus_data)
165
+ end
166
+
167
+ row_detection = Detection.new(*row_detection_data)
168
+
169
+ dump_data[plugin_id].add_detection(row_detection)
170
+ end
171
+ end
172
+
173
+ dump_data
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,100 @@
1
+ require 'optparse'
2
+ require 'pp'
3
+ require 'ruby-prof'
4
+ require 'ruby-prof-flamegraph'
5
+
6
+ require 'nessana/executor/execution_configuration'
7
+ require 'nessana/filter'
8
+ require 'nessana/dump'
9
+
10
+ module Nessana
11
+ module Executor
12
+ def self.print_usage!
13
+ puts @parser
14
+ end
15
+
16
+ def self.execute!(argv = ARGV)
17
+ parse!(*argv)
18
+
19
+ RubyProf.start if !!@configuration['performance']
20
+
21
+ unless @configuration['old_filename']
22
+ $stderr.puts 'No old dump filename given; will assume no prior knowledge.'
23
+ end
24
+
25
+ unless @configuration['new_filename']
26
+ puts 'No new dump filename given; cannot do anything.'
27
+ return
28
+ end
29
+
30
+ filters = @configuration['filters'].map do |filter_hash|
31
+ Filter.new(filter_hash)
32
+ end
33
+
34
+ old_dump = @configuration['old_filename'] ? Dump.new(@configuration['old_filename'], filters) : Dump.new
35
+ new_dump = Dump.new(@configuration['new_filename'], filters)
36
+
37
+ diff = new_dump - old_dump
38
+
39
+ diff.sort do |vulnerability_a, vulnerability_b|
40
+ vulnerability_a.plugin_id.to_i <=> vulnerability_b.plugin_id.to_i
41
+ end.sort do |vulnerability_a, vulnerability_b|
42
+ vulnerability_a.cvss.to_f <=> vulnerability_b.cvss.to_f
43
+ end.each do |v|
44
+ puts "#{v}
45
+
46
+ DISCOVERIES"
47
+ v.detections.sort_by(&:port).sort_by(&:host).each do |detection|
48
+ if detection.status && detection.status != true
49
+ puts detection.to_s
50
+ end
51
+ end
52
+ puts "\n" * 2
53
+ end
54
+
55
+ if !!@configuration['performance']
56
+ result = RubyProf.stop
57
+ printer = RubyProf::FlameGraphPrinter.new(result)
58
+ io = open(@configuration['performance'], 'wb')
59
+ printer.print(io, {})
60
+ end
61
+ end
62
+
63
+ def self.parse(*argv)
64
+ configuration = ExecutionConfiguration.new
65
+
66
+ option_parser = OptionParser.new do |parser|
67
+ configuration.add_parser_hooks(parser)
68
+
69
+ if argv.count == 0
70
+ puts parser
71
+ exit 1
72
+ end
73
+
74
+ parser.parse(*argv)
75
+ end
76
+
77
+ remaining_arguments = option_parser.order!(argv)
78
+
79
+ case remaining_arguments.count
80
+ when 2
81
+ configuration['old_filename'] = remaining_arguments[0]
82
+ configuration['new_filename'] = remaining_arguments[1]
83
+ when 1
84
+ configuration['old_filename'] = nil
85
+ configuration['new_filename'] = remaining_arguments[0]
86
+ end
87
+
88
+ configuration.read_configuration_file!
89
+
90
+ configuration
91
+ end
92
+
93
+ protected
94
+
95
+ def self.parse!(*argv)
96
+ @configuration = parse(*argv)
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,88 @@
1
+ require 'mime-types'
2
+
3
+ require 'nessana/version'
4
+
5
+ module Nessana::Executor
6
+ class ExecutionConfiguration < ::Hash
7
+ def initialize
8
+ self['verbosity'] = 'info'
9
+ self['config'] = 'config.yml'
10
+ self['dump_filename'] = nil
11
+ end
12
+
13
+ # FIXME: too many lines
14
+ def add_parser_hooks(parser)
15
+ parser.banner = "Usage: #{$PROGRAM_NAME} [options] <filename.csv>"
16
+
17
+ parser.separator ''
18
+ parser.separator 'Execution Options'
19
+
20
+ add_config_option(parser)
21
+
22
+ parser.separator ''
23
+ parser.separator 'General Options'
24
+
25
+ add_usage_option(parser)
26
+ add_verbosity_option(parser)
27
+
28
+ parser.on_tail('-h', '--help', 'Show this message') do
29
+ puts parser
30
+ exit
31
+ end
32
+
33
+ parser.on_tail('--version', 'Show version') do
34
+ puts Nessana::VERSION
35
+ exit
36
+ end
37
+ end
38
+
39
+ # TODO: deep merge?
40
+ def read_configuration_file!
41
+ merge!(read_configuration_file(self['config']))
42
+ end
43
+
44
+ protected
45
+
46
+ def add_config_option(parser)
47
+ parser.on('-c', '--config CONFIG', "Load configuration from CONFIG (default: #{self['config']})") do |config|
48
+ self['config'] = config
49
+ end
50
+ end
51
+
52
+ def add_usage_option(parser)
53
+ parser.on('-h', '--help', 'Print usage summary.') do
54
+ puts parser
55
+ exit 0
56
+ end
57
+ end
58
+
59
+ def add_verbosity_option(parser)
60
+ parser.on('-v', '--verbosity VERBOSITY', "The level of verbosity to use. (default: #{self['verbosity']})")
61
+ end
62
+
63
+ def infer_mime_type(filename)
64
+ MIME::Types.type_for(filename).first.content_type
65
+ end
66
+
67
+ # FIXME: too many lines
68
+ def read_configuration_file(filename)
69
+ raise ArgumentError, 'Must pass a valid filename' if filename.nil?
70
+
71
+ mime_type = infer_mime_type(filename)
72
+ parsed = nil
73
+
74
+ data = File.open(filename, 'rb', &:read)
75
+
76
+ case mime_type
77
+ when /ya?ml/
78
+ require 'yaml'
79
+ parsed = YAML.safe_load(data, [Regexp])
80
+ when /json/
81
+ require 'json'
82
+ parsed = JSON.parse(data)
83
+ end
84
+
85
+ parsed.to_h
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,34 @@
1
+ require 'ostruct'
2
+
3
+ require 'nessana/vulnerability'
4
+
5
+ module Nessana
6
+ class Filter < ::Hash
7
+ def initialize(hash)
8
+ super
9
+
10
+ fixed_hash = hash.map do |key, value|
11
+ [key.to_sym, value]
12
+ end.to_h
13
+
14
+ merge!(fixed_hash)
15
+ end
16
+
17
+ def applies_to?(vulnerability)
18
+ each do |key, value|
19
+ method = key.to_sym
20
+
21
+ return false unless vulnerability.respond_to?(method)
22
+
23
+ case value
24
+ when Regexp
25
+ return true if vulnerability.send(method).to_s =~ value
26
+ else
27
+ return true if vulnerability.send(method).to_s == value
28
+ end
29
+ end
30
+
31
+ false
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Nessana
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,77 @@
1
+ module Nessana
2
+ class Vulnerability
3
+ attr_reader :plugin_id
4
+ attr_reader :cve, :cvss, :risk
5
+ attr_reader :name, :synopsis, :description, :solution
6
+ attr_reader :see_also, :plugin_output
7
+ attr_accessor :detections
8
+
9
+ def initialize(plugin_id, cve, cvss, risk, name, synopsis, description, solution, see_also, plugin_output)
10
+ @plugin_id = plugin_id
11
+ @cve = cve
12
+ @cvss = cvss
13
+ @risk = risk
14
+ @name = name
15
+ @synopsis = reformat_multiline(synopsis)
16
+ @description = reformat_multiline(description)
17
+ @solution = solution
18
+ @see_also = see_also
19
+ @plugin_output = plugin_output
20
+ end
21
+
22
+ def add_detection(detection)
23
+ @detections ||= []
24
+ @detections.push(detection)
25
+ end
26
+
27
+ def to_s
28
+ [title_line, @synopsis, cve_s, cvss_s, description_s, solution_s, see_also_s].join("\n\n")
29
+ end
30
+
31
+ def matches?(filters = [])
32
+ filters.each do |filter|
33
+ return true if filter.applies_to?(self)
34
+ end
35
+
36
+ false
37
+ end
38
+
39
+ def title_line
40
+ "[Nessus #{@plugin_id || "???"}] #{@name || "Unknown Vulnerability"} (#{@risk || "Unknown Risk"})"
41
+ end
42
+
43
+ def short_description
44
+ "#{title_line}
45
+ #{@synopsis}"
46
+ end
47
+
48
+ protected
49
+
50
+ def cve_s
51
+ @cve ? "CVE: " + @cve : "No CVE."
52
+ end
53
+
54
+ def cvss_s
55
+ @cvss ? "CVSS: " + @cvss : "No CVSS."
56
+ end
57
+
58
+ def description_s
59
+ "DESCRIPTION:\n#{@description}"
60
+ end
61
+
62
+ def solution_s
63
+ "SOLUTION:\n#{@solution}"
64
+ end
65
+
66
+ def see_also_s
67
+ "SEE ALSO:\n#{@see_also}"
68
+ end
69
+
70
+ # TODO Replace with something not O(2 n)? [n = line count]
71
+ def reformat_multiline(multiline)
72
+ multiline.split("\n").map do |line|
73
+ line.length == 0 ? "\n\n" : line
74
+ end.join(' ').gsub(/^ /,'').gsub(/ {3,}/,' ')
75
+ end
76
+ end
77
+ end
metadata ADDED
@@ -0,0 +1,252 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nessana
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kristofer Rye <kristofer.rye@gmail.com>
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: asana
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: fastcsv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.6
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.6
41
+ - !ruby/object:Gem::Dependency
42
+ name: mime-types
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: tty-spinner
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: codecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.14
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.14
83
+ - !ruby/object:Gem::Dependency
84
+ name: coveralls
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.22
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.22
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.14'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.14'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.7'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.7'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.11.3
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.11.3
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.7'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.7'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.57'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.57'
167
+ - !ruby/object:Gem::Dependency
168
+ name: ruby-prof
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 0.17.0
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 0.17.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: ruby-prof-flamegraph
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 0.3.0
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 0.3.0
195
+ - !ruby/object:Gem::Dependency
196
+ name: simplecov
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 0.16.1
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 0.16.1
209
+ description: 'A Nessus dump parser and differ which can create Asana tasks.
210
+
211
+ '
212
+ email:
213
+ executables:
214
+ - nessana
215
+ extensions: []
216
+ extra_rdoc_files: []
217
+ files:
218
+ - README.md
219
+ - bin/nessana
220
+ - bin/nessana_old
221
+ - lib/nessana.rb
222
+ - lib/nessana/detection.rb
223
+ - lib/nessana/dump.rb
224
+ - lib/nessana/executor.rb
225
+ - lib/nessana/executor/execution_configuration.rb
226
+ - lib/nessana/filter.rb
227
+ - lib/nessana/version.rb
228
+ - lib/nessana/vulnerability.rb
229
+ homepage: https://github.com/rye/nessana
230
+ licenses:
231
+ - MIT
232
+ metadata: {}
233
+ post_install_message:
234
+ rdoc_options: []
235
+ require_paths:
236
+ - lib
237
+ required_ruby_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - "~>"
240
+ - !ruby/object:Gem::Version
241
+ version: '2.4'
242
+ required_rubygems_version: !ruby/object:Gem::Requirement
243
+ requirements:
244
+ - - ">="
245
+ - !ruby/object:Gem::Version
246
+ version: '0'
247
+ requirements: []
248
+ rubygems_version: 3.0.2
249
+ signing_key:
250
+ specification_version: 4
251
+ summary: A Nessus dump parser and Asana task creator
252
+ test_files: []