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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3c7d833dd84e2bf216b93b9b25bf4c536323bb1273bdf02bc538e0c864d9beb
4
- data.tar.gz: 60844d26ac41bd494d11418861275e09b3ffae575a587c8a243e366422febc42
3
+ metadata.gz: 3ba4d6ad211ac3a9b0b671a70ca898f8b4209476b0a60f2da30e8e4421a805f6
4
+ data.tar.gz: 5d2e7db73a7a40784fa6c4334e060daa1cbf730655d489efe594f3fd8acb7b07
5
5
  SHA512:
6
- metadata.gz: d65729940d71d316e43744a4e426ae5b22979cccfa7fa3eed6de53ba3e603e662f34764eee9c7db7ddaef92aa3a56d28d30df274a86a982d13b4766f36ddbc91
7
- data.tar.gz: 4be4b2c87ec17804e03549b3bbc216fb7201d9a737f8ccea8aa48ce32fc2c0ead85118703497c1616272b71852a4d50e11817105d6b15203701f196bee13816e
6
+ metadata.gz: d612c8715670f02a26c9be3efc6f9406c5543e3e5b76725d56baf394a0cce8eeafe4fd2c8cab28e2558b70d60b9e72a79c25a29a9266df52e0efa32a963425ae
7
+ data.tar.gz: e3ae22e600fa0b9ad4f96215ed9e981f4f932bc38480ee48608f28c578c1fe53d426550c30b0a6228d0109d36f422eb7deee3fc6df62359c55cc39396c42848c
data/CHANGELOG.md CHANGED
@@ -1,65 +1,62 @@
1
- ## Dradis Framework 3.21 (February, 2021) ##
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
- * No changes.
5
+ v4.1.0 (November 2021)
6
+ - Add <dd>, <dt> support
7
+ - Remove orphaned <b> tags
4
8
 
5
- ## Dradis Framework 3.20 (December, 2020) ##
9
+ v4.0.0 (July 2021)
10
+ - No changes
6
11
 
7
- * No changes.
12
+ v3.22.0 (April 2021)
13
+ - No changes
8
14
 
9
- ## Dradis Framework 3.19 (September, 2020) ##
15
+ v3.21.0 (February 2021)
16
+ - No changes
10
17
 
11
- * No changes.
18
+ v3.20.0 (December 2020)
19
+ - No changes
12
20
 
13
- ## Dradis Framework 3.18 (July, 2020) ##
21
+ v3.19.0 (September 2020)
22
+ - No changes
14
23
 
15
- * No changes.
24
+ v3.18.0 (July 2020)
25
+ - No changes
16
26
 
17
- ## Dradis Framework 3.17 (May, 2020) ##
27
+ v3.17.0 (May 2020)
28
+ - No changes
18
29
 
19
- * No changes.
30
+ v3.16.0 (February 2020)
31
+ - No changes
20
32
 
21
- ## Dradis Framework 3.16 (February, 2020) ##
33
+ v3.15.0 (November 2019)
34
+ - No changes
22
35
 
23
- * No changes.
36
+ v3.14.0 (August 2019)
37
+ - No changes
24
38
 
25
- ## Dradis Framework 3.15 (November, 2019) ##
39
+ v3.13.0 (June 2019)
40
+ - No changes
26
41
 
27
- * No changes.
42
+ v3.12.0 (March 2019)
43
+ - No changes
28
44
 
29
- ## Dradis Framework 3.14 (August, 2019) ##
45
+ v3.11.0 (November 2018)
46
+ - No changes
30
47
 
31
- * No changes.
48
+ v3.10.0 (August 2018)
49
+ - No changes
32
50
 
33
- ## Dradis Framework 3.13 (June, 2019) ##
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
- * No changes.
54
+ v3.8.0 (September 2017)
55
+ - No changes
36
56
 
37
- ## Dradis Framework 3.12 (March, 2019) ##
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
- * No changes.
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
@@ -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]
@@ -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', '~> 3.6'
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
- @cat_object = data
8
- @qualys_object = ::Qualys::Element.new(data.elements.first)
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
- _, name = field.split('.')
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
@@ -7,8 +7,8 @@ module Dradis
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 3
11
- MINOR = 21
10
+ MAJOR = 4
11
+ MINOR = 2
12
12
  TINY = 0
13
13
  PRE = nil
14
14
 
@@ -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
@@ -6,3 +6,5 @@ require 'dradis/plugins/qualys'
6
6
 
7
7
  # Load supporting Qualys classes
8
8
  require 'qualys/element'
9
+ require 'qualys/was/qid'
10
+ require 'qualys/was/vulnerability'
@@ -1,4 +1,27 @@
1
1
  module Qualys
2
+
3
+ def self.cleanup_html(source)
4
+ result = source.dup
5
+ result.gsub!(/&quot;/, '"')
6
+ result.gsub!(/&lt;/, '<')
7
+ result.gsub!(/&gt;/, '>')
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.xpath("./#{method_name.upcase}").first
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
- # Finally the enumerations: vendor_reference_list, cve_id_list, bugtraq_id_list
81
- if method_name == 'references'
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!(/&quot;/, '"')
96
- result.gsub!(/&lt;/, '<')
97
- result.gsub!(/&gt;/, '>')
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