dradis-acunetix 3.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/issue_template.md +16 -0
- data/.github/pull_request_template.md +36 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +63 -0
- data/CONTRIBUTING.md +3 -0
- data/Gemfile +23 -0
- data/LICENSE +339 -0
- data/README.md +27 -0
- data/Rakefile +2 -0
- data/dradis-acunetix.gemspec +35 -0
- data/lib/acunetix/report_item.rb +168 -0
- data/lib/acunetix/scan.rb +93 -0
- data/lib/dradis-acunetix.rb +9 -0
- data/lib/dradis/plugins/acunetix.rb +12 -0
- data/lib/dradis/plugins/acunetix/engine.rb +9 -0
- data/lib/dradis/plugins/acunetix/field_processor.rb +25 -0
- data/lib/dradis/plugins/acunetix/gem_version.rb +19 -0
- data/lib/dradis/plugins/acunetix/importer.rb +78 -0
- data/lib/dradis/plugins/acunetix/version.rb +13 -0
- data/lib/tasks/thorfile.rb +20 -0
- data/spec/dradis-acunetix_spec.rb +109 -0
- data/spec/fixtures/files/code-pre.acunetix.xml +1732 -0
- data/spec/fixtures/files/commas-format.acunetix.xml +105 -0
- data/spec/fixtures/files/simple.acunetix.xml +1691 -0
- data/spec/fixtures/files/testphp.vulnweb.com.export.acunetix.xml +23976 -0
- data/spec/models/acunetix/scan_spec.rb +74 -0
- data/spec/spec_helper.rb +10 -0
- data/templates/evidence.fields +7 -0
- data/templates/evidence.sample +12 -0
- data/templates/evidence.template +16 -0
- data/templates/report_item.fields +18 -0
- data/templates/report_item.sample +151 -0
- data/templates/report_item.template +59 -0
- data/templates/scan.fields +12 -0
- data/templates/scan.sample +15 -0
- data/templates/scan.template +16 -0
- metadata +172 -0
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Acunetix add-on for Dradis
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/dradis/dradis-acunetix.png?branch=master)](http://travis-ci.org/dradis/dradis-acunetix) [![Code Climate](https://codeclimate.com/github/dradis/dradis-acunetix.png)](https://codeclimate.com/github/dradis/dradis-acunetix.png)
|
4
|
+
|
5
|
+
The Acunetix add-on enables users to upload Acunexit XML files to create a structure of nodes/notes that contain the same information about the hosts/ports/services as the original file.
|
6
|
+
|
7
|
+
The add-on requires [Dradis CE](https://dradisframework.org/) > 3.0, or [Dradis Pro](https://dradisframework.com/pro/).
|
8
|
+
|
9
|
+
|
10
|
+
## More information
|
11
|
+
|
12
|
+
See the Dradis Framework's [README.md](https://github.com/dradis/dradisframework/blob/master/README.md)
|
13
|
+
|
14
|
+
|
15
|
+
## Contributing
|
16
|
+
|
17
|
+
See the Dradis Framework's [CONTRIBUTING.md](https://github.com/dradis/dradisframework/blob/master/CONTRIBUTING.md)
|
18
|
+
|
19
|
+
|
20
|
+
## License
|
21
|
+
|
22
|
+
Dradis Framework and all its components are released under [GNU General Public License version 2.0](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.
|
23
|
+
|
24
|
+
|
25
|
+
## Feature requests and bugs
|
26
|
+
|
27
|
+
Please use the [Dradis Framework issue tracker](https://github.com/dradis/dradis-ce/issues) for add-on improvements and bug reports.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
$:.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'dradis/plugins/acunetix/version'
|
3
|
+
version = Dradis::Plugins::Acunetix::VERSION::STRING
|
4
|
+
|
5
|
+
|
6
|
+
# Describe your gem and declare its dependencies:
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.name = 'dradis-acunetix'
|
10
|
+
spec.version = version
|
11
|
+
spec.summary = 'Acunetix add-on for the Dradis Framework.'
|
12
|
+
spec.description = 'This add-on allows you to upload and parse output produced from Acunetix Web Vulnerability Scanner into Dradis.'
|
13
|
+
|
14
|
+
spec.license = 'GPL-2'
|
15
|
+
|
16
|
+
spec.authors = ['Daniel Martin']
|
17
|
+
spec.email = ['etd@nomejortu.com']
|
18
|
+
spec.homepage = 'http://dradisframework.org'
|
19
|
+
|
20
|
+
spec.files = `git ls-files`.split($\)
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
|
24
|
+
# By not including Rails as a dependency, we can use the gem with different
|
25
|
+
# versions of Rails (a sure recipe for disaster, I'm sure), which is needed
|
26
|
+
# until we bump Dradis Pro to 4.1.
|
27
|
+
# s.add_dependency 'rails', '~> 4.1.1'
|
28
|
+
spec.add_dependency 'dradis-plugins', '~> 3.6'
|
29
|
+
spec.add_dependency 'nokogiri', '~> 1.3'
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
32
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
33
|
+
spec.add_development_dependency 'rspec-rails'
|
34
|
+
spec.add_development_dependency 'combustion', '~> 0.5.2'
|
35
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module Acunetix
|
2
|
+
# This class represents each of the /ScanGroup/Scan/ReportItems/ReportItem
|
3
|
+
# elements in the Acunetix 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 ReportItem
|
11
|
+
attr_accessor :xml
|
12
|
+
|
13
|
+
# Accepts an XML node from Nokogiri::XML.
|
14
|
+
def initialize(xml_node)
|
15
|
+
@xml = xml_node
|
16
|
+
end
|
17
|
+
|
18
|
+
# List of supported tags. They can be attributes, simple descendans or
|
19
|
+
# collections.
|
20
|
+
def supported_tags
|
21
|
+
[
|
22
|
+
# attributes
|
23
|
+
# :color
|
24
|
+
|
25
|
+
# simple tags
|
26
|
+
:name, :module_name, :severity, :type, :impact, :description,
|
27
|
+
:detailed_information, :recommendation, :cwe,
|
28
|
+
|
29
|
+
# tags that correspond to Evidence
|
30
|
+
:details, :affects, :parameter, :aop_source_file, :aop_source_line,
|
31
|
+
:aop_additional, :is_false_positive,
|
32
|
+
|
33
|
+
|
34
|
+
# nested tags
|
35
|
+
:request, :response,
|
36
|
+
:cvss_descriptor, :cvss_score,
|
37
|
+
:cvss3_descriptor, :cvss3_score, :cvss3_tempscore, :cvss3_envscore,
|
38
|
+
|
39
|
+
# multiple tags
|
40
|
+
:cve_list, :references
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
# This allows external callers (and specs) to check for implemented
|
45
|
+
# properties
|
46
|
+
def respond_to?(method, include_private=false)
|
47
|
+
return true if supported_tags.include?(method.to_sym)
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
# This method is invoked by Ruby when a method that is not defined in this
|
52
|
+
# instance is called.
|
53
|
+
#
|
54
|
+
# In our case we inspect the @method@ parameter and try to find the
|
55
|
+
# attribute, simple descendent or collection that it maps to in the XML
|
56
|
+
# tree.
|
57
|
+
def method_missing(method, *args)
|
58
|
+
|
59
|
+
# We could remove this check and return nil for any non-recognized tag.
|
60
|
+
# The problem would be that it would make tricky to debug problems with
|
61
|
+
# typos. For instance: <>.potr would return nil instead of raising an
|
62
|
+
# exception
|
63
|
+
unless supported_tags.include?(method)
|
64
|
+
super
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
# Any fields where a simple .camelcase() won't work we need to translate,
|
69
|
+
# this includes acronyms (e.g. :cwe would become 'Cwe') and simple nested
|
70
|
+
# tags.
|
71
|
+
translations_table = {
|
72
|
+
cwe: 'CWE',
|
73
|
+
aop_source_file: 'AOPSourceFile',
|
74
|
+
aop_source_line: 'AOPSourceLine',
|
75
|
+
aop_additional: 'AOPAdditional',
|
76
|
+
request: 'TechnicalDetails/Request',
|
77
|
+
response: 'TechnicalDetails/Response',
|
78
|
+
cvss_descriptor: 'CVSS/Descriptor',
|
79
|
+
cvss_score: 'CVSS/Score',
|
80
|
+
cvss3_descriptor: 'CVSS3/Descriptor',
|
81
|
+
cvss3_score: 'CVSS3/Score',
|
82
|
+
cvss3_tempscore: 'CVSS3/TempScore',
|
83
|
+
cvss3_envscore: 'CVSS3/EnvScore'
|
84
|
+
}
|
85
|
+
method_name = translations_table.fetch(method, method.to_s.camelcase)
|
86
|
+
# first we try the attributes:
|
87
|
+
# return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)
|
88
|
+
|
89
|
+
|
90
|
+
# There is a ./References tag, but we want to short-circuit that one to
|
91
|
+
# do custom processing.
|
92
|
+
return references_list() if method == :references
|
93
|
+
|
94
|
+
# then we try the children tags
|
95
|
+
tag = xml.at_xpath("./#{method_name}")
|
96
|
+
if tag && !tag.text.blank?
|
97
|
+
if tags_with_html_content.include?(method)
|
98
|
+
return cleanup_html(tag.text)
|
99
|
+
elsif tags_with_commas.include?(method)
|
100
|
+
return cleanup_decimals(tag.text)
|
101
|
+
else
|
102
|
+
return tag.text
|
103
|
+
end
|
104
|
+
else
|
105
|
+
'n/a'
|
106
|
+
end
|
107
|
+
|
108
|
+
return 'unimplemented' if method == :cve_list
|
109
|
+
|
110
|
+
# nothing found
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def cleanup_html(source)
|
117
|
+
result = source.dup
|
118
|
+
result.gsub!(/"/, '"')
|
119
|
+
result.gsub!(/&/, '&')
|
120
|
+
result.gsub!(/</, '<')
|
121
|
+
result.gsub!(/>/, '>')
|
122
|
+
|
123
|
+
result.gsub!(/<b>(.*?)<\/b>/) { "*#{$1.strip}*" }
|
124
|
+
result.gsub!(/<br\/>/, "\n")
|
125
|
+
result.gsub!(/<font.*?>(.*?)<\/font>/m, '\1')
|
126
|
+
result.gsub!(/<h2>(.*?)<\/h2>/) { "*#{$1.strip}*" }
|
127
|
+
result.gsub!(/<i>(.*?)<\/i>/, '\1')
|
128
|
+
result.gsub!(/<p>(.*?)<\/p>/, '\1')
|
129
|
+
result.gsub!(/<code><pre.*?>(.*?)<\/pre><\/code>/m){|m| "\n\nbc.. #{$1.strip}\n\np. \n" }
|
130
|
+
result.gsub!(/<pre.*?>(.*?)<\/pre>/m){|m| "\n\nbc.. #{$1.strip}\n\np. \n" }
|
131
|
+
result.gsub!(/<ul>(.*?)<\/ul>/m){"#{$1.strip}\n"}
|
132
|
+
|
133
|
+
result.gsub!(/<li>(.*?)<\/li>/){"\n* #{$1.strip}"}
|
134
|
+
|
135
|
+
result.gsub!(/<strong>(.*?)<\/strong>/) { "*#{$1.strip}*" }
|
136
|
+
result.gsub!(/<span.*?>(.*?)<\/span>/m){"#{$1.strip}\n"}
|
137
|
+
|
138
|
+
result
|
139
|
+
end
|
140
|
+
|
141
|
+
def cleanup_decimals(source)
|
142
|
+
result = source.dup
|
143
|
+
result.gsub!(/([0-9])\,([0-9])/, '\1.\2')
|
144
|
+
result
|
145
|
+
end
|
146
|
+
|
147
|
+
def references_list
|
148
|
+
references = ''
|
149
|
+
xml.xpath('./References/Reference').each do |xml_reference|
|
150
|
+
references << xml_reference.at_xpath('./Database').text()
|
151
|
+
references << "\n"
|
152
|
+
references << xml_reference.at_xpath('./URL').text()
|
153
|
+
references << "\n\n"
|
154
|
+
end
|
155
|
+
references
|
156
|
+
end
|
157
|
+
|
158
|
+
# Some of the values have embedded HTML conent that we need to strip
|
159
|
+
def tags_with_html_content
|
160
|
+
[:details, :description, :detailed_information, :impact, :recommendation]
|
161
|
+
end
|
162
|
+
|
163
|
+
def tags_with_commas
|
164
|
+
[:cvss3_score, :cvss3_tempscore, :cvss3_envscore]
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Acunetix
|
2
|
+
# This class represents each of the /ScanGroup/Scan elements in the Acunetix
|
3
|
+
# 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 Scan
|
11
|
+
attr_accessor :xml
|
12
|
+
# Accepts an XML node from Nokogiri::XML.
|
13
|
+
def initialize(xml_node)
|
14
|
+
@xml = xml_node
|
15
|
+
unless @xml.name == "Scan"
|
16
|
+
raise "Invalid XML; root node must be called 'Scan'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# List of supported tags. They are all descendents of the ./Scan node.
|
21
|
+
SUPPORTED_TAGS = [
|
22
|
+
# attributes
|
23
|
+
|
24
|
+
# simple tags
|
25
|
+
:name, :short_name, :start_url, :start_time, :finish_time, :scan_time,
|
26
|
+
:aborted, :responsive, :banner, :os, :web_server, :technologies, :ip,
|
27
|
+
:fqdn, :operating_system, :mac_address, :netbios_name, :scan_start_time,
|
28
|
+
:scan_stop_time
|
29
|
+
]
|
30
|
+
|
31
|
+
# This allows external callers (and specs) to check for implemented
|
32
|
+
# properties
|
33
|
+
def respond_to?(method, include_private=false)
|
34
|
+
return true if SUPPORTED_TAGS.include?(method.to_sym)
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
# This method is invoked by Ruby when a method that is not defined in this
|
39
|
+
# instance is called.
|
40
|
+
#
|
41
|
+
# In our case we inspect the @method@ parameter and try to find the
|
42
|
+
# corresponding <tag/> element inside the ./Scan child.
|
43
|
+
def method_missing(method, *args)
|
44
|
+
# We could remove this check and return nil for any non-recognized tag.
|
45
|
+
# The problem would be that it would make tricky to debug problems with
|
46
|
+
# typos. For instance: <>.potr would return nil instead of raising an
|
47
|
+
# exception
|
48
|
+
super and return unless SUPPORTED_TAGS.include?(method)
|
49
|
+
|
50
|
+
if tag = xml.at_xpath("./#{tag_name_for_method(method)}")
|
51
|
+
tag.text
|
52
|
+
else
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def report_items
|
59
|
+
@xml.xpath('./ReportItems/ReportItem')
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def service
|
64
|
+
"port #{start_url_port}, #{banner}"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def start_url_host
|
69
|
+
start_uri.host
|
70
|
+
end
|
71
|
+
alias_method :hostname, :start_url_host
|
72
|
+
|
73
|
+
|
74
|
+
def start_url_port
|
75
|
+
start_uri.port
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def start_uri
|
81
|
+
@start_uri ||= URI::parse(start_url)
|
82
|
+
end
|
83
|
+
|
84
|
+
def tag_name_for_method(method)
|
85
|
+
# Any fields where a simple .camelcase() won't work we need to translate,
|
86
|
+
# this includes acronyms (e.g. :scan_url would become 'ScanUrl').
|
87
|
+
{
|
88
|
+
start_url: 'StartURL'
|
89
|
+
}[method] || method.to_s.camelcase
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Dradis
|
2
|
+
module Plugins
|
3
|
+
module Acunetix
|
4
|
+
end
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'dradis/plugins/acunetix/engine'
|
9
|
+
require 'dradis/plugins/acunetix/field_processor'
|
10
|
+
require 'dradis/plugins/acunetix/importer'
|
11
|
+
require 'dradis/plugins/acunetix/version'
|
12
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Dradis::Plugins::Acunetix
|
2
|
+
# This processor defers to ::Acunetix::Scan for the scan template and to
|
3
|
+
# ::Acunetix::ReportItem for the report_item and evidence templates.
|
4
|
+
class FieldProcessor < Dradis::Plugins::Upload::FieldProcessor
|
5
|
+
|
6
|
+
def post_initialize(args={})
|
7
|
+
if data.name == "Scan"
|
8
|
+
@acunetix_object = ::Acunetix::Scan.new(data)
|
9
|
+
else
|
10
|
+
@acunetix_object = ::Acunetix::ReportItem.new(data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def value(args={})
|
15
|
+
field = args[:field]
|
16
|
+
|
17
|
+
# fields in the template are of the form <foo>.<field>, where <foo>
|
18
|
+
# is common across all fields for a given template (and meaningless).
|
19
|
+
_, name = field.split('.')
|
20
|
+
|
21
|
+
@acunetix_object.try(name) || 'n/a'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Dradis
|
2
|
+
module Plugins
|
3
|
+
module Acunetix
|
4
|
+
# Returns the version of the currently loaded Acunetix as a <tt>Gem::Version</tt>
|
5
|
+
def self.gem_version
|
6
|
+
Gem::Version.new VERSION::STRING
|
7
|
+
end
|
8
|
+
|
9
|
+
module VERSION
|
10
|
+
MAJOR = 3
|
11
|
+
MINOR = 18
|
12
|
+
TINY = 0
|
13
|
+
PRE = nil
|
14
|
+
|
15
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Dradis::Plugins::Acunetix
|
2
|
+
class Importer < Dradis::Plugins::Upload::Importer
|
3
|
+
|
4
|
+
# The framework will call this function if the user selects this plugin from
|
5
|
+
# the dropdown list and uploads a file.
|
6
|
+
# @returns true if the operation was successful, false otherwise
|
7
|
+
def import(params={})
|
8
|
+
file_content = File.read( params.fetch(:file) )
|
9
|
+
|
10
|
+
logger.info{'Parsing Acunetix output file...'}
|
11
|
+
@doc = Nokogiri::XML( file_content )
|
12
|
+
logger.info{'Done.'}
|
13
|
+
|
14
|
+
if @doc.xpath('/ScanGroup/Scan').empty?
|
15
|
+
error = "No scan results were detected in the uploaded file (/ScanGroup/Scan). Ensure you uploaded an Acunetix XML report."
|
16
|
+
logger.fatal{ error }
|
17
|
+
content_service.create_note text: error
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
|
21
|
+
@doc.xpath('/ScanGroup/Scan').each do |xml_scan|
|
22
|
+
process_scan(xml_scan)
|
23
|
+
end
|
24
|
+
|
25
|
+
return true
|
26
|
+
end # /import
|
27
|
+
|
28
|
+
|
29
|
+
private
|
30
|
+
attr_accessor :scan_node
|
31
|
+
|
32
|
+
def process_scan(xml_scan)
|
33
|
+
url = xml_scan.at_xpath('./StartURL').text()
|
34
|
+
start_url = URI::parse(url).host || url # urls wo/ protocol returned nil
|
35
|
+
|
36
|
+
self.scan_node = content_service.create_node(label: start_url, type: :host)
|
37
|
+
logger.info{ "\tScan start URL: #{start_url}" }
|
38
|
+
|
39
|
+
# Define Node properties
|
40
|
+
if scan_node.respond_to?(:properties)
|
41
|
+
scan_node.set_property(:short_name, xml_scan.at_xpath('./ShortName').text() )
|
42
|
+
scan_node.set_property(:start_url, start_url)
|
43
|
+
scan_node.set_property(:start_time, xml_scan.at_xpath('./StartTime').text() )
|
44
|
+
scan_node.set_property(:finish_time, xml_scan.at_xpath('./FinishTime').text() )
|
45
|
+
scan_node.set_property(:scan_time, xml_scan.at_xpath('./ScanTime').text() )
|
46
|
+
scan_node.set_property(:aborted, xml_scan.at_xpath('./Aborted').text() )
|
47
|
+
scan_node.set_property(:responsive, xml_scan.at_xpath('./Responsive').text() )
|
48
|
+
scan_node.set_property(:banner, xml_scan.at_xpath('./Banner').text() )
|
49
|
+
scan_node.set_property(:os, xml_scan.at_xpath('./Os').text() )
|
50
|
+
scan_node.set_property(:web_server, xml_scan.at_xpath('./WebServer').text() )
|
51
|
+
scan_node.set_property(:technologies, xml_scan.at_xpath('./Technologies').text() )
|
52
|
+
scan_node.save
|
53
|
+
end
|
54
|
+
|
55
|
+
scan_note = template_service.process_template(template: 'scan', data: xml_scan)
|
56
|
+
content_service.create_note text: scan_note, node: scan_node
|
57
|
+
|
58
|
+
xml_scan.xpath('./ReportItems/ReportItem').each do |xml_report_item|
|
59
|
+
process_report_item(xml_report_item)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_report_item(xml_report_item)
|
64
|
+
plugin_id = "%s/%s" % [
|
65
|
+
xml_report_item.at_xpath('./ModuleName').text(),
|
66
|
+
xml_report_item.at_xpath('./Name').text()
|
67
|
+
]
|
68
|
+
logger.info{ "\t\t => Creating new issue (plugin_id: #{plugin_id})" }
|
69
|
+
|
70
|
+
issue_text = template_service.process_template(template: 'report_item', data: xml_report_item)
|
71
|
+
issue = content_service.create_issue(text: issue_text, id: plugin_id)
|
72
|
+
|
73
|
+
logger.info{ "\t\t => Creating new evidence" }
|
74
|
+
evidence_content = template_service.process_template(template: 'evidence', data: xml_report_item)
|
75
|
+
content_service.create_evidence(issue: issue, node: scan_node, content: evidence_content)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|