nexpose_ticketing 1.0.2 → 1.2.1
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 +4 -4
- data/README.md +22 -0
- data/bin/nexpose_ticketing +45 -0
- data/lib/nexpose_ticketing/config/jira.config +5 -1
- data/lib/nexpose_ticketing/config/remedy.config +4 -1
- data/lib/nexpose_ticketing/config/servicedesk.config +4 -1
- data/lib/nexpose_ticketing/config/servicenow.config +4 -1
- data/lib/nexpose_ticketing/config/ticket_service.config +13 -5
- data/lib/nexpose_ticketing/helpers/base_helper.rb +43 -0
- data/lib/nexpose_ticketing/helpers/jira_helper.rb +149 -97
- data/lib/nexpose_ticketing/helpers/remedy_helper.rb +37 -52
- data/lib/nexpose_ticketing/helpers/servicedesk_helper.rb +30 -49
- data/lib/nexpose_ticketing/helpers/servicenow_helper.rb +39 -56
- data/lib/nexpose_ticketing/modes/base_mode.rb +199 -0
- data/lib/nexpose_ticketing/modes/default_mode.rb +50 -0
- data/lib/nexpose_ticketing/modes/ip_mode.rb +52 -0
- data/lib/nexpose_ticketing/modes/vulnerability_mode.rb +50 -0
- data/lib/nexpose_ticketing/nx_logger.rb +44 -33
- data/lib/nexpose_ticketing/queries.rb +30 -27
- data/lib/nexpose_ticketing/report_helper.rb +1 -0
- data/lib/nexpose_ticketing/ticket_metrics.rb +39 -0
- data/lib/nexpose_ticketing/ticket_repository.rb +65 -206
- data/lib/nexpose_ticketing/ticket_service.rb +470 -441
- data/lib/nexpose_ticketing/version.rb +1 -1
- metadata +15 -16
- data/bin/nexpose_jira +0 -27
- data/bin/nexpose_remedy +0 -27
- data/bin/nexpose_servicedesk +0 -27
- data/bin/nexpose_servicenow +0 -27
- data/lib/nexpose_ticketing/common_helper.rb +0 -344
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'nexpose_ticketing/nx_logger'
|
2
|
+
|
3
|
+
class BaseMode
|
4
|
+
|
5
|
+
# Initializes the mode
|
6
|
+
def initialize(options)
|
7
|
+
@options = options
|
8
|
+
@log = NexposeTicketing::NxLogger.instance
|
9
|
+
end
|
10
|
+
|
11
|
+
# True if this mode supports ticket updates
|
12
|
+
def updates_supported?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the fields used to identify individual tickets
|
17
|
+
def get_matching_fields
|
18
|
+
['']
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the ticket's title
|
22
|
+
def get_title(row)
|
23
|
+
"#{nil} => #{nil}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generates a unique identifier for a ticket
|
27
|
+
def get_nxid(nexpose_id, row)
|
28
|
+
"#{nil}c#{nil}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the base ticket description object
|
32
|
+
def get_description(nexpose_id, row)
|
33
|
+
description
|
34
|
+
end
|
35
|
+
|
36
|
+
# Updates the ticket description based on row data
|
37
|
+
def update_description(description, row)
|
38
|
+
description
|
39
|
+
end
|
40
|
+
|
41
|
+
# Converts the ticket description object into a formatted string
|
42
|
+
def print_description(description)
|
43
|
+
''
|
44
|
+
end
|
45
|
+
|
46
|
+
# Cuts the title down to size specified in config, if necessary
|
47
|
+
def truncate_title(title)
|
48
|
+
return title if title.length <= @options[:max_title_length]
|
49
|
+
"#{title[0, @options[:max_title_length]-3]}..."
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the suffix used for query method names
|
53
|
+
def get_query_suffix
|
54
|
+
'_by_ip'
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_queries
|
58
|
+
file_name = "#{self.class.to_s.downcase}_queries.rb"
|
59
|
+
file_path = File.join(File.dirname(__FILE__), "../queries/#{file_name}")
|
60
|
+
@queries = []
|
61
|
+
|
62
|
+
@queries << YAML.load_file(file_path)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Generates a final description string based on a description hash.
|
66
|
+
#
|
67
|
+
# - +ticket_desc+ - The ticket description to be formatted.
|
68
|
+
# - +nxid+ - The NXID to be appended to the ticket.
|
69
|
+
#
|
70
|
+
# * *Returns* :
|
71
|
+
# - String containing ticket description text.
|
72
|
+
#
|
73
|
+
def finalize_description(ticket_desc, nxid)
|
74
|
+
nxid_line = "\n\n\n#{nxid}"
|
75
|
+
|
76
|
+
#If the ticket is too long, truncate it to fit the NXID
|
77
|
+
max_len = @options[:max_ticket_length]
|
78
|
+
if max_len > 0 and (ticket_desc + nxid_line).length > max_len
|
79
|
+
#Leave space for newline characters, nxid and ellipsis (...)
|
80
|
+
ticket_desc = ticket_desc[0...max_len - (nxid_line.length+5)]
|
81
|
+
ticket_desc << "\n...\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
"#{ticket_desc}#{nxid_line}"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Formats the row data to be inserted into a 'D' or 'I' mode ticket description.
|
88
|
+
#
|
89
|
+
# - +row+ - CSV row containing vulnerability data.
|
90
|
+
#
|
91
|
+
# * *Returns* :
|
92
|
+
# - String formatted with vulnerability data.
|
93
|
+
#
|
94
|
+
def get_vuln_info(row)
|
95
|
+
ticket = get_header(row)
|
96
|
+
ticket << get_discovery_info(row)
|
97
|
+
ticket << get_references(row)
|
98
|
+
ticket << "\n#{get_solutions(row)}"
|
99
|
+
ticket.gsub("\n", "\n ")
|
100
|
+
end
|
101
|
+
|
102
|
+
# Generates the vulnerability header from the row data.
|
103
|
+
#
|
104
|
+
# - +row+ - CSV row containing vulnerability data.
|
105
|
+
#
|
106
|
+
# * *Returns* :
|
107
|
+
# - String formatted with vulnerability data.
|
108
|
+
#
|
109
|
+
def get_header(row)
|
110
|
+
ticket = "\n=============================="
|
111
|
+
ticket << "\nVulnerability ID: #{row['vulnerability_id']}"
|
112
|
+
ticket << "\nNexpose ID: #{row['vuln_nexpose_id']}"
|
113
|
+
ticket << "\nCVSS Score: #{row['cvss_score']}"
|
114
|
+
ticket << "\n=============================="
|
115
|
+
end
|
116
|
+
|
117
|
+
# Generates a short summary for a vulnerability.
|
118
|
+
#
|
119
|
+
# - +row+ - CSV row containing vulnerability data.
|
120
|
+
#
|
121
|
+
# * *Returns* :
|
122
|
+
# - String containing a short summary of the vulnerability.
|
123
|
+
#
|
124
|
+
def get_short_summary(row)
|
125
|
+
summary = row['solutions'].to_s
|
126
|
+
delimiter = summary.index('|')
|
127
|
+
return summary[summary.index(':')+1...delimiter].strip if delimiter
|
128
|
+
summary.length <= 100 ? summary : summary[0...100]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Formats the solutions for a vulnerability in a format suitable to be inserted into a ticket.
|
132
|
+
#
|
133
|
+
# - +row+ - CSV row containing vulnerability data.
|
134
|
+
#
|
135
|
+
# * *Returns* :
|
136
|
+
# - String formatted with solution information.
|
137
|
+
#
|
138
|
+
def get_solutions(row)
|
139
|
+
row['solutions'].to_s.gsub('|', "\n").gsub('~', "\n--\n")
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_discovery_info(row)
|
143
|
+
return '' if row['first_discovered'].to_s == ""
|
144
|
+
info = "\nFirst Seen: #{row['first_discovered']}\n"
|
145
|
+
info << "Last Seen: #{row['most_recently_discovered']}\n"
|
146
|
+
info
|
147
|
+
end
|
148
|
+
|
149
|
+
# Formats the references for a vulnerability in a format suitable to be inserted into a ticket.
|
150
|
+
#
|
151
|
+
# - +row+ - CSV row containing vulnerability data.
|
152
|
+
#
|
153
|
+
# * *Returns* :
|
154
|
+
# - String formatted with source and reference.
|
155
|
+
#
|
156
|
+
def get_references(row)
|
157
|
+
num_refs = @options[:max_num_refs]
|
158
|
+
return '' if row['references'].nil? || num_refs == 0
|
159
|
+
|
160
|
+
refs = row['references'].split(', ')[0..num_refs]
|
161
|
+
refs[num_refs] = '...' if refs.count > num_refs
|
162
|
+
"\nSources:\n#{refs.map { |r| " - #{r}" }.join("\n")}\n"
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
# Returns the assets for a vulnerability in a format suitable to be inserted into a ticket.
|
167
|
+
#
|
168
|
+
# - +row+ - CSV row containing vulnerability data.
|
169
|
+
#
|
170
|
+
# * *Returns* :
|
171
|
+
# - String formatted with affected assets.
|
172
|
+
#
|
173
|
+
def get_assets(row)
|
174
|
+
assets = "\n#{row['comparison'] || 'Affected' } Assets\n"
|
175
|
+
|
176
|
+
row['assets'].to_s.split('~').each do |a|
|
177
|
+
asset = a.split('|')
|
178
|
+
assets << " - #{asset[1]} #{"\t(#{asset[2]})" if !asset[2].empty?}\n"
|
179
|
+
end
|
180
|
+
assets
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns the relevant row values for printing.
|
184
|
+
#
|
185
|
+
# - +fields+ - The fields which are relevant to the ticket.
|
186
|
+
# - +row+ - CSV row containing vulnerability data.
|
187
|
+
#
|
188
|
+
# * *Returns* :
|
189
|
+
# - String formatted with relevant fields.
|
190
|
+
#
|
191
|
+
def get_field_info(fields, row)
|
192
|
+
fields.map { |x| "#{x.gsub("_", " ")}: #{row[x]}" }.join(", ")
|
193
|
+
end
|
194
|
+
|
195
|
+
# Catch-all method when a unknown method is called
|
196
|
+
def method_missing(name, *args)
|
197
|
+
@log.log_message("Method #{name} not implemented for #{@options[:ticket_mode]} mode.")
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative './base_mode.rb'
|
2
|
+
|
3
|
+
class DefaultMode < BaseMode
|
4
|
+
|
5
|
+
# Initializes the mode
|
6
|
+
def initialize(options)
|
7
|
+
super(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
# True if this mode supports ticket updates
|
11
|
+
def updates_supported?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the fields used to identify individual tickets
|
16
|
+
def get_matching_fields
|
17
|
+
['ip_address', 'vulnerability_id']
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the ticket's title
|
21
|
+
def get_title(row)
|
22
|
+
truncate_title "#{row['ip_address']} => #{get_short_summary(row)}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generates a unique identifier for a ticket
|
26
|
+
def get_nxid(nexpose_id, row)
|
27
|
+
"#{nexpose_id}d#{row['asset_id']}d#{row['vulnerability_id']}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the base ticket description object
|
31
|
+
def get_description(nexpose_id, row)
|
32
|
+
description = { nxid: "NXID: #{get_nxid(nexpose_id, row)}" }
|
33
|
+
fields = ['header', 'references', 'solutions']
|
34
|
+
fields.each { |f| description[f.intern] = self.send("get_#{f}", row) }
|
35
|
+
description[:header] << get_discovery_info(row)
|
36
|
+
description
|
37
|
+
end
|
38
|
+
|
39
|
+
# Updates the ticket description based on row data
|
40
|
+
def update_description(description, row)
|
41
|
+
description
|
42
|
+
end
|
43
|
+
|
44
|
+
# Converts the ticket description object into a formatted string
|
45
|
+
def print_description(description)
|
46
|
+
fields = [:header, :references, :solutions].map { |f| description[f] }
|
47
|
+
finalize_description(fields.join("\n"),
|
48
|
+
description[:nxid])
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative './base_mode.rb'
|
2
|
+
|
3
|
+
class IPMode < BaseMode
|
4
|
+
|
5
|
+
# Initializes the mode
|
6
|
+
def initialize(options)
|
7
|
+
super(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the fields used to identify individual tickets
|
11
|
+
def get_matching_fields
|
12
|
+
['ip_address']
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the ticket's title
|
16
|
+
def get_title(row)
|
17
|
+
truncate_title "#{row['ip_address']} => Vulnerabilities"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Generates a unique identifier for a ticket
|
21
|
+
def get_nxid(nexpose_id, row)
|
22
|
+
"#{nexpose_id}i#{row['ip_address']}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the base ticket description object
|
26
|
+
def get_description(nexpose_id, row)
|
27
|
+
description = { nxid: "NXID: #{get_nxid(nexpose_id, row)}" }
|
28
|
+
status = row['comparison']
|
29
|
+
description[:ticket_status] = status
|
30
|
+
header = "++ #{status} Vulnerabilities ++\n" if !status.nil?
|
31
|
+
description[:vulnerabilities] = [ header.to_s + get_vuln_info(row) ]
|
32
|
+
description
|
33
|
+
end
|
34
|
+
|
35
|
+
# Updates the ticket description based on row data
|
36
|
+
def update_description(description, row)
|
37
|
+
header = ""
|
38
|
+
if description[:ticket_status] != row['comparison']
|
39
|
+
header = "++ #{row['comparison']} Vulnerabilities ++\n"
|
40
|
+
description[:ticket_status] = row['comparison']
|
41
|
+
end
|
42
|
+
|
43
|
+
description[:vulnerabilities] << "#{header}#{get_vuln_info(row)}"
|
44
|
+
description
|
45
|
+
end
|
46
|
+
|
47
|
+
# Converts the ticket description object into a formatted string
|
48
|
+
def print_description(description)
|
49
|
+
finalize_description(description[:vulnerabilities].join("\n"),
|
50
|
+
description[:nxid])
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative './base_mode.rb'
|
2
|
+
|
3
|
+
class VulnerabilityMode < BaseMode
|
4
|
+
|
5
|
+
# Initializes the mode
|
6
|
+
def initialize(options)
|
7
|
+
super(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the fields used to identify individual tickets
|
11
|
+
def get_matching_fields
|
12
|
+
['vulnerability_id']
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the ticket's title
|
16
|
+
def get_title(row)
|
17
|
+
truncate_title "Vulnerability: #{row['title']}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Generates a unique identifier for a ticket
|
21
|
+
def get_nxid(nexpose_id, row)
|
22
|
+
"#{nexpose_id}v#{row['vulnerability_id']}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the suffix used for query method names
|
26
|
+
def get_query_suffix
|
27
|
+
'_by_vuln_id'
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the base ticket description object
|
31
|
+
def get_description(nexpose_id, row)
|
32
|
+
description = { nxid: "NXID: #{get_nxid(nexpose_id, row)}" }
|
33
|
+
fields = ['header', 'references', 'solutions', 'assets']
|
34
|
+
fields.each { |f| description[f.intern] = self.send("get_#{f}", row) }
|
35
|
+
description
|
36
|
+
end
|
37
|
+
|
38
|
+
# Updates the ticket description based on row data
|
39
|
+
def update_description(description, row)
|
40
|
+
description[:assets] += "\n#{get_assets(row)}"
|
41
|
+
description
|
42
|
+
end
|
43
|
+
|
44
|
+
# Converts the ticket description object into a formatted string
|
45
|
+
def print_description(description)
|
46
|
+
fields = [:header, :assets, :references, :solutions]
|
47
|
+
finalize_description(fields.map { |f| description[f] }.join("\n"),
|
48
|
+
description[:nxid])
|
49
|
+
end
|
50
|
+
end
|
@@ -6,27 +6,23 @@ require 'singleton'
|
|
6
6
|
module NexposeTicketing
|
7
7
|
class NxLogger
|
8
8
|
include Singleton
|
9
|
-
attr_accessor :options, :statistic_key, :product, :logger_file
|
10
9
|
LOG_PATH = "./logs/rapid7_%s.log"
|
11
10
|
KEY_FORMAT = "external.integration.%s"
|
12
11
|
PRODUCT_FORMAT = "%s_%s"
|
13
12
|
|
14
13
|
DEFAULT_LOG = 'integration'
|
15
|
-
PRODUCT_RANGE =
|
14
|
+
PRODUCT_RANGE = 4..30
|
16
15
|
KEY_RANGE = 3..15
|
17
16
|
|
18
17
|
ENDPOINT = '/data/external/statistic/'
|
19
18
|
|
20
19
|
def initialize()
|
21
|
-
|
20
|
+
create_calls
|
21
|
+
@logger_file = get_log_path @product
|
22
22
|
setup_logging(true, 'info')
|
23
23
|
end
|
24
24
|
|
25
25
|
def setup_statistics_collection(vendor, product_name, gem_version)
|
26
|
-
#Remove illegal characters
|
27
|
-
vendor.to_s.gsub!('-', '_')
|
28
|
-
product_name.to_s.gsub!('-', '_')
|
29
|
-
|
30
26
|
begin
|
31
27
|
@statistic_key = get_statistic_key vendor
|
32
28
|
@product = get_product product_name, gem_version
|
@@ -35,13 +31,14 @@ module NexposeTicketing
|
|
35
31
|
end
|
36
32
|
end
|
37
33
|
|
38
|
-
def setup_logging(enabled, log_level = 'info')
|
39
|
-
|
40
|
-
log_message('Logging disabled.')
|
41
|
-
return
|
42
|
-
end
|
34
|
+
def setup_logging(enabled, log_level = 'info', stdout=false)
|
35
|
+
@stdout = stdout
|
43
36
|
|
44
|
-
|
37
|
+
log_message('Logging disabled.') unless enabled || @log.nil?
|
38
|
+
@enabled = enabled
|
39
|
+
return unless @enabled
|
40
|
+
|
41
|
+
@logger_file = get_log_path @product
|
45
42
|
|
46
43
|
require 'logger'
|
47
44
|
directory = File.dirname(@logger_file)
|
@@ -58,24 +55,19 @@ module NexposeTicketing
|
|
58
55
|
log_message("Logging enabled at level <#{log_level}>")
|
59
56
|
end
|
60
57
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# Logs an error message
|
72
|
-
def log_error_message(message)
|
73
|
-
@log.error(message) unless @log.nil?
|
58
|
+
def create_calls
|
59
|
+
levels = [:info, :debug, :error, :warn]
|
60
|
+
levels.each do |level|
|
61
|
+
method_name =
|
62
|
+
define_singleton_method("log_#{level.to_s}_message") do |message|
|
63
|
+
puts message if @stdout
|
64
|
+
@log.send(level, message) unless !@enabled || @log.nil?
|
65
|
+
end
|
66
|
+
end
|
74
67
|
end
|
75
68
|
|
76
|
-
|
77
|
-
|
78
|
-
@log.warn(message) unless @log.nil?
|
69
|
+
def log_message(message)
|
70
|
+
log_info_message message
|
79
71
|
end
|
80
72
|
|
81
73
|
def log_stat_message(message)
|
@@ -92,13 +84,28 @@ module NexposeTicketing
|
|
92
84
|
return nil
|
93
85
|
end
|
94
86
|
|
87
|
+
vendor.gsub!('-', '_')
|
88
|
+
vendor.slice! vendor.rindex('_') until vendor.count('_') <= 1
|
89
|
+
|
90
|
+
vendor.delete! "^A-Za-z0-9\_"
|
91
|
+
|
95
92
|
KEY_FORMAT % vendor[0...KEY_RANGE.max].downcase
|
96
93
|
end
|
97
94
|
|
98
95
|
def get_product(product, version)
|
99
|
-
return nil if (product.nil? ||
|
96
|
+
return nil if ((product.nil? || product.empty?) ||
|
97
|
+
(version.nil? || version.empty?))
|
98
|
+
|
99
|
+
product.gsub!('-', '_')
|
100
|
+
product.slice! product.rindex('_') until product.count('_') <= 1
|
101
|
+
|
102
|
+
product.delete! "^A-Za-z0-9\_"
|
103
|
+
version.delete! "^A-Za-z0-9\.\-"
|
104
|
+
|
100
105
|
product = (PRODUCT_FORMAT % [product, version])[0...PRODUCT_RANGE.max]
|
101
106
|
|
107
|
+
product.slice! product.rindex(/[A-Z0-9]/i)+1..-1
|
108
|
+
|
102
109
|
if product.length < PRODUCT_RANGE.min
|
103
110
|
log_stat_message("Product length below minimum <#{PRODUCT_RANGE.min}>.")
|
104
111
|
return nil
|
@@ -107,9 +114,12 @@ module NexposeTicketing
|
|
107
114
|
end
|
108
115
|
|
109
116
|
def generate_payload(statistic_value='')
|
110
|
-
|
111
|
-
|
112
|
-
|
117
|
+
product_name, separator, version = @product.to_s.rpartition('_')
|
118
|
+
payload_value = {'version' => version}.to_json
|
119
|
+
|
120
|
+
payload = {'statistic-key' => @statistic_key.to_s,
|
121
|
+
'statistic-value' => payload_value,
|
122
|
+
'product' => product_name}
|
113
123
|
JSON.generate(payload)
|
114
124
|
end
|
115
125
|
|
@@ -126,6 +136,7 @@ module NexposeTicketing
|
|
126
136
|
log_stat_message "Received code #{response.code} from Nexpose console."
|
127
137
|
log_stat_message "Received message #{response.msg} from Nexpose console."
|
128
138
|
log_stat_message 'Finished sending statistics data to Nexpose.'
|
139
|
+
|
129
140
|
response.code
|
130
141
|
end
|
131
142
|
|
@@ -4,9 +4,8 @@ module NexposeTicketing
|
|
4
4
|
# for Nexpose.
|
5
5
|
# Copyright:: Copyright (c) 2014 Rapid7, LLC.
|
6
6
|
module Queries
|
7
|
-
|
8
7
|
# Formats SQL query for riskscore based on user config options.
|
9
|
-
|
8
|
+
#
|
10
9
|
# * *Args* :
|
11
10
|
# - +riskScore+ - riskscore for assets to match in results of query.
|
12
11
|
#
|
@@ -63,8 +62,9 @@ module NexposeTicketing
|
|
63
62
|
# -Returns |asset_id| |ip_address| |current_scan| |vulnerability_id||solution_id| |nexpose_id|
|
64
63
|
# |url| |summary| |fix|
|
65
64
|
#
|
66
|
-
def self.
|
67
|
-
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan,
|
65
|
+
def self.all_new_vulns_by_ip(options = {})
|
66
|
+
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan,
|
67
|
+
subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id,
|
68
68
|
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
69
69
|
'|Nexpose ID: ' || ds.nexpose_id ||
|
70
70
|
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
@@ -90,7 +90,7 @@ module NexposeTicketing
|
|
90
90
|
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
91
91
|
JOIN fact_asset fa ON fa.asset_id = da.asset_id
|
92
92
|
#{createRiskString( options[:riskScore])}
|
93
|
-
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
93
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id, dv.nexpose_id,
|
94
94
|
fa.riskscore, dv.cvss_score, fasva.first_discovered, fasva.most_recently_discovered
|
95
95
|
ORDER BY da.ip_address, subs.vulnerability_id"
|
96
96
|
end
|
@@ -102,7 +102,7 @@ module NexposeTicketing
|
|
102
102
|
# |url| |summary| |fix|
|
103
103
|
#
|
104
104
|
def self.all_new_vulns_by_vuln_id(options = {})
|
105
|
-
"SELECT DISTINCT on (subs.vulnerability_id) subs.vulnerability_id, dv.title, MAX(dv.cvss_score) as cvss_score,
|
105
|
+
"SELECT DISTINCT on (subs.vulnerability_id) subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id, dv.title, MAX(dv.cvss_score) as cvss_score,
|
106
106
|
string_agg(DISTINCT subs.asset_id ||
|
107
107
|
'|' || da.ip_address ||
|
108
108
|
'|' || coalesce(da.host_name, '') ||
|
@@ -132,7 +132,7 @@ module NexposeTicketing
|
|
132
132
|
JOIN fact_asset_vulnerability_age fasva ON subs.vulnerability_id = fasva.vulnerability_id AND subs.asset_id = fasva.asset_id
|
133
133
|
#{createRiskString(options[:riskScore])}
|
134
134
|
|
135
|
-
GROUP BY subs.vulnerability_id, dv.title
|
135
|
+
GROUP BY subs.vulnerability_id, dv.title, dv.nexpose_id
|
136
136
|
ORDER BY subs.vulnerability_id"
|
137
137
|
end
|
138
138
|
|
@@ -145,8 +145,9 @@ module NexposeTicketing
|
|
145
145
|
# - Returns |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
146
146
|
# |url| |summary| |fix|
|
147
147
|
#
|
148
|
-
def self.
|
149
|
-
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan,
|
148
|
+
def self.new_vulns_since_scan_by_ip(options = {})
|
149
|
+
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan,
|
150
|
+
subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id,
|
150
151
|
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
151
152
|
'|Nexpose ID: ' || ds.nexpose_id ||
|
152
153
|
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
@@ -173,7 +174,7 @@ module NexposeTicketing
|
|
173
174
|
AND subs.current_scan > #{options[:scan_id]}
|
174
175
|
JOIN fact_asset fa ON fa.asset_id = da.asset_id
|
175
176
|
#{createRiskString(options[:riskScore])}
|
176
|
-
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
177
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id, dv.nexpose_id,
|
177
178
|
fa.riskscore, dv.cvss_score, fasva.first_discovered, fasva.most_recently_discovered
|
178
179
|
ORDER BY da.ip_address, subs.vulnerability_id"
|
179
180
|
end
|
@@ -188,8 +189,9 @@ module NexposeTicketing
|
|
188
189
|
# - Returns |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
189
190
|
# |url| |summary| |fix|
|
190
191
|
#
|
191
|
-
def self.
|
192
|
-
"SELECT DISTINCT on (subs.vulnerability_id) subs.vulnerability_id, dv.
|
192
|
+
def self.new_vulns_since_scan_by_vuln_id(options = {})
|
193
|
+
"SELECT DISTINCT on (subs.vulnerability_id) subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id,
|
194
|
+
dv.title, MAX(dv.cvss_score) as cvss_score,
|
193
195
|
string_agg(DISTINCT subs.asset_id ||
|
194
196
|
'|' || da.ip_address ||
|
195
197
|
'|' || coalesce(da.host_name, '') ||
|
@@ -219,7 +221,7 @@ module NexposeTicketing
|
|
219
221
|
AND subs.current_scan > #{options[:scan_id]}
|
220
222
|
JOIN fact_asset fa ON fa.asset_id = da.asset_id
|
221
223
|
#{createRiskString(options[:riskScore])}
|
222
|
-
GROUP BY subs.vulnerability_id, dv.title
|
224
|
+
GROUP BY subs.vulnerability_id, dv.title, dv.nexpose_id
|
223
225
|
ORDER BY vulnerability_id"
|
224
226
|
end
|
225
227
|
|
@@ -266,8 +268,9 @@ module NexposeTicketing
|
|
266
268
|
# - Returns |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
267
269
|
# |url| |summary| |fix| |comparison|
|
268
270
|
#
|
269
|
-
def self.
|
270
|
-
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan,
|
271
|
+
def self.all_vulns_since_scan_by_ip(options = {})
|
272
|
+
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan,
|
273
|
+
subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id,
|
271
274
|
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
272
275
|
'|Nexpose ID: ' || ds.nexpose_id ||
|
273
276
|
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
@@ -293,12 +296,11 @@ module NexposeTicketing
|
|
293
296
|
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
294
297
|
#{createRiskString(options[:riskScore])}
|
295
298
|
AND subs.current_scan > #{options[:scan_id]}
|
296
|
-
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
299
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id, dv.nexpose_id,
|
297
300
|
fa.riskscore, dv.cvss_score, subs.comparison
|
298
|
-
|
299
301
|
UNION
|
300
|
-
|
301
|
-
|
302
|
+
SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan,
|
303
|
+
subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id,
|
302
304
|
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
303
305
|
'|Nexpose ID: ' || ds.nexpose_id ||
|
304
306
|
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
@@ -327,9 +329,8 @@ module NexposeTicketing
|
|
327
329
|
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
328
330
|
#{createRiskString(options[:riskScore])}
|
329
331
|
AND subs.current_scan > #{options[:scan_id]}
|
330
|
-
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
332
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id, dv.nexpose_id,
|
331
333
|
fa.riskscore, dv.cvss_score, fasva.first_discovered, fasva.most_recently_discovered, subs.comparison
|
332
|
-
|
333
334
|
ORDER BY ip_address, comparison"
|
334
335
|
end
|
335
336
|
|
@@ -343,8 +344,9 @@ module NexposeTicketing
|
|
343
344
|
# - Returns |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
344
345
|
# |url| |summary| |fix| |comparison|
|
345
346
|
#
|
346
|
-
def self.
|
347
|
-
"SELECT DISTINCT on (subs.vulnerability_id, subs.comparison) subs.vulnerability_id, dv.
|
347
|
+
def self.all_vulns_since_scan_by_vuln_id(options = {})
|
348
|
+
"SELECT DISTINCT on (subs.vulnerability_id, subs.comparison) subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id,
|
349
|
+
dv.title, MAX(dv.cvss_score) as cvss_score,
|
348
350
|
string_agg(DISTINCT subs.asset_id ||
|
349
351
|
'|' || da.ip_address ||
|
350
352
|
'|' || coalesce(da.host_name, '') ||
|
@@ -374,11 +376,12 @@ module NexposeTicketing
|
|
374
376
|
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
375
377
|
#{createRiskString(options[:riskScore])}
|
376
378
|
AND subs.current_scan > #{options[:scan_id]}
|
377
|
-
GROUP BY subs.vulnerability_id, dv.title, subs.comparison
|
379
|
+
GROUP BY subs.vulnerability_id, dv.title, dv.nexpose_id, subs.comparison
|
378
380
|
|
379
381
|
UNION
|
380
382
|
|
381
|
-
SELECT DISTINCT on (subs.vulnerability_id, subs.comparison) subs.vulnerability_id, dv.
|
383
|
+
SELECT DISTINCT on (subs.vulnerability_id, subs.comparison) subs.vulnerability_id, dv.nexpose_id as vuln_nexpose_id,
|
384
|
+
dv.title, MAX(dv.cvss_score) as cvss_score,
|
382
385
|
string_agg(DISTINCT subs.asset_id ||
|
383
386
|
'|' || da.ip_address ||
|
384
387
|
'|' || coalesce(da.host_name, '') ||
|
@@ -410,7 +413,7 @@ module NexposeTicketing
|
|
410
413
|
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
411
414
|
#{createRiskString(options[:riskScore])}
|
412
415
|
AND subs.current_scan > #{options[:scan_id]}
|
413
|
-
GROUP BY subs.vulnerability_id, dv.title, subs.comparison
|
416
|
+
GROUP BY subs.vulnerability_id, dv.title, dv.nexpose_id, subs.comparison
|
414
417
|
|
415
418
|
ORDER BY vulnerability_id, comparison"
|
416
419
|
end
|
@@ -462,7 +465,7 @@ module NexposeTicketing
|
|
462
465
|
# - Returns |asset_id| |ip_address| |current_scan| |vulnerability_id| |comparison|
|
463
466
|
#
|
464
467
|
def self.old_tickets_by_vuln_id(options = {})
|
465
|
-
"SELECT subs.vulnerability_id, subs.asset_id, subs.ip_address, subs.current_scan, subs.comparison
|
468
|
+
"SELECT DISTINCT on(subs.vulnerability_id) subs.vulnerability_id, subs.asset_id, subs.ip_address, subs.current_scan, subs.comparison
|
466
469
|
FROM (
|
467
470
|
SELECT fasv.asset_id, s.ip_address, fasv.vulnerability_id, s.current_scan, baselineComparison(fasv.scan_id, s.current_scan) as comparison, fa.riskscore
|
468
471
|
FROM fact_asset_scan_vulnerability_finding fasv
|