dradis-qualys 4.1.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: 953114d5501cf866740c9ab4657191ab2fc123998bf29a5c7303292740b6a3c9
4
- data.tar.gz: 3cb44c03b7a3ac23d8a07e73cae291ffb9a5cdf8f5f8f27b57739788da164204
3
+ metadata.gz: 3ba4d6ad211ac3a9b0b671a70ca898f8b4209476b0a60f2da30e8e4421a805f6
4
+ data.tar.gz: 5d2e7db73a7a40784fa6c4334e060daa1cbf730655d489efe594f3fd8acb7b07
5
5
  SHA512:
6
- metadata.gz: c98b9d282b4b7fc7dd74e70a9755b8df60776f3a918a9283f1ce5081ec9cbcffee16e63566ade648405d44f56fae635d2d94baeed949fe08e211fc6c4efaf2a1
7
- data.tar.gz: 835a5c1e0f1956b61354c72fe821ddd31c455a69c8a8df2393514f3c80a663c4365d821ee424383af9268a534cced11c22a4477b03770a7f0fc0461e8631d712
6
+ metadata.gz: d612c8715670f02a26c9be3efc6f9406c5543e3e5b76725d56baf394a0cce8eeafe4fd2c8cab28e2558b70d60b9e72a79c25a29a9266df52e0efa32a963425ae
7
+ data.tar.gz: e3ae22e600fa0b9ad4f96215ed9e981f4f932bc38480ee48608f28c578c1fe53d426550c30b0a6228d0109d36f422eb7deee3fc6df62359c55cc39396c42848c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ v4.2.0 (February 2022)
2
+ - Added 'element.qualys_collection' as issue field
3
+ - Added Qualys Web Application Scanner (WAS) support
4
+
1
5
  v4.1.0 (November 2021)
2
6
  - Add <dd>, <dt> support
3
7
  - Remove orphaned <b> tags
@@ -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
@@ -8,7 +8,7 @@ module Dradis
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 4
11
- MINOR = 1
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,27 +113,6 @@ 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=\"(.*?)\"\s?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!(/<b>|<\/b>/i, "")
106
- result.gsub!(/<i>(.*?)<\/i>/i) { "_#{$1.strip}_" }
107
-
108
- result.gsub!(/<dl>|<\/dl>/i, "\n")
109
- result.gsub!(/<dt>(.*?)<\/dt>/i) { "* #{$1.strip}" }
110
- result.gsub!(/<dd>(.*?)<\/dd>/i) { "** #{$1.strip}" }
111
- result
112
- end
113
-
114
116
  def tags_with_html_content
115
117
  [:consequence, :diagnosis, :solution]
116
118
  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
@@ -14,8 +14,22 @@ class QualysTasks < Thor
14
14
 
15
15
  detect_and_set_project_scope
16
16
 
17
- importer = Dradis::Plugins::Qualys::Importer.new(task_options)
17
+ importer = Dradis::Plugins::Qualys::Vuln::Importer.new(task_options)
18
18
  importer.import(file: file_path)
19
19
  end
20
20
 
21
+ desc "upload_was FILE", "upload Qualys WAS XML results"
22
+ def upload_was(file_path)
23
+ require 'config/environment'
24
+
25
+ unless File.exists?(file_path)
26
+ $stderr.puts "** the file [#{file_path}] does not exist"
27
+ exit -1
28
+ end
29
+
30
+ detect_and_set_project_scope
31
+
32
+ importer = Dradis::Plugins::Qualys::WAS::Importer.new(task_options)
33
+ importer.import(file: file_path)
34
+ end
21
35
  end
@@ -0,0 +1,127 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <WAS_SCAN_REPORT>
3
+ <HEADER>
4
+ <NAME>Scan Report</NAME>
5
+ <DESCRIPTION>Vulnerabilities of all selected scans are consolidated into one report so that you can view their evolution.</DESCRIPTION>
6
+ <GENERATION_DATETIME>10 Nov 2021 10:00AM GMT-0500</GENERATION_DATETIME>
7
+ <COMPANY_INFO>
8
+ <NAME>Sample Company</NAME>
9
+ <ADDRESS>Sample Address</ADDRESS>
10
+ <CITY>Sample City</CITY>
11
+ <STATE>Sample State</STATE>
12
+ <COUNTRY>Sample Country</COUNTRY>
13
+ <ZIP_CODE>00000</ZIP_CODE>
14
+ </COMPANY_INFO>
15
+ <USER_INFO>
16
+ <NAME>Test User</NAME>
17
+ <USERNAME>test_user</USERNAME>
18
+ <ROLE>PC User,VM User</ROLE>
19
+ </USER_INFO>
20
+ </HEADER>
21
+ <FILTERS>
22
+ <FILTER>
23
+ <NAME>REMEDIATION</NAME>
24
+ <VALUE>Include patched findings</VALUE>
25
+ </FILTER>
26
+ <FILTER>
27
+ <NAME>REMEDIATION</NAME>
28
+ <VALUE>Show ignored findings </VALUE>
29
+ </FILTER>
30
+ </FILTERS>
31
+ <TARGET>
32
+ <SCAN>Test Scan</SCAN>
33
+ </TARGET>
34
+ <SUMMARY>
35
+ <GLOBAL_SUMMARY>
36
+ <SECURITY_RISK>High</SECURITY_RISK>
37
+ <VULNERABILITY>31</VULNERABILITY>
38
+ <SENSITIVE_CONTENT>0</SENSITIVE_CONTENT>
39
+ <INFORMATION_GATHERED>30</INFORMATION_GATHERED>
40
+ </GLOBAL_SUMMARY>
41
+ <SUMMARY_STATS>
42
+ <SUMMARY_STAT>
43
+ <SCAN>test Scan</SCAN>
44
+ <DATE>12 Oct 2021</DATE>
45
+ <LEVEL5>5</LEVEL5>
46
+ <LEVEL4>2</LEVEL4>
47
+ <LEVEL3>9</LEVEL3>
48
+ <LEVEL2>2</LEVEL2>
49
+ <LEVEL1>13</LEVEL1>
50
+ <SENSITIVE_CONTENT>0</SENSITIVE_CONTENT>
51
+ <INFORMATION_GATHERED>30</INFORMATION_GATHERED>
52
+ </SUMMARY_STAT>
53
+ </SUMMARY_STATS>
54
+ </SUMMARY>
55
+ <RESULTS>
56
+ <VULNERABILITY_LIST>
57
+ <VULNERABILITY>
58
+ <UNIQUE_ID>test-id</UNIQUE_ID>
59
+ <ID>1</ID>
60
+ <DETECTION_ID>1</DETECTION_ID>
61
+ <QID>6</QID>
62
+ <URL>http://example.com</URL>
63
+ <ACCESS_PATH>
64
+ <URL>http://example.com</URL>
65
+ </ACCESS_PATH>
66
+ <AJAX>false</AJAX>
67
+ <AUTHENTICATION>Not Required</AUTHENTICATION>
68
+ <DETECTION_DATE>21 Aug 2021 10:00PM GMT-0500</DETECTION_DATE>
69
+ <POTENTIAL>false</POTENTIAL>
70
+ <PAYLOADS>
71
+ <PAYLOAD>
72
+ <NUM>1</NUM>
73
+ <PAYLOAD>N/A</PAYLOAD>
74
+ <REQUEST>
75
+ <METHOD>GET</METHOD>
76
+ <URL>http://example.com</URL>
77
+ <HEADERS>
78
+ <HEADER>
79
+ <key>Host</key>
80
+ <value><![CDATA[ example.com ]]></value>
81
+ </HEADER>
82
+ <HEADER>
83
+ <key>User-Agent</key>
84
+ <value>user-agent</value>
85
+ </HEADER>
86
+ <HEADER>
87
+ <key>Accept</key>
88
+ <value><![CDATA[ */*
89
+ </HEADER>
90
+ </HEADERS>
91
+ <BODY></BODY>
92
+ </REQUEST>
93
+ <RESPONSE>
94
+ <CONTENTS base64="true"></CONTENTS>
95
+ </RESPONSE>
96
+ </PAYLOAD>
97
+ </PAYLOADS>
98
+ <IGNORED>false</IGNORED>
99
+ </VULNERABILITY>
100
+ </VULNERABILITY_LIST>
101
+ </RESULTS>
102
+ <GLOSSARY>
103
+ <QID_LIST>
104
+ <QID>
105
+ <QID>6</QID>
106
+ <CATEGORY>Information Gathered</CATEGORY>
107
+ <SEVERITY>1</SEVERITY>
108
+ <TITLE>DNS Host Name</TITLE>
109
+ <GROUP>DIAG</GROUP>
110
+ <DESCRIPTION>The fully qualified domain name of this host, if it was obtained from a DNS server, is displayed in the RESULT section.</DESCRIPTION>
111
+ <IMPACT>N/A</IMPACT>
112
+ <SOLUTION>N/A</SOLUTION>
113
+ </QID>
114
+ </QID_LIST>
115
+ </GLOSSARY>
116
+ <APPENDIX>
117
+ <WEBAPP>
118
+ <ID>1</ID>
119
+ <NAME>Test</NAME>
120
+ <URL>http://example.com</URL>
121
+ <OWNER>Test User</OWNER>
122
+ <SCOPE>Limit to URL hostname</SCOPE>
123
+ <CUSTOM_ATTRIBUTES/>
124
+ <TAGS/>
125
+ </WEBAPP>
126
+ </APPENDIX>
127
+ </WAS_SCAN_REPORT>
@@ -5,37 +5,15 @@ module Dradis::Plugins
5
5
  describe 'Qualys upload plugin' do
6
6
  before(:each) do
7
7
  # Stub template service
8
- templates_dir = File.expand_path('../../../templates', __FILE__)
8
+ templates_dir = File.expand_path('../../../../templates', __FILE__)
9
9
  expect_any_instance_of(Dradis::Plugins::TemplateService)
10
10
  .to receive(:default_templates_dir).and_return(templates_dir)
11
11
 
12
- # Init services
13
- plugin = Dradis::Plugins::Qualys
12
+ stub_content_service
14
13
 
15
- @content_service = Dradis::Plugins::ContentService::Base.new(
16
- logger: Logger.new(STDOUT),
17
- plugin: plugin
18
- )
19
-
20
- @importer = Dradis::Plugins::Qualys::Importer.new(
14
+ @importer = Dradis::Plugins::Qualys::Vuln::Importer.new(
21
15
  content_service: @content_service
22
16
  )
23
-
24
- # Stub dradis-plugins methods
25
- #
26
- # They return their argument hashes as objects mimicking
27
- # Nodes, Issues, etc
28
- allow(@content_service).to receive(:create_node) do |args|
29
- obj = OpenStruct.new(args)
30
- obj.define_singleton_method(:set_property) { |_, __| }
31
- obj
32
- end
33
- allow(@content_service).to receive(:create_issue) do |args|
34
- OpenStruct.new(args)
35
- end
36
- allow(@content_service).to receive(:create_evidence) do |args|
37
- OpenStruct.new(args)
38
- end
39
17
  end
40
18
 
41
19
  let(:example_xml) { 'spec/fixtures/files/simple.xml' }
@@ -84,12 +62,12 @@ module Dradis::Plugins
84
62
  expect_to_create_issue_with(
85
63
  text: "Apache 1.3 HTTP Server Expect Header Cross-Site Scripting"
86
64
  )
87
-
65
+
88
66
  expect_to_create_issue_with(
89
67
  text: "Apache Web Server ETag Header Information Disclosure Weakness",
90
68
  text: "OpenBSD has released a \"patch\":ftp://ftp.openbsd.org/pub/OpenBSD/patches/3.2/common/008_httpd.patch that fixes this vulnerability. After installing the patch, inode numbers returned from the server are encoded using a private hash to avoid the release of sensitive information.\n\n\n\nCustomers"
91
69
  )
92
-
70
+
93
71
  run_import!
94
72
  end
95
73
 
@@ -165,28 +143,5 @@ module Dradis::Plugins
165
143
  @importer.import(file: 'spec/fixtures/files/no_result.xml')
166
144
  end
167
145
  end
168
-
169
-
170
- def expect_to_create_node_with(label:)
171
- expect(@content_service).to receive(:create_node).with(
172
- hash_including label: label
173
- ).once
174
- end
175
-
176
- def expect_to_create_issue_with(text:)
177
- expect(@content_service).to receive(:create_issue) do |args|
178
- expect(args[:text]).to include text
179
- OpenStruct.new(args)
180
- end.once
181
- end
182
-
183
- def expect_to_create_evidence_with(content:, issue:, node_label:)
184
- expect(@content_service).to receive(:create_evidence) do |args|
185
- expect(args[:content]).to include content
186
- expect(args[:issue].text).to include issue
187
- expect(args[:node].label).to eq node_label
188
- end.once
189
- end
190
-
191
146
  end
192
147
  end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ module Dradis::Plugins
5
+ describe 'Qualys upload plugin' do
6
+ before(:each) do
7
+ # Stub template service
8
+ templates_dir = File.expand_path('../../../../templates', __FILE__)
9
+ expect_any_instance_of(Dradis::Plugins::TemplateService)
10
+ .to receive(:default_templates_dir).and_return(templates_dir)
11
+
12
+ stub_content_service
13
+
14
+ @importer = Dradis::Plugins::Qualys::WAS::Importer.new(
15
+ content_service: @content_service
16
+ )
17
+ end
18
+
19
+ let(:example_xml) { 'spec/fixtures/files/simple_was.xml' }
20
+ let(:run_import!) { @importer.import(file: example_xml) }
21
+
22
+ it 'creates nodes as needed' do
23
+ expect_to_create_node_with(label: 'example.com')
24
+ run_import!
25
+ end
26
+
27
+ it 'creates issues as needed' do
28
+ expect_to_create_issue_with(text: 'DNS Host Name')
29
+ run_import!
30
+ end
31
+
32
+ it 'creates evidence as needed' do
33
+ expect_to_create_evidence_with(
34
+ content: 'http://example.com',
35
+ issue: 'DNS Host Name',
36
+ node_label: 'example.com'
37
+ )
38
+ run_import!
39
+ end
40
+ end
41
+ end
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,10 @@ require 'nokogiri'
4
4
 
5
5
  require 'combustion'
6
6
 
7
+ Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
8
+
7
9
  Combustion.initialize!
8
10
 
9
11
  RSpec.configure do |config|
12
+ config.include SpecMacros
10
13
  end
@@ -0,0 +1,50 @@
1
+ module SpecMacros
2
+ extend ActiveSupport::Concern
3
+
4
+ def stub_content_service
5
+ # Init services
6
+ plugin = Dradis::Plugins::Qualys
7
+
8
+ @content_service = Dradis::Plugins::ContentService::Base.new(
9
+ logger: Logger.new(STDOUT),
10
+ plugin: plugin
11
+ )
12
+
13
+ # Stub dradis-plugins methods
14
+ #
15
+ # They return their argument hashes as objects mimicking
16
+ # Nodes, Issues, etc
17
+ allow(@content_service).to receive(:create_node) do |args|
18
+ obj = OpenStruct.new(args)
19
+ obj.define_singleton_method(:set_property) { |_, __| }
20
+ obj
21
+ end
22
+ allow(@content_service).to receive(:create_issue) do |args|
23
+ OpenStruct.new(args)
24
+ end
25
+ allow(@content_service).to receive(:create_evidence) do |args|
26
+ OpenStruct.new(args)
27
+ end
28
+ end
29
+
30
+ def expect_to_create_node_with(label:)
31
+ expect(@content_service).to receive(:create_node).with(
32
+ hash_including label: label
33
+ ).once
34
+ end
35
+
36
+ def expect_to_create_issue_with(text:)
37
+ expect(@content_service).to receive(:create_issue) do |args|
38
+ expect(args[:text]).to include text
39
+ OpenStruct.new(args)
40
+ end.once
41
+ end
42
+
43
+ def expect_to_create_evidence_with(content:, issue:, node_label:)
44
+ expect(@content_service).to receive(:create_evidence) do |args|
45
+ expect(args[:content]).to include content
46
+ expect(args[:issue].text).to include issue
47
+ expect(args[:node].label).to eq node_label
48
+ end.once
49
+ end
50
+ end
@@ -14,3 +14,4 @@ element.consequence
14
14
  element.solution
15
15
  element.compliance
16
16
  element.result
17
+ element.qualys_collection
@@ -33,3 +33,7 @@ Temporal: %element.cvss_temporal%
33
33
 
34
34
  #[CVEList]#
35
35
  %element.cve_id_list%
36
+
37
+
38
+ #[QualysCollection]#
39
+ %element.qualys_collection%
@@ -0,0 +1,6 @@
1
+ was-evidence.access_paths
2
+ was-evidence.ajax
3
+ was-evidence.authentication
4
+ was-evidence.ignored
5
+ was-evidence.potential
6
+ was-evidence.url
@@ -0,0 +1,44 @@
1
+ <VULNERABILITY>
2
+ <UNIQUE_ID>db9bd89e-a8d8-402d-a6ca-8f6ff8be426f</UNIQUE_ID>
3
+ <ID>827065910</ID>
4
+ <DETECTION_ID>20879664</DETECTION_ID>
5
+ <QID>150124</QID>
6
+ <URL>http://demo.hackmebank.net/index.jsp?content=personal_loans.htm</URL>
7
+ <ACCESS_PATH>
8
+ <URL>http://demo.hackmebank.net/index.jsp</URL>
9
+ </ACCESS_PATH>
10
+ <AJAX>false</AJAX>
11
+ <AUTHENTICATION>Not Required</AUTHENTICATION>
12
+ <DETECTION_DATE>11 Oct 2021 07:16PM GMT-0500</DETECTION_DATE>
13
+ <POTENTIAL>false</POTENTIAL>
14
+ <PAYLOADS>
15
+ <PAYLOAD>
16
+ <NUM>1</NUM>
17
+ <PAYLOAD>N/A</PAYLOAD>
18
+ <REQUEST>
19
+ <METHOD>GET</METHOD>
20
+ <URL>http://demo.hackmebank.net/index.jsp?content=business.htm</URL>
21
+ <HEADERS>
22
+ <HEADER>
23
+ <key>Host</key>
24
+ <value><![CDATA[ demo.hackmebank.net
25
+ </HEADER>
26
+ <HEADER>
27
+ <key>User-Agent</key>
28
+ <value><![CDATA[ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Safari/605.1.15
29
+ </HEADER>
30
+ <HEADER>
31
+ <key>Accept</key>
32
+ <value><![CDATA[ */*
33
+ </HEADER>
34
+ </HEADERS>
35
+ <BODY></BODY>
36
+ </REQUEST>
37
+ <RESPONSE>
38
+ <CONTENTS base64="true"><![CDATA[VGhlIFVSSSB3YXMgZnJhbWVkLgo=
39
+ ]]></CONTENTS>
40
+ </RESPONSE>
41
+ </PAYLOAD>
42
+ </PAYLOADS>
43
+ <IGNORED>false</IGNORED>
44
+ </VULNERABILITY>
@@ -0,0 +1,11 @@
1
+ #[Location]#
2
+ %was-evidence.url%
3
+
4
+ #[AccessPaths]#
5
+ %was-evidence.access_paths%
6
+
7
+ #[Flags]#
8
+ Ajax: %was-evidence.ajax%
9
+ Authentication: %was-evidence.authentication%
10
+ Ignored: %was-evidence.ignored%
11
+ Potential: %was-evidence.potential%
@@ -0,0 +1,16 @@
1
+ was-issue.category
2
+ was-issue.cvss_base
3
+ was-issue.cvss_temporal
4
+ was-issue.cvss3_base
5
+ was-issue.cvss3_temporal
6
+ was-issue.cvss3_vector
7
+ was-issue.cwe
8
+ was-issue.description
9
+ was-issue.group
10
+ was-issue.impact
11
+ was-issue.owasp
12
+ was-issue.qid
13
+ was-issue.severity
14
+ was-issue.solution
15
+ was-issue.title
16
+ was-issue.wasc
@@ -0,0 +1,24 @@
1
+ <QID>
2
+ <QID>150001</QID>
3
+ <CATEGORY>Confirmed Vulnerability</CATEGORY>
4
+ <SEVERITY>5</SEVERITY>
5
+ <TITLE>Reflected Cross-Site Scripting (XSS) Vulnerabilities</TITLE>
6
+ <GROUP>XSS</GROUP>
7
+ <OWASP>A7</OWASP>
8
+ <WASC>WASC-8</WASC>
9
+ <CWE>CWE-79</CWE>
10
+ <CVSS_BASE>4.3</CVSS_BASE>
11
+ <CVSS_TEMPORAL>3.9</CVSS_TEMPORAL>
12
+ <CVSS_V3>
13
+ <BASE>6.1</BASE>
14
+ <TEMPORAL>5.8</TEMPORAL>
15
+ <ATTACK_VECTOR>Network</ATTACK_VECTOR>
16
+ </CVSS_V3>
17
+ <DESCRIPTION><![CDATA[XSS vulnerabilities occur when the Web application echoes user-supplied data in an HTML response sent to the Web browser. For example, a Web application might include the user's name as part of a welcome message or display a home address when confirming a shipping destination. If the user-supplied data contain characters that are interpreted as part of an HTML element instead of literal text, then an attacker can modify the HTML that is received by the victim's Web browser.
18
+ <P>
19
+ The XSS payload is echoed in HTML document returned by the request. An XSS payload may consist of HTML, JavaScript or other content that will be rendered by the browser. In order to exploit this vulnerability, a malicious user would need to trick a victim into visiting the URL with the XSS payload.]]></DESCRIPTION>
20
+ <IMPACT>XSS exploits pose a significant threat to a Web application, its users and user data. XSS exploits target the users of a Web application rather than the Web application itself. An exploit can lead to theft of the user's credentials and personal or financial information. Complex exploits and attack scenarios are possible via XSS because it enables an attacker to execute dynamic code. Consequently, any capability or feature available to the Web browser (for example HTML, JavaScript, Flash and Java applets) can be used to as a part of a compromise.</IMPACT>
21
+ <SOLUTION><![CDATA[Filter all data collected from the client including user-supplied content and browser content such as Referrer and User-Agent headers.
22
+ <P>
23
+ Any data collected from the client and displayed in a Web page should be HTML-encoded to ensure the content is rendered as text instead of an HTML element or JavaScript.]]></SOLUTION>
24
+ </QID>
@@ -0,0 +1,28 @@
1
+ #[Title]#
2
+ %was-issue.title%
3
+
4
+ #[Severity]#
5
+ %was-issue.severity%
6
+
7
+ #[Categories]#
8
+ Category: %was-issue.category%
9
+ Group: %was-issue.group%
10
+ OWASP: %was-issue.owasp%
11
+ CWE: %was-issue.cwe%
12
+
13
+ #[CVSSv3.Vector]#
14
+ %was-issue.cvss3_vector%
15
+
16
+ #[CVSSv3.BaseScore]#
17
+ %was-issue.cvss3_base%
18
+
19
+ #[CVSSv3.TemporalScore]#
20
+ %was-issue.cvss3_temporal%
21
+
22
+ #[Description]#
23
+ %was-issue.description%
24
+
25
+ %was-issue.impact%
26
+
27
+ #[Solution]#
28
+ %was-issue.solution%
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dradis-qualys
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Martin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-18 00:00:00.000000000 Z
11
+ date: 2022-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dradis-plugins
@@ -119,23 +119,35 @@ files:
119
119
  - lib/dradis/plugins/qualys/engine.rb
120
120
  - lib/dradis/plugins/qualys/field_processor.rb
121
121
  - lib/dradis/plugins/qualys/gem_version.rb
122
- - lib/dradis/plugins/qualys/importer.rb
123
122
  - lib/dradis/plugins/qualys/version.rb
123
+ - lib/dradis/plugins/qualys/vuln/importer.rb
124
+ - lib/dradis/plugins/qualys/was/importer.rb
124
125
  - lib/qualys/element.rb
126
+ - lib/qualys/was/qid.rb
127
+ - lib/qualys/was/vulnerability.rb
125
128
  - lib/tasks/thorfile.rb
126
129
  - spec/.keep
127
130
  - spec/fixtures/files/no_result.xml
128
131
  - spec/fixtures/files/simple.xml
132
+ - spec/fixtures/files/simple_was.xml
129
133
  - spec/fixtures/files/two_hosts_common_issue.xml
130
134
  - spec/qualys/element_spec.rb
131
- - spec/qualys/importer_spec.rb
135
+ - spec/qualys/vuln/importer_spec.rb
136
+ - spec/qualys/was/importer_spec.rb
132
137
  - spec/spec_helper.rb
138
+ - spec/support/spec_macros.rb
133
139
  - templates/element.fields
134
140
  - templates/element.sample
135
141
  - templates/element.template
136
142
  - templates/evidence.fields
137
143
  - templates/evidence.sample
138
144
  - templates/evidence.template
145
+ - templates/was-evidence.fields
146
+ - templates/was-evidence.sample
147
+ - templates/was-evidence.template
148
+ - templates/was-issue.fields
149
+ - templates/was-issue.sample
150
+ - templates/was-issue.template
139
151
  homepage: http://dradisframework.org
140
152
  licenses:
141
153
  - GPL-2
@@ -155,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
167
  - !ruby/object:Gem::Version
156
168
  version: '0'
157
169
  requirements: []
158
- rubygems_version: 3.1.6
170
+ rubygems_version: 3.1.4
159
171
  signing_key:
160
172
  specification_version: 4
161
173
  summary: Qualys add-on for the Dradis Framework.
@@ -163,7 +175,10 @@ test_files:
163
175
  - spec/.keep
164
176
  - spec/fixtures/files/no_result.xml
165
177
  - spec/fixtures/files/simple.xml
178
+ - spec/fixtures/files/simple_was.xml
166
179
  - spec/fixtures/files/two_hosts_common_issue.xml
167
180
  - spec/qualys/element_spec.rb
168
- - spec/qualys/importer_spec.rb
181
+ - spec/qualys/vuln/importer_spec.rb
182
+ - spec/qualys/was/importer_spec.rb
169
183
  - spec/spec_helper.rb
184
+ - spec/support/spec_macros.rb
@@ -1,88 +0,0 @@
1
- module Dradis::Plugins::Qualys
2
- class Importer < Dradis::Plugins::Upload::Importer
3
-
4
- attr_accessor :host_node
5
-
6
- # The framework will call this function if the user selects this plugin from
7
- # the dropdown list and uploads a file.
8
- # @returns true if the operation was successful, false otherwise
9
- def import(params={})
10
- file_content = File.read( params[:file] )
11
-
12
- logger.info{'Parsing Qualys output file...'}
13
- @doc = Nokogiri::XML( file_content )
14
- logger.info{'Done.'}
15
-
16
- if @doc.root.name != 'SCAN'
17
- error = "No scan results were detected in the uploaded file. Ensure you uploaded a Qualys XML file."
18
- logger.fatal{ error }
19
- content_service.create_note text: error
20
- return false
21
- end
22
-
23
- @doc.xpath('SCAN/IP').each do |xml_host|
24
- process_ip(xml_host)
25
- end
26
-
27
- return true
28
- end
29
-
30
- private
31
-
32
- def process_ip(xml_host)
33
- host_ip = xml_host['value']
34
- logger.info{ "Host: %s" % host_ip }
35
-
36
- self.host_node = content_service.create_node(label: host_ip, type: :host)
37
-
38
- host_node.set_property(:ip, host_ip)
39
- host_node.set_property(:hostname, xml_host['name'])
40
- if (xml_os = xml_host.xpath('OS')) && xml_os.any?
41
- host_node.set_property(:os, xml_os.text)
42
- end
43
- host_node.save
44
-
45
- # We treat INFOS, SERVICES, PRACTICES, and VULNS the same way
46
- # All of these are imported into Dradis as Issues
47
- ['INFOS', 'SERVICES', 'PRACTICES', 'VULNS'].each do |collection|
48
- xml_host.xpath(collection).each do |xml_collection|
49
- process_collection(collection, xml_collection)
50
- end
51
- end
52
- end
53
-
54
- def process_collection(collection, xml_collection)
55
- xml_cats = xml_collection.xpath('CAT')
56
-
57
- xml_cats.each do |xml_cat|
58
- logger.info{ "\t#{ collection } - #{ xml_cat['value'] }" }
59
-
60
- empty_dup_xml_cat = xml_cat.dup
61
- empty_dup_xml_cat.children.remove
62
-
63
- # For each INFOS/CAT/INFO, SERVICES/CAT/SERVICE, VULNS/CAT/VULN, etc.
64
- xml_cat.xpath(collection.chop).each do |xml_element|
65
- dup_xml_cat = empty_dup_xml_cat.dup
66
- dup_xml_cat.add_child(xml_element.dup)
67
- cat_number = xml_element[:number]
68
-
69
- process_vuln(collection, cat_number, dup_xml_cat)
70
-
71
- end
72
- end
73
- end
74
-
75
- # Takes a <CAT> element containing a single <VULN> element and processes an
76
- # Issue and Evidence template out of it.
77
- def process_vuln(collection, vuln_number, xml_cat)
78
- logger.info{ "\t\t => Creating new issue (plugin_id: #{ vuln_number })" }
79
- issue_text = template_service.process_template(template: 'element', data: xml_cat)
80
- issue_text << "\n\n#[qualys_collection]#\n#{ collection }"
81
- issue = content_service.create_issue(text: issue_text, id: vuln_number)
82
-
83
- logger.info{ "\t\t => Creating new evidence" }
84
- evidence_content = template_service.process_template(template: 'evidence', data: xml_cat)
85
- content_service.create_evidence(issue: issue, node: self.host_node, content: evidence_content)
86
- end
87
- end
88
- end