dradis-qualys 4.0.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -53
- data/CHANGELOG.template +12 -0
- data/dradis-qualys.gemspec +1 -1
- data/lib/dradis/plugins/qualys/asset/importer.rb +112 -0
- data/lib/dradis/plugins/qualys/engine.rb +13 -0
- data/lib/dradis/plugins/qualys/field_processor.rb +23 -3
- data/lib/dradis/plugins/qualys/gem_version.rb +1 -1
- data/lib/dradis/plugins/qualys/vuln/importer.rb +103 -0
- data/lib/dradis/plugins/qualys/was/importer.rb +109 -0
- data/lib/dradis/plugins/qualys.rb +4 -1
- data/lib/dradis-qualys.rb +4 -0
- data/lib/qualys/asset/evidence.rb +74 -0
- data/lib/qualys/asset/vulnerability.rb +87 -0
- data/lib/qualys/element.rb +32 -25
- data/lib/qualys/was/qid.rb +85 -0
- data/lib/qualys/was/vulnerability.rb +68 -0
- data/lib/tasks/thorfile.rb +15 -1
- data/spec/fixtures/files/simple_asset.xml +126 -0
- data/spec/fixtures/files/simple_was.xml +134 -0
- data/spec/qualys/asset/importer_spec.rb +41 -0
- data/spec/qualys/{importer_spec.rb → vuln/importer_spec.rb} +10 -53
- data/spec/qualys/was/importer_spec.rb +41 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/spec_macros.rb +46 -0
- data/templates/asset-evidence.fields +9 -0
- data/templates/asset-evidence.sample +14 -0
- data/templates/asset-evidence.template +11 -0
- data/templates/asset-issue.fields +14 -0
- data/templates/asset-issue.sample +21 -0
- data/templates/asset-issue.template +22 -0
- data/templates/element.fields +1 -0
- data/templates/element.template +4 -0
- data/templates/was-evidence.fields +6 -0
- data/templates/was-evidence.sample +44 -0
- data/templates/was-evidence.template +11 -0
- data/templates/was-issue.fields +16 -0
- data/templates/was-issue.sample +24 -0
- data/templates/was-issue.template +28 -0
- metadata +36 -7
- data/lib/dradis/plugins/qualys/importer.rb +0 -88
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 179f7a4d51e6e9514362058cfe23587808351d96fff29ff3638c1f3020f2bcc3
|
4
|
+
data.tar.gz: e0ff0cb6075b177f802fa74f3ba0d396fb8c8ebbecf07fd390860788e2e1e506
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f06c828d8f2209711734decbafa5f2dcb0e3b1f0df3bb9b1fc86b24bcb0634c8a0892d3be4b78d3a87bfd64431cc1091f8dd9b2de1ccfb12ff4ea958a28abaac
|
7
|
+
data.tar.gz: e595872f8ad27155e0a5916eed09f043a30e19122402ded9dbdd4ff1367857da4d67bee5c336967406e78e771cca89230b9a263f8a0c8de48bd3ae4fc6116e89
|
data/CHANGELOG.md
CHANGED
@@ -1,73 +1,67 @@
|
|
1
|
-
|
1
|
+
v4.3.0 (April 2022)
|
2
|
+
- Adds Qualys Asset Scanner (ASSET) support
|
3
|
+
- Bugs fixes:
|
4
|
+
- Fixes WAS CVSS3 mapping and not importing fields to the issue
|
2
5
|
|
3
|
-
|
6
|
+
v4.2.0 (February 2022)
|
7
|
+
- Adds 'element.qualys_collection' as issue field
|
8
|
+
- Adds Qualys Web Application Scanner (WAS) support
|
4
9
|
|
5
|
-
|
10
|
+
v4.1.0 (November 2021)
|
11
|
+
- Add <dd>, <dt> support
|
12
|
+
- Remove orphaned <b> tags
|
6
13
|
|
7
|
-
|
14
|
+
v4.0.0 (July 2021)
|
15
|
+
- No changes
|
8
16
|
|
9
|
-
|
17
|
+
v3.22.0 (April 2021)
|
18
|
+
- No changes
|
10
19
|
|
11
|
-
|
20
|
+
v3.21.0 (February 2021)
|
21
|
+
- No changes
|
12
22
|
|
13
|
-
|
23
|
+
v3.20.0 (December 2020)
|
24
|
+
- No changes
|
14
25
|
|
15
|
-
|
26
|
+
v3.19.0 (September 2020)
|
27
|
+
- No changes
|
16
28
|
|
17
|
-
|
29
|
+
v3.18.0 (July 2020)
|
30
|
+
- No changes
|
18
31
|
|
19
|
-
|
32
|
+
v3.17.0 (May 2020)
|
33
|
+
- No changes
|
20
34
|
|
21
|
-
|
35
|
+
v3.16.0 (February 2020)
|
36
|
+
- No changes
|
22
37
|
|
23
|
-
|
38
|
+
v3.15.0 (November 2019)
|
39
|
+
- No changes
|
24
40
|
|
25
|
-
|
41
|
+
v3.14.0 (August 2019)
|
42
|
+
- No changes
|
26
43
|
|
27
|
-
|
44
|
+
v3.13.0 (June 2019)
|
45
|
+
- No changes
|
28
46
|
|
29
|
-
|
47
|
+
v3.12.0 (March 2019)
|
48
|
+
- No changes
|
30
49
|
|
31
|
-
|
50
|
+
v3.11.0 (November 2018)
|
51
|
+
- No changes
|
32
52
|
|
33
|
-
|
53
|
+
v3.10.0 (August 2018)
|
54
|
+
- No changes
|
34
55
|
|
35
|
-
|
56
|
+
v3.9.0 (January 2018)
|
57
|
+
- Add `os`, `hostname`, and `ip` as Node properties instead of a `Basic host info` Note
|
36
58
|
|
37
|
-
|
59
|
+
v3.8.0 (September 2017)
|
60
|
+
- No changes
|
38
61
|
|
39
|
-
|
62
|
+
v3.7.0 (July 2017)
|
63
|
+
- Better HTML entity translation (thanks @leesoh)
|
64
|
+
- Import INFOS, SERVICES, etc as Issues #7 (thanks @rachkor)
|
40
65
|
|
41
|
-
|
42
|
-
|
43
|
-
* No changes.
|
44
|
-
|
45
|
-
## Dradis Framework 3.12 (March, 2019) ##
|
46
|
-
|
47
|
-
* No changes.
|
48
|
-
|
49
|
-
## Dradis Framework 3.11 (November, 2018) ##
|
50
|
-
|
51
|
-
* No changes.
|
52
|
-
|
53
|
-
## Dradis Framework 3.10 (August, 2018) ##
|
54
|
-
|
55
|
-
* No changes.
|
56
|
-
|
57
|
-
## Dradis Framework 3.9 (January, 2018) ##
|
58
|
-
|
59
|
-
* Add `os`, `hostname`, and `ip` as Node properties
|
60
|
-
instead of a `Basic host info` Note (v3.8.1)
|
61
|
-
|
62
|
-
## Dradis Framework 3.8 (September, 2017) ##
|
63
|
-
|
64
|
-
* No changes.
|
65
|
-
|
66
|
-
## Dradis Framework 3.7 (July, 2017) ##
|
67
|
-
|
68
|
-
* Better HTML entity translation (thanks @leesoh).
|
69
|
-
* Import INFOS, SERVICES, etc as Issues #7 (thanks @rachkor).
|
70
|
-
|
71
|
-
## Dradis Framework 3.6 (March, 2017) ##
|
72
|
-
|
73
|
-
* No changes.
|
66
|
+
v3.6.0 (March 2017)
|
67
|
+
- No changes
|
data/CHANGELOG.template
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
[v#.#.#] ([month] [YYYY])
|
2
|
+
- [future tense verb] [feature]
|
3
|
+
- Upgraded gems:
|
4
|
+
- [gem]
|
5
|
+
- Bugs fixes:
|
6
|
+
- [future tense verb] [bug fix]
|
7
|
+
- Bug tracker items:
|
8
|
+
- [item]
|
9
|
+
- Security Fixes:
|
10
|
+
- High: (Authenticated|Unauthenticated) (admin|author|contributor) [vulnerability description]
|
11
|
+
- Medium: (Authenticated|Unauthenticated) (admin|author|contributor) [vulnerability description]
|
12
|
+
- Low: (Authenticated|Unauthenticated) (admin|author|contributor) [vulnerability description]
|
data/dradis-qualys.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
# versions of Rails (a sure recipe for disaster, I'm sure), which is needed
|
25
25
|
# until we bump Dradis Pro to 4.1.
|
26
26
|
# s.add_dependency 'rails', '~> 4.1.1'
|
27
|
-
spec.add_dependency 'dradis-plugins', '~> 4.0
|
27
|
+
spec.add_dependency 'dradis-plugins', '~> 4.0'
|
28
28
|
spec.add_dependency 'nokogiri', '~> 1.3'
|
29
29
|
|
30
30
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Dradis::Plugins::Qualys
|
2
|
+
module Asset
|
3
|
+
ROOT_PATH_NAME = 'ASSET_DATA_REPORT'.freeze
|
4
|
+
|
5
|
+
def self.meta
|
6
|
+
package = Dradis::Plugins::Qualys
|
7
|
+
|
8
|
+
{
|
9
|
+
name: package::Engine::plugin_name,
|
10
|
+
description: 'Upload Qualys Asset results (.xml)',
|
11
|
+
version: package.version
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
class Importer < Dradis::Plugins::Upload::Importer
|
16
|
+
def initialize(args={})
|
17
|
+
args[:plugin] = Dradis::Plugins::Qualys
|
18
|
+
super(args)
|
19
|
+
|
20
|
+
@issue_lookup = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# The framework will call this function if the user selects this plugin from
|
24
|
+
# the dropdown list and uploads a file.
|
25
|
+
# @returns true if the operation was successful, false otherwise
|
26
|
+
def import(params={})
|
27
|
+
file_content = File.read( params[:file] )
|
28
|
+
|
29
|
+
logger.info { 'Parsing Qualys ASSET XML output file...' }
|
30
|
+
doc = Nokogiri::XML(file_content)
|
31
|
+
logger.info { 'Done.' }
|
32
|
+
|
33
|
+
if doc.root.name != ROOT_PATH_NAME
|
34
|
+
error = 'No scan results were detected in the uploaded file. Ensure you uploaded a Qualys ASSET XML file.'
|
35
|
+
logger.fatal { error }
|
36
|
+
content_service.create_note text: error
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
doc.xpath('ASSET_DATA_REPORT/GLOSSARY/VULN_DETAILS_LIST/VULN_DETAILS').each do |xml_issue|
|
41
|
+
process_issue(xml_issue)
|
42
|
+
end
|
43
|
+
|
44
|
+
doc.xpath('ASSET_DATA_REPORT/HOST_LIST/HOST').each do |xml_node|
|
45
|
+
process_node(xml_node)
|
46
|
+
end
|
47
|
+
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_accessor :issue_lookup
|
54
|
+
|
55
|
+
def process_node(xml_node)
|
56
|
+
logger.info { 'Creating node...' }
|
57
|
+
|
58
|
+
# Create host node
|
59
|
+
host_node = content_service.create_node(
|
60
|
+
label: xml_node.at_xpath('IP').text,
|
61
|
+
type: :host
|
62
|
+
)
|
63
|
+
|
64
|
+
%w[dns host_id operating_system qg_hostid tracking_method].each do |key|
|
65
|
+
prop = xml_node.at_xpath(key.upcase)
|
66
|
+
host_node.set_property(key.to_sym, prop.text) if prop
|
67
|
+
end
|
68
|
+
|
69
|
+
tags = xml_node.at_xpath('ASSET_TAGS/ASSET_TAG')
|
70
|
+
if tags
|
71
|
+
tags.each do |tag|
|
72
|
+
host_node.set_property(:asset_tags, tag.text)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
host_node.save
|
77
|
+
|
78
|
+
xml_node.xpath('./VULN_INFO_LIST/VULN_INFO').each do |xml_evidence|
|
79
|
+
process_evidence(xml_evidence, host_node)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def process_issue(xml_vuln)
|
84
|
+
qid = xml_vuln.at_xpath('QID').text
|
85
|
+
logger.info { "\t => Creating new issue (plugin_id: #{ qid })" }
|
86
|
+
issue_text = template_service.process_template(template: 'asset-issue', data: xml_vuln)
|
87
|
+
issue = content_service.create_issue(text: issue_text, id: qid)
|
88
|
+
|
89
|
+
issue_lookup[qid.to_i] = issue
|
90
|
+
end
|
91
|
+
|
92
|
+
def process_evidence(xml_evidence, node)
|
93
|
+
qid = xml_evidence.at_xpath('./QID').text
|
94
|
+
|
95
|
+
issue = issue_lookup[qid.to_i]
|
96
|
+
if issue
|
97
|
+
issue_id = issue.respond_to?(:id) ? issue.id : issue.to_issue.id
|
98
|
+
|
99
|
+
logger.info { "\t => Creating new evidence (plugin_id: #{qid})" }
|
100
|
+
logger.info { "\t\t => Issue: #{issue.title} (plugin_id: #{issue_id})" }
|
101
|
+
logger.info { "\t\t => Node: #{node.label} (#{node.id})" }
|
102
|
+
else
|
103
|
+
logger.info { "\t => Couldn't find QID for issue with ID=#{qid}" }
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
evidence_content = template_service.process_template(template: 'asset-evidence', data: xml_evidence)
|
108
|
+
content_service.create_evidence(issue: issue, node: node, content: evidence_content)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -5,5 +5,18 @@ module Dradis::Plugins::Qualys
|
|
5
5
|
include ::Dradis::Plugins::Base
|
6
6
|
description 'Processes Qualys output'
|
7
7
|
provides :upload
|
8
|
+
|
9
|
+
# Because this plugin provides two export modules, we have to overwrite
|
10
|
+
# the default .uploaders() method.
|
11
|
+
#
|
12
|
+
# See:
|
13
|
+
# Dradis::Plugins::Upload::Base in dradis-plugins
|
14
|
+
def self.uploaders
|
15
|
+
[
|
16
|
+
Dradis::Plugins::Qualys::Asset,
|
17
|
+
Dradis::Plugins::Qualys::Vuln,
|
18
|
+
Dradis::Plugins::Qualys::WAS
|
19
|
+
]
|
20
|
+
end
|
8
21
|
end
|
9
22
|
end
|
@@ -4,8 +4,19 @@ module Dradis
|
|
4
4
|
class FieldProcessor < Dradis::Plugins::Upload::FieldProcessor
|
5
5
|
|
6
6
|
def post_initialize(args={})
|
7
|
-
|
8
|
-
|
7
|
+
case data.name
|
8
|
+
when 'CAT'
|
9
|
+
@cat_object = data
|
10
|
+
@qualys_object = ::Qualys::Element.new(data.elements.first)
|
11
|
+
when 'QID'
|
12
|
+
@qualys_object = ::Qualys::WAS::QID.new(data)
|
13
|
+
when 'VULNERABILITY'
|
14
|
+
@qualys_object = ::Qualys::WAS::Vulnerability.new(data)
|
15
|
+
when 'VULN_DETAILS'
|
16
|
+
@qualys_object = ::Qualys::Asset::Vulnerability.new(data)
|
17
|
+
when 'VULN_INFO'
|
18
|
+
@qualys_object = ::Qualys::Asset::Evidence.new(data)
|
19
|
+
end
|
9
20
|
end
|
10
21
|
|
11
22
|
def value(args={})
|
@@ -13,8 +24,14 @@ module Dradis
|
|
13
24
|
|
14
25
|
# Fields in the template are of the form <foo>.<field>, where <foo>
|
15
26
|
# is common across all fields for a given template (and meaningless).
|
16
|
-
|
27
|
+
# However we can use it to identify the type of scan we're processing.
|
28
|
+
type, name = field.split('.')
|
29
|
+
|
30
|
+
%{element evidence}.include?(type) ? value_network(name) : value_was(name)
|
31
|
+
end
|
17
32
|
|
33
|
+
private
|
34
|
+
def value_network(name)
|
18
35
|
if %w{cat_fqdn cat_misc cat_port cat_protocol cat_value}.include?(name)
|
19
36
|
attribute = name[4..-1]
|
20
37
|
@cat_object[attribute] || 'n/a'
|
@@ -36,6 +53,9 @@ module Dradis
|
|
36
53
|
end
|
37
54
|
end
|
38
55
|
|
56
|
+
def value_was(name)
|
57
|
+
@qualys_object.try(name) || 'n/a'
|
58
|
+
end
|
39
59
|
end
|
40
60
|
end
|
41
61
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Dradis::Plugins::Qualys
|
2
|
+
module Vuln
|
3
|
+
def self.meta
|
4
|
+
package = Dradis::Plugins::Qualys
|
5
|
+
|
6
|
+
{
|
7
|
+
name: package::Engine::plugin_name,
|
8
|
+
description: 'Upload Qualys Vulnerability Management results (.xml)',
|
9
|
+
version: package.version
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
class Importer < Dradis::Plugins::Upload::Importer
|
14
|
+
attr_accessor :host_node
|
15
|
+
|
16
|
+
def initialize(args={})
|
17
|
+
args[:plugin] = Dradis::Plugins::Qualys
|
18
|
+
super(args)
|
19
|
+
end
|
20
|
+
|
21
|
+
# The framework will call this function if the user selects this plugin from
|
22
|
+
# the dropdown list and uploads a file.
|
23
|
+
# @returns true if the operation was successful, false otherwise
|
24
|
+
def import(params={})
|
25
|
+
file_content = File.read( params[:file] )
|
26
|
+
|
27
|
+
logger.info{'Parsing Qualys output file...'}
|
28
|
+
@doc = Nokogiri::XML( file_content )
|
29
|
+
logger.info{'Done.'}
|
30
|
+
|
31
|
+
if @doc.root.name != 'SCAN'
|
32
|
+
error = "No scan results were detected in the uploaded file. Ensure you uploaded a Qualys XML file."
|
33
|
+
logger.fatal{ error }
|
34
|
+
content_service.create_note text: error
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
@doc.xpath('SCAN/IP').each do |xml_host|
|
39
|
+
process_ip(xml_host)
|
40
|
+
end
|
41
|
+
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def process_ip(xml_host)
|
48
|
+
host_ip = xml_host['value']
|
49
|
+
logger.info{ "Host: %s" % host_ip }
|
50
|
+
|
51
|
+
self.host_node = content_service.create_node(label: host_ip, type: :host)
|
52
|
+
|
53
|
+
host_node.set_property(:ip, host_ip)
|
54
|
+
host_node.set_property(:hostname, xml_host['name'])
|
55
|
+
if (xml_os = xml_host.xpath('OS')) && xml_os.any?
|
56
|
+
host_node.set_property(:os, xml_os.text)
|
57
|
+
end
|
58
|
+
host_node.save
|
59
|
+
|
60
|
+
# We treat INFOS, SERVICES, PRACTICES, and VULNS the same way
|
61
|
+
# All of these are imported into Dradis as Issues
|
62
|
+
['INFOS', 'SERVICES', 'PRACTICES', 'VULNS'].each do |collection|
|
63
|
+
xml_host.xpath(collection).each do |xml_collection|
|
64
|
+
process_collection(collection, xml_collection)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def process_collection(collection, xml_collection)
|
70
|
+
xml_cats = xml_collection.xpath('CAT')
|
71
|
+
|
72
|
+
xml_cats.each do |xml_cat|
|
73
|
+
logger.info{ "\t#{ collection } - #{ xml_cat['value'] }" }
|
74
|
+
|
75
|
+
empty_dup_xml_cat = xml_cat.dup
|
76
|
+
empty_dup_xml_cat.children.remove
|
77
|
+
|
78
|
+
# For each INFOS/CAT/INFO, SERVICES/CAT/SERVICE, VULNS/CAT/VULN, etc.
|
79
|
+
xml_cat.xpath(collection.chop).each do |xml_element|
|
80
|
+
dup_xml_cat = empty_dup_xml_cat.dup
|
81
|
+
dup_xml_cat.add_child(xml_element.dup)
|
82
|
+
cat_number = xml_element[:number]
|
83
|
+
|
84
|
+
process_vuln(cat_number, dup_xml_cat)
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Takes a <CAT> element containing a single <VULN> element and processes an
|
91
|
+
# Issue and Evidence template out of it.
|
92
|
+
def process_vuln(vuln_number, xml_cat)
|
93
|
+
logger.info{ "\t\t => Creating new issue (plugin_id: #{ vuln_number })" }
|
94
|
+
issue_text = template_service.process_template(template: 'element', data: xml_cat)
|
95
|
+
issue = content_service.create_issue(text: issue_text, id: vuln_number)
|
96
|
+
|
97
|
+
logger.info{ "\t\t => Creating new evidence" }
|
98
|
+
evidence_content = template_service.process_template(template: 'evidence', data: xml_cat)
|
99
|
+
content_service.create_evidence(issue: issue, node: self.host_node, content: evidence_content)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Dradis::Plugins::Qualys
|
2
|
+
|
3
|
+
# This module knows how to parse Qualys Web Application Scanner format.
|
4
|
+
module WAS
|
5
|
+
def self.meta
|
6
|
+
package = Dradis::Plugins::Qualys
|
7
|
+
|
8
|
+
{
|
9
|
+
name: package::Engine::plugin_name,
|
10
|
+
description: 'Upload Qualys WAS results (.xml)',
|
11
|
+
version: package.version
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
class Importer < Dradis::Plugins::Upload::Importer
|
16
|
+
def initialize(args={})
|
17
|
+
args[:plugin] = Dradis::Plugins::Qualys
|
18
|
+
super(args)
|
19
|
+
|
20
|
+
@issue_lookup = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def import(params={})
|
24
|
+
file_content = File.read(params[:file])
|
25
|
+
|
26
|
+
logger.info { 'Parsing Qualys WAS XML output file...'}
|
27
|
+
doc = Nokogiri::XML(file_content)
|
28
|
+
logger.info { 'Done.' }
|
29
|
+
|
30
|
+
if doc.root.name != 'WAS_SCAN_REPORT'
|
31
|
+
error = 'Document doesn\'t seem to be in the Qualys WAS XML format.'
|
32
|
+
logger.fatal { error }
|
33
|
+
content_service.create_note text: error
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
logger.info { 'Global Summary information'}
|
38
|
+
|
39
|
+
xml_global_summary = doc.at_xpath('WAS_SCAN_REPORT/SUMMARY/GLOBAL_SUMMARY')
|
40
|
+
logger.info { 'Security Risk: ' + xml_global_summary.at_xpath('./SECURITY_RISK').text }
|
41
|
+
logger.info { 'Vulnerabilities found: ' + xml_global_summary.at_xpath('./VULNERABILITY').text }
|
42
|
+
|
43
|
+
xml_webapp = doc.at_xpath('WAS_SCAN_REPORT/APPENDIX/WEBAPP')
|
44
|
+
process_webapp(xml_webapp)
|
45
|
+
|
46
|
+
doc.xpath('WAS_SCAN_REPORT/GLOSSARY/QID_LIST/QID').each do |xml_qid|
|
47
|
+
process_issue(xml_qid)
|
48
|
+
end
|
49
|
+
|
50
|
+
doc.xpath('WAS_SCAN_REPORT/RESULTS/VULNERABILITY_LIST/VULNERABILITY').each do |xml_vulnerability|
|
51
|
+
process_evidence(xml_vulnerability)
|
52
|
+
end
|
53
|
+
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
attr_accessor :webapp_node, :issue_lookup
|
59
|
+
|
60
|
+
def process_evidence(xml_vulnerability)
|
61
|
+
id = xml_vulnerability.at_xpath('./ID').text
|
62
|
+
|
63
|
+
issue = issue_lookup[xml_vulnerability.at_xpath('./QID').text.to_i]
|
64
|
+
if issue
|
65
|
+
issue_id = issue.respond_to?(:id) ? issue.id : issue.to_issue.id
|
66
|
+
|
67
|
+
logger.info{ "\t => Creating new evidence (plugin_id: #{id})" }
|
68
|
+
logger.info{ "\t\t => Issue: #{issue.title} (plugin_id: #{issue_id})" }
|
69
|
+
logger.info{ "\t\t => Node: #{webapp_node.label} (#{webapp_node.id})" }
|
70
|
+
else
|
71
|
+
logger.info{ "\t => Couldn't find QID for evidence with ID=#{id}" }
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
evidence_content = template_service.process_template(template: 'was-evidence', data: xml_vulnerability)
|
76
|
+
content_service.create_evidence(issue: issue, node: webapp_node, content: evidence_content)
|
77
|
+
end
|
78
|
+
|
79
|
+
def process_issue(xml_qid)
|
80
|
+
qid = xml_qid.at_xpath('QID').text
|
81
|
+
logger.info{ "\t => Creating new issue (plugin_id: #{ qid })" }
|
82
|
+
issue_text = template_service.process_template(template: 'was-issue', data: xml_qid)
|
83
|
+
issue = content_service.create_issue(text: issue_text, id: qid)
|
84
|
+
|
85
|
+
issue_lookup[qid.to_i] = issue
|
86
|
+
end
|
87
|
+
|
88
|
+
def process_webapp(xml_webapp)
|
89
|
+
id = xml_webapp.at_xpath('./ID').text
|
90
|
+
name = xml_webapp.at_xpath('./NAME').text
|
91
|
+
url = xml_webapp.at_xpath('./URL').text
|
92
|
+
scope = xml_webapp.at_xpath('./SCOPE').text
|
93
|
+
|
94
|
+
uri = URI(url)
|
95
|
+
@webapp_node = content_service.create_node(label: uri.host, type: :host)
|
96
|
+
|
97
|
+
webapp_node.set_property('qualys.webapp.id', id)
|
98
|
+
webapp_node.set_property('qualys.webapp.name', name)
|
99
|
+
webapp_node.set_property('qualys.webapp.url', url)
|
100
|
+
webapp_node.set_property('qualys.webapp.scope', scope)
|
101
|
+
webapp_node.save!
|
102
|
+
|
103
|
+
logger.info { 'Webapp name: ' + name }
|
104
|
+
logger.info { 'Webapp URL: ' + url }
|
105
|
+
logger.info { 'Webapp scope: ' + scope }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -7,5 +7,8 @@ end
|
|
7
7
|
|
8
8
|
require 'dradis/plugins/qualys/engine'
|
9
9
|
require 'dradis/plugins/qualys/field_processor'
|
10
|
-
require 'dradis/plugins/qualys/importer'
|
11
10
|
require 'dradis/plugins/qualys/version'
|
11
|
+
|
12
|
+
require 'dradis/plugins/qualys/asset/importer'
|
13
|
+
require 'dradis/plugins/qualys/vuln/importer'
|
14
|
+
require 'dradis/plugins/qualys/was/importer'
|
data/lib/dradis-qualys.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Qualys::Asset
|
2
|
+
# This class represents each of the ASSET_DATA_REPORT/GLOSSARY/VULN_INFO_LIST/
|
3
|
+
# VULN_INFO elements in the Qualys Asset XML document.
|
4
|
+
#
|
5
|
+
# It provides a convenient way to access the information scattered all over
|
6
|
+
# the XML in attributes and nested tags.
|
7
|
+
#
|
8
|
+
# Instead of providing separate methods for each supported property we rely
|
9
|
+
# on Ruby's #method_missing to do most of the work.
|
10
|
+
class Evidence
|
11
|
+
# Accepts an XML node from Nokogiri::XML.
|
12
|
+
def initialize(xml_node)
|
13
|
+
@xml = xml_node
|
14
|
+
end
|
15
|
+
|
16
|
+
# List of supported tags. They can be attributes, simple descendans or
|
17
|
+
# collections (e.g. <references/>, <tags/>)
|
18
|
+
def supported_tags
|
19
|
+
[
|
20
|
+
# simple tags
|
21
|
+
:first_round, :last_round, :result, :ssl, :times_found,
|
22
|
+
:type, :vuln_status,
|
23
|
+
|
24
|
+
:cvss_base, :cvss3_final
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
# This allows external callers (and specs) to check for implemented
|
29
|
+
# properties
|
30
|
+
def respond_to?(method, include_private=false)
|
31
|
+
return true if supported_tags.include?(method.to_sym)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
# This method is invoked by Ruby when a method that is not defined in this
|
36
|
+
# instance is called.
|
37
|
+
#
|
38
|
+
# In our case we inspect the @method@ parameter and try to find the
|
39
|
+
# attribute, simple descendent or collection that it maps to in the XML
|
40
|
+
# tree.
|
41
|
+
def method_missing(method, *args)
|
42
|
+
# We could remove this check and return nil for any non-recognized tag.
|
43
|
+
# The problem would be that it would make tricky to debug problems with
|
44
|
+
# typos. For instance: <>.potr would return nil instead of raising an
|
45
|
+
# exception
|
46
|
+
unless supported_tags.include?(method)
|
47
|
+
super
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
process_field_value(method.to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
def process_field_value(method)
|
55
|
+
tag = @xml.at_xpath("./#{method.upcase}")
|
56
|
+
|
57
|
+
if tag && !tag.text.blank?
|
58
|
+
if tags_with_html_content.include?(method)
|
59
|
+
Qualys.cleanup_html(tag.text)
|
60
|
+
else
|
61
|
+
tag.text
|
62
|
+
end
|
63
|
+
else
|
64
|
+
'n/a'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def tags_with_html_content
|
71
|
+
%w[result]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|