dradis-qualys 4.0.0 → 4.3.0
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/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
|