dradis-qualys 3.21.0 → 4.2.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 +43 -46
- data/CHANGELOG.template +12 -0
- data/dradis-qualys.gemspec +1 -1
- data/lib/dradis/plugins/qualys/engine.rb +12 -0
- data/lib/dradis/plugins/qualys/field_processor.rb +19 -3
- data/lib/dradis/plugins/qualys/gem_version.rb +2 -2
- 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 +3 -1
- data/lib/dradis-qualys.rb +2 -0
- data/lib/qualys/element.rb +32 -25
- data/lib/qualys/was/qid.rb +71 -0
- data/lib/qualys/was/vulnerability.rb +68 -0
- data/lib/tasks/thorfile.rb +15 -1
- data/spec/fixtures/files/simple_was.xml +127 -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 +50 -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 +27 -11
- 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: 3ba4d6ad211ac3a9b0b671a70ca898f8b4209476b0a60f2da30e8e4421a805f6
|
4
|
+
data.tar.gz: 5d2e7db73a7a40784fa6c4334e060daa1cbf730655d489efe594f3fd8acb7b07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d612c8715670f02a26c9be3efc6f9406c5543e3e5b76725d56baf394a0cce8eeafe4fd2c8cab28e2558b70d60b9e72a79c25a29a9266df52e0efa32a963425ae
|
7
|
+
data.tar.gz: e3ae22e600fa0b9ad4f96215ed9e981f4f932bc38480ee48608f28c578c1fe53d426550c30b0a6228d0109d36f422eb7deee3fc6df62359c55cc39396c42848c
|
data/CHANGELOG.md
CHANGED
@@ -1,65 +1,62 @@
|
|
1
|
-
|
1
|
+
v4.2.0 (February 2022)
|
2
|
+
- Added 'element.qualys_collection' as issue field
|
3
|
+
- Added Qualys Web Application Scanner (WAS) support
|
2
4
|
|
3
|
-
|
5
|
+
v4.1.0 (November 2021)
|
6
|
+
- Add <dd>, <dt> support
|
7
|
+
- Remove orphaned <b> tags
|
4
8
|
|
5
|
-
|
9
|
+
v4.0.0 (July 2021)
|
10
|
+
- No changes
|
6
11
|
|
7
|
-
|
12
|
+
v3.22.0 (April 2021)
|
13
|
+
- No changes
|
8
14
|
|
9
|
-
|
15
|
+
v3.21.0 (February 2021)
|
16
|
+
- No changes
|
10
17
|
|
11
|
-
|
18
|
+
v3.20.0 (December 2020)
|
19
|
+
- No changes
|
12
20
|
|
13
|
-
|
21
|
+
v3.19.0 (September 2020)
|
22
|
+
- No changes
|
14
23
|
|
15
|
-
|
24
|
+
v3.18.0 (July 2020)
|
25
|
+
- No changes
|
16
26
|
|
17
|
-
|
27
|
+
v3.17.0 (May 2020)
|
28
|
+
- No changes
|
18
29
|
|
19
|
-
|
30
|
+
v3.16.0 (February 2020)
|
31
|
+
- No changes
|
20
32
|
|
21
|
-
|
33
|
+
v3.15.0 (November 2019)
|
34
|
+
- No changes
|
22
35
|
|
23
|
-
|
36
|
+
v3.14.0 (August 2019)
|
37
|
+
- No changes
|
24
38
|
|
25
|
-
|
39
|
+
v3.13.0 (June 2019)
|
40
|
+
- No changes
|
26
41
|
|
27
|
-
|
42
|
+
v3.12.0 (March 2019)
|
43
|
+
- No changes
|
28
44
|
|
29
|
-
|
45
|
+
v3.11.0 (November 2018)
|
46
|
+
- No changes
|
30
47
|
|
31
|
-
|
48
|
+
v3.10.0 (August 2018)
|
49
|
+
- No changes
|
32
50
|
|
33
|
-
|
51
|
+
v3.9.0 (January 2018)
|
52
|
+
- Add `os`, `hostname`, and `ip` as Node properties instead of a `Basic host info` Note
|
34
53
|
|
35
|
-
|
54
|
+
v3.8.0 (September 2017)
|
55
|
+
- No changes
|
36
56
|
|
37
|
-
|
57
|
+
v3.7.0 (July 2017)
|
58
|
+
- Better HTML entity translation (thanks @leesoh)
|
59
|
+
- Import INFOS, SERVICES, etc as Issues #7 (thanks @rachkor)
|
38
60
|
|
39
|
-
|
40
|
-
|
41
|
-
## Dradis Framework 3.11 (November, 2018) ##
|
42
|
-
|
43
|
-
* No changes.
|
44
|
-
|
45
|
-
## Dradis Framework 3.10 (August, 2018) ##
|
46
|
-
|
47
|
-
* No changes.
|
48
|
-
|
49
|
-
## Dradis Framework 3.9 (January, 2018) ##
|
50
|
-
|
51
|
-
* Add `os`, `hostname`, and `ip` as Node properties
|
52
|
-
instead of a `Basic host info` Note (v3.8.1)
|
53
|
-
|
54
|
-
## Dradis Framework 3.8 (September, 2017) ##
|
55
|
-
|
56
|
-
* No changes.
|
57
|
-
|
58
|
-
## Dradis Framework 3.7 (July, 2017) ##
|
59
|
-
|
60
|
-
* Better HTML entity translation (thanks @leesoh).
|
61
|
-
* Import INFOS, SERVICES, etc as Issues #7 (thanks @rachkor).
|
62
|
-
|
63
|
-
## Dradis Framework 3.6 (March, 2017) ##
|
64
|
-
|
65
|
-
* No changes.
|
61
|
+
v3.6.0 (March 2017)
|
62
|
+
- 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', '~>
|
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'
|
@@ -5,5 +5,17 @@ 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::Vuln,
|
17
|
+
Dradis::Plugins::Qualys::WAS
|
18
|
+
]
|
19
|
+
end
|
8
20
|
end
|
9
21
|
end
|
@@ -4,8 +4,15 @@ 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
|
+
end
|
9
16
|
end
|
10
17
|
|
11
18
|
def value(args={})
|
@@ -13,8 +20,14 @@ module Dradis
|
|
13
20
|
|
14
21
|
# Fields in the template are of the form <foo>.<field>, where <foo>
|
15
22
|
# is common across all fields for a given template (and meaningless).
|
16
|
-
|
23
|
+
# However we can use it to identify the type of scan we're processing.
|
24
|
+
type, name = field.split('.')
|
25
|
+
|
26
|
+
%{element evidence}.include?(type) ? value_network(name) : value_was(name)
|
27
|
+
end
|
17
28
|
|
29
|
+
private
|
30
|
+
def value_network(name)
|
18
31
|
if %w{cat_fqdn cat_misc cat_port cat_protocol cat_value}.include?(name)
|
19
32
|
attribute = name[4..-1]
|
20
33
|
@cat_object[attribute] || 'n/a'
|
@@ -36,6 +49,9 @@ module Dradis
|
|
36
49
|
end
|
37
50
|
end
|
38
51
|
|
52
|
+
def value_was(name)
|
53
|
+
@qualys_object.try(name) || 'n/a'
|
54
|
+
end
|
39
55
|
end
|
40
56
|
end
|
41
57
|
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,7 @@ 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/vuln/importer'
|
13
|
+
require 'dradis/plugins/qualys/was/importer'
|
data/lib/dradis-qualys.rb
CHANGED
data/lib/qualys/element.rb
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
module Qualys
|
2
|
+
|
3
|
+
def self.cleanup_html(source)
|
4
|
+
result = source.dup
|
5
|
+
result.gsub!(/"/, '"')
|
6
|
+
result.gsub!(/</, '<')
|
7
|
+
result.gsub!(/>/, '>')
|
8
|
+
|
9
|
+
result.gsub!(/<p>/i, "\n\n")
|
10
|
+
result.gsub!(/<br>/i, "\n")
|
11
|
+
result.gsub!(/ /, "")
|
12
|
+
result.gsub!(/<a href=\"(.*?)\"\s?target=\"_blank\">(.*?)<\/a>/i) { "\"#{$2.strip}\":#{$1.strip}" }
|
13
|
+
result.gsub!(/<pre>(.*?)<\/pre>/im) { |m| "\n\nbc.. #{$1.strip}\n\np. \n" }
|
14
|
+
result.gsub!(/<b>(.*?)<\/b>/i) { "*#{$1.strip}*" }
|
15
|
+
result.gsub!(/<b>|<\/b>/i, "")
|
16
|
+
result.gsub!(/<i>(.*?)<\/i>/i) { "_#{$1.strip}_" }
|
17
|
+
|
18
|
+
result.gsub!(/<dl>|<\/dl>/i, "\n")
|
19
|
+
result.gsub!(/<dt>(.*?)<\/dt>/i) { "* #{$1.strip}" }
|
20
|
+
result.gsub!(/<dd>(.*?)<\/dd>/i) { "** #{$1.strip}" }
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
|
2
25
|
# This class represents each of the /SCAN/IP/[INFOS|SERVICES|VULNS|PRACTICES]/CAT/[INFO|SERVICE|VULN|PRACTICE]
|
3
26
|
# elements in the Qualys XML document.
|
4
27
|
#
|
@@ -25,7 +48,10 @@ module Qualys
|
|
25
48
|
:consequence, :solution, :compliance, :result,
|
26
49
|
|
27
50
|
# multiple tags
|
28
|
-
:vendor_reference_list, :cve_id_list, :bugtraq_id_list
|
51
|
+
:vendor_reference_list, :cve_id_list, :bugtraq_id_list,
|
52
|
+
|
53
|
+
# category
|
54
|
+
:qualys_collection
|
29
55
|
]
|
30
56
|
end
|
31
57
|
|
@@ -66,10 +92,10 @@ module Qualys
|
|
66
92
|
return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)
|
67
93
|
|
68
94
|
# Then we try simple children tags: TITLE, LAST_UPDATE, CVSS_BASE...
|
69
|
-
tag = @xml.
|
95
|
+
tag = @xml.at_xpath("./#{method_name.upcase}")
|
70
96
|
if tag && !tag.text.blank?
|
71
97
|
if tags_with_html_content.include?(method)
|
72
|
-
return cleanup_html(tag.text)
|
98
|
+
return Qualys::cleanup_html(tag.text)
|
73
99
|
else
|
74
100
|
return tag.text
|
75
101
|
end
|
@@ -77,11 +103,8 @@ module Qualys
|
|
77
103
|
'n/a'
|
78
104
|
end
|
79
105
|
|
80
|
-
|
81
|
-
|
82
|
-
# @xml.xpath("./references/reference").collect{|entry| {:source => entry['source'], :text => entry.text} }
|
83
|
-
elsif method == 'tags'
|
84
|
-
# @xml.xpath("./tags/tag").collect(&:text)
|
106
|
+
if method_name == 'qualys_collection'
|
107
|
+
@xml.name
|
85
108
|
else
|
86
109
|
# nothing found, the tag is valid but not present in this ReportItem
|
87
110
|
return nil
|
@@ -90,24 +113,8 @@ module Qualys
|
|
90
113
|
|
91
114
|
private
|
92
115
|
|
93
|
-
def cleanup_html(source)
|
94
|
-
result = source.dup
|
95
|
-
result.gsub!(/"/, '"')
|
96
|
-
result.gsub!(/</, '<')
|
97
|
-
result.gsub!(/>/, '>')
|
98
|
-
|
99
|
-
result.gsub!(/<p>/i, "\n\n")
|
100
|
-
result.gsub!(/<br>/i, "\n")
|
101
|
-
result.gsub!(/ /, "")
|
102
|
-
result.gsub!(/<a href=\"(.*?)\" target=\"_blank\">(.*?)<\/a>/i) { "\"#{$2.strip}\":#{$1.strip}" }
|
103
|
-
result.gsub!(/<pre>(.*?)<\/pre>/im) { |m| "\n\nbc.. #{$1.strip}\n\np. \n" }
|
104
|
-
result.gsub!(/<b>(.*?)<\/b>/i) { "*#{$1.strip}*" }
|
105
|
-
result.gsub!(/<i>(.*?)<\/i>/i) { "_#{$1.strip}_" }
|
106
|
-
result
|
107
|
-
end
|
108
|
-
|
109
116
|
def tags_with_html_content
|
110
|
-
[:diagnosis, :solution]
|
117
|
+
[:consequence, :diagnosis, :solution]
|
111
118
|
end
|
112
119
|
|
113
120
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Qualys::WAS
|
2
|
+
# This class represents each of the WAS_SCAN_REPORT/GLOSSARY/QID_LIST/QID
|
3
|
+
# elements in the Qualys WAS 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 QID
|
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
|
+
:category, :cwe, :description, :group, :impact, :owasp, :qid,
|
22
|
+
:severity, :solution, :title, :wasc,
|
23
|
+
|
24
|
+
:cvss_base, :cvss_temporal, :cvss3_base, :cvss3_temporal, :cvss3_vector
|
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
|
+
method_name = method.to_s
|
52
|
+
|
53
|
+
# Then we try simple children tags: TITLE, LAST_UPDATE, CVSS_BASE...
|
54
|
+
tag = @xml.at_xpath("./#{method_name.upcase}")
|
55
|
+
if tag && !tag.text.blank?
|
56
|
+
if tags_with_html_content.include?(method)
|
57
|
+
return Qualys::cleanup_html(tag.text)
|
58
|
+
else
|
59
|
+
return tag.text
|
60
|
+
end
|
61
|
+
else
|
62
|
+
'n/a'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def tags_with_html_content
|
68
|
+
[:description, :impact, :solution]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Qualys::WAS
|
2
|
+
# This class represents each of the WAS_SCAN_REPORT/RESULTS/VULNERABILITY_LIST/
|
3
|
+
# VULNERABILITY elements in the Qualys WAS 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 Vulnerability
|
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
|
+
:access_paths, :ajax, :authentication, :ignored, :potential, :url
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
# This allows external callers (and specs) to check for implemented
|
26
|
+
# properties
|
27
|
+
def respond_to?(method, include_private=false)
|
28
|
+
return true if supported_tags.include?(method.to_sym)
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
# This method is invoked by Ruby when a method that is not defined in this
|
33
|
+
# instance is called.
|
34
|
+
#
|
35
|
+
# In our case we inspect the @method@ parameter and try to find the
|
36
|
+
# attribute, simple descendent or collection that it maps to in the XML
|
37
|
+
# tree.
|
38
|
+
def method_missing(method, *args)
|
39
|
+
# We could remove this check and return nil for any non-recognized tag.
|
40
|
+
# The problem would be that it would make tricky to debug problems with
|
41
|
+
# typos. For instance: <>.potr would return nil instead of raising an
|
42
|
+
# exception
|
43
|
+
unless supported_tags.include?(method)
|
44
|
+
super
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
method_name = method.to_s
|
49
|
+
|
50
|
+
# Then we try simple children tags: TITLE, LAST_UPDATE, CVSS_BASE...
|
51
|
+
tag = @xml.at_xpath("./#{method_name.upcase}")
|
52
|
+
if tag && !tag.text.blank?
|
53
|
+
if tags_with_html_content.include?(method)
|
54
|
+
return Qualys::cleanup_html(tag.text)
|
55
|
+
else
|
56
|
+
return tag.text
|
57
|
+
end
|
58
|
+
else
|
59
|
+
'n/a'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def tags_with_html_content
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|