dradis-acunetix 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +3 -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 +165 -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 +62 -0
- data/lib/dradis/plugins/acunetix/version.rb +13 -0
- data/lib/tasks/thorfile.rb +25 -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 +171 -0
@@ -0,0 +1,165 @@
|
|
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
|
136
|
+
end
|
137
|
+
|
138
|
+
def cleanup_decimals(source)
|
139
|
+
result = source.dup
|
140
|
+
result.gsub!(/([0-9])\,([0-9])/, '\1.\2')
|
141
|
+
result
|
142
|
+
end
|
143
|
+
|
144
|
+
def references_list
|
145
|
+
references = ''
|
146
|
+
xml.xpath('./References/Reference').each do |xml_reference|
|
147
|
+
references << xml_reference.at_xpath('./Database').text()
|
148
|
+
references << "\n"
|
149
|
+
references << xml_reference.at_xpath('./URL').text()
|
150
|
+
references << "\n\n"
|
151
|
+
end
|
152
|
+
references
|
153
|
+
end
|
154
|
+
|
155
|
+
# Some of the values have embedded HTML conent that we need to strip
|
156
|
+
def tags_with_html_content
|
157
|
+
[:details, :description, :detailed_information, :impact, :recommendation]
|
158
|
+
end
|
159
|
+
|
160
|
+
def tags_with_commas
|
161
|
+
[:cvss3_score, :cvss3_tempscore, :cvss3_envscore]
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
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 = 6
|
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,62 @@
|
|
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
|
+
|
34
|
+
start_url = URI::parse(xml_scan.at_xpath('./StartURL').text()).host
|
35
|
+
|
36
|
+
self.scan_node = content_service.create_node(label: start_url, type: :host)
|
37
|
+
logger.info{ "\tScan start URL: #{start_url}" }
|
38
|
+
|
39
|
+
scan_note = template_service.process_template(template: 'scan', data: xml_scan)
|
40
|
+
content_service.create_note text: scan_note, node: scan_node
|
41
|
+
|
42
|
+
xml_scan.xpath('./ReportItems/ReportItem').each do |xml_report_item|
|
43
|
+
process_report_item(xml_report_item)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_report_item(xml_report_item)
|
48
|
+
plugin_id = "%s/%s" % [
|
49
|
+
xml_report_item.at_xpath('./ModuleName').text(),
|
50
|
+
xml_report_item.at_xpath('./Name').text()
|
51
|
+
]
|
52
|
+
logger.info{ "\t\t => Creating new issue (plugin_id: #{plugin_id})" }
|
53
|
+
|
54
|
+
issue_text = template_service.process_template(template: 'report_item', data: xml_report_item)
|
55
|
+
issue = content_service.create_issue(text: issue_text, id: plugin_id)
|
56
|
+
|
57
|
+
logger.info{ "\t\t => Creating new evidence" }
|
58
|
+
evidence_content = template_service.process_template(template: 'evidence', data: xml_report_item)
|
59
|
+
content_service.create_evidence(issue: issue, node: scan_node, content: evidence_content)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class AcunetixTasks < Thor
|
2
|
+
include Rails.application.config.dradis.thor_helper_module
|
3
|
+
|
4
|
+
namespace "dradis:plugins:acunetix"
|
5
|
+
|
6
|
+
desc "upload FILE", "upload Acunetix XML results"
|
7
|
+
def upload(file_path)
|
8
|
+
require 'config/environment'
|
9
|
+
|
10
|
+
logger = Logger.new(STDOUT)
|
11
|
+
logger.level = Logger::DEBUG
|
12
|
+
|
13
|
+
unless File.exists?(file_path)
|
14
|
+
$stderr.puts "** the file [#{file_path}] does not exist"
|
15
|
+
exit -1
|
16
|
+
end
|
17
|
+
|
18
|
+
detect_and_set_project_scope
|
19
|
+
importer = Dradis::Plugins::Acunetix::Importer.new(logger: logger)
|
20
|
+
importer.import(file: file_path)
|
21
|
+
|
22
|
+
logger.close
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Dradis::Plugins
|
5
|
+
describe 'Acunetix upload plugin' do
|
6
|
+
before(:each) do
|
7
|
+
# Stub template service
|
8
|
+
templates_dir = File.expand_path('../../templates', __FILE__)
|
9
|
+
allow_any_instance_of(TemplateService).to \
|
10
|
+
receive(:default_templates_dir).and_return(templates_dir)
|
11
|
+
|
12
|
+
@content_service = ContentService.new(plugin: Acunetix)
|
13
|
+
template_service = TemplateService.new(plugin: Acunetix)
|
14
|
+
|
15
|
+
@importer = Acunetix::Importer.new(
|
16
|
+
content_service: @content_service,
|
17
|
+
template_service: template_service
|
18
|
+
)
|
19
|
+
|
20
|
+
# Stub dradis-plugins methods
|
21
|
+
#
|
22
|
+
# They return their argument hashes as objects mimicking
|
23
|
+
# Nodes, Issues, etc
|
24
|
+
allow(@content_service).to receive(:create_node) do |args|
|
25
|
+
OpenStruct.new(args)
|
26
|
+
end
|
27
|
+
allow(@content_service).to receive(:create_note) do |args|
|
28
|
+
OpenStruct.new(args)
|
29
|
+
end
|
30
|
+
allow(@content_service).to receive(:create_issue) do |args|
|
31
|
+
OpenStruct.new(args)
|
32
|
+
end
|
33
|
+
allow(@content_service).to receive(:create_evidence) do |args|
|
34
|
+
OpenStruct.new(args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "creates nodes, issues, notes and an evidences as needed" do
|
39
|
+
|
40
|
+
expect(@content_service).to receive(:create_node).with(hash_including label: "testphp.vulnweb.com", type: :host).once
|
41
|
+
expect(@content_service).to receive(:create_note) do |args|
|
42
|
+
expect(args[:text]).to include("#[Title]#\nAcunetix scanner notes (7/10/2014, 11:56:03)")
|
43
|
+
expect(args[:node].label).to eq("testphp.vulnweb.com")
|
44
|
+
end.once
|
45
|
+
|
46
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
47
|
+
expect(args[:text]).to include("#[Title]#\nHTML form without CSRF protection")
|
48
|
+
expect(args[:id]).to eq("Crawler/HTML form without CSRF protection")
|
49
|
+
OpenStruct.new(args)
|
50
|
+
end.once
|
51
|
+
|
52
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
53
|
+
expect(args[:text]).to include("#[Title]#\nClickjacking: X-Frame-Options header missing")
|
54
|
+
expect(args[:id]).to eq("Scripting (Clickjacking_X_Frame_Options.script)/Clickjacking: X-Frame-Options header missing")
|
55
|
+
OpenStruct.new(args)
|
56
|
+
end.once
|
57
|
+
|
58
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
59
|
+
expect(args[:content]).to include("/")
|
60
|
+
expect(args[:issue].id).to eq("Crawler/HTML form without CSRF protection")
|
61
|
+
expect(args[:node].label).to eq("testphp.vulnweb.com")
|
62
|
+
end.once
|
63
|
+
|
64
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
65
|
+
expect(args[:content]).to include("Web Server")
|
66
|
+
expect(args[:issue].id).to eq("Scripting (Clickjacking_X_Frame_Options.script)/Clickjacking: X-Frame-Options header missing")
|
67
|
+
expect(args[:node].label).to eq("testphp.vulnweb.com")
|
68
|
+
end.once
|
69
|
+
|
70
|
+
@importer.import(file: 'spec/fixtures/files/simple.acunetix.xml')
|
71
|
+
end
|
72
|
+
|
73
|
+
# Regression test for github.com/dradis/dradis-nexpose/issues/1
|
74
|
+
describe "Source HTML parsing" do
|
75
|
+
it "identifies code/pre blocks and replaces them with the Textile equivalent" do
|
76
|
+
|
77
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
78
|
+
expect(args[:text]).to include("#[Title]#\nSQL injection (verified)")
|
79
|
+
expect(args[:text]).not_to include("<code>")
|
80
|
+
expect(args[:text]).not_to include("<pre")
|
81
|
+
expect(args[:id]).to eq("Scripting (Sql_Injection.script)/SQL injection (verified)")
|
82
|
+
OpenStruct.new(args)
|
83
|
+
end.once
|
84
|
+
|
85
|
+
# expect(@content_service).to receive(:create_evidence) do |args|
|
86
|
+
# expect(args[:content]).to include("Web Server")
|
87
|
+
# expect(args[:issue].id).to eq("Scripting (Clickjacking_X_Frame_Options.script)")
|
88
|
+
# expect(args[:node].label).to eq("testphp.vulnweb.com")
|
89
|
+
# end.once
|
90
|
+
|
91
|
+
@importer.import(file: 'spec/fixtures/files/code-pre.acunetix.xml')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Regression test to make sure that commas are replaced with decimals in the CVSSv3 scores
|
96
|
+
describe "CVSS clean up decimals" do
|
97
|
+
it "identifies commas used as decimals in CVSSv3 scores and replaces them with periods" do
|
98
|
+
|
99
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
100
|
+
expect(args[:text]).to include("#[CVSS3Score]#\n5.3")
|
101
|
+
OpenStruct.new(args)
|
102
|
+
end
|
103
|
+
|
104
|
+
@importer.import(file: 'spec/fixtures/files/commas-format.acunetix.xml')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|