dradis-burp 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 +10 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +57 -0
- data/CONTRIBUTING.md +3 -0
- data/Gemfile +23 -0
- data/LICENSE +339 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/dradis-burp.gemspec +34 -0
- data/lib/burp/html/issue.rb +157 -0
- data/lib/burp/issue.rb +43 -0
- data/lib/burp/xml/issue.rb +127 -0
- data/lib/dradis-burp.rb +10 -0
- data/lib/dradis/plugins/burp.rb +12 -0
- data/lib/dradis/plugins/burp/engine.rb +25 -0
- data/lib/dradis/plugins/burp/field_processor.rb +27 -0
- data/lib/dradis/plugins/burp/gem_version.rb +19 -0
- data/lib/dradis/plugins/burp/html/importer.rb +144 -0
- data/lib/dradis/plugins/burp/version.rb +13 -0
- data/lib/dradis/plugins/burp/xml/importer.rb +144 -0
- data/lib/tasks/thorfile.rb +30 -0
- data/spec/burp_upload_spec.rb +220 -0
- data/spec/fixtures/files/burp.html +229 -0
- data/spec/fixtures/files/burp.xml +100 -0
- data/spec/fixtures/files/burp_issue_severity.xml +118 -0
- data/spec/fixtures/files/invalid-utf-issue.xml +21 -0
- data/spec/fixtures/files/without-base64.xml +709 -0
- data/spec/spec_helper.rb +9 -0
- data/templates/evidence.fields +8 -0
- data/templates/evidence.sample +76 -0
- data/templates/evidence.template +20 -0
- data/templates/html_evidence.fields +13 -0
- data/templates/html_evidence.sample +36 -0
- data/templates/html_evidence.template +50 -0
- data/templates/issue.fields +8 -0
- data/templates/issue.sample +23 -0
- data/templates/issue.template +30 -0
- metadata +174 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Dradis
|
2
|
+
module Plugins
|
3
|
+
module Burp
|
4
|
+
class FieldProcessor < Dradis::Plugins::Upload::FieldProcessor
|
5
|
+
|
6
|
+
def post_initialize(args={})
|
7
|
+
@burp_object =
|
8
|
+
if data.is_a?(Nokogiri::XML::Element)
|
9
|
+
::Burp::Xml::Issue.new(data)
|
10
|
+
elsif data.is_a?(Nokogiri::XML::NodeSet)
|
11
|
+
::Burp::Html::Issue.new(data)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def value(args={})
|
16
|
+
field = args[:field]
|
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
|
+
@burp_object.try(name) || 'n/a'
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Dradis
|
2
|
+
module Plugins
|
3
|
+
module Burp
|
4
|
+
# Returns the version of the currently loaded Frontend 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,144 @@
|
|
1
|
+
module Dradis::Plugins::Burp
|
2
|
+
# This module knows how to parse Burp HTML format.
|
3
|
+
module Html
|
4
|
+
def self.meta
|
5
|
+
package = Dradis::Plugins::Burp
|
6
|
+
{
|
7
|
+
name: package::Engine::plugin_name,
|
8
|
+
description: 'Upload Burp Scanner output file (.html)',
|
9
|
+
version: package.version
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
class Importer < Dradis::Plugins::Upload::Importer
|
14
|
+
def initialize(args={})
|
15
|
+
args[:plugin] = Dradis::Plugins::Burp
|
16
|
+
super(args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def import(params = {})
|
20
|
+
logger.info { 'Parsing Burp Scanner HTML output file...' }
|
21
|
+
@doc = Nokogiri::HTML(File.read(params[:file]))
|
22
|
+
logger.info { 'Done.' }
|
23
|
+
|
24
|
+
# Issue headers are like: <span class="BODH0" id="X">
|
25
|
+
issue_headers = @doc.xpath("//span[contains(@class, 'BODH0')]")
|
26
|
+
|
27
|
+
if issue_headers.count.zero?
|
28
|
+
error = "Document doesn't seem to be in the Burp Scanner HTML format."
|
29
|
+
logger.fatal { error }
|
30
|
+
content_service.create_note text: error
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
|
34
|
+
issue_headers.each do |header|
|
35
|
+
issue_id = header.attr('id')
|
36
|
+
html = extract_html_fragment_for(issue_id)
|
37
|
+
process_html_issue(html)
|
38
|
+
end
|
39
|
+
|
40
|
+
logger.info { 'Burp Scanner results successfully imported' }
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def process_html_issue(html_issue)
|
45
|
+
header = html_issue.first
|
46
|
+
title = header.text.gsub(/^\d+\.\S/, '')
|
47
|
+
burp_id =
|
48
|
+
if (link = header.css('a').first)
|
49
|
+
link.attr('href')[/\/([0-9a-f]+)_.*/, 1].to_i(16)
|
50
|
+
else
|
51
|
+
title
|
52
|
+
end
|
53
|
+
issue_id = html_issue.attr('id').value
|
54
|
+
issue_text =
|
55
|
+
template_service.process_template(
|
56
|
+
template: 'issue',
|
57
|
+
data: html_issue
|
58
|
+
)
|
59
|
+
|
60
|
+
logger.info { "Processing issue #{issue_id}: #{title}" }
|
61
|
+
issue = content_service.create_issue(text: issue_text, id: burp_id)
|
62
|
+
|
63
|
+
# Evidence headers are like:
|
64
|
+
# <span class="BODH1" id="X.Y">
|
65
|
+
# where:
|
66
|
+
# X is the issue index
|
67
|
+
# Y is the evidence index
|
68
|
+
evidence_headers = html_issue.xpath(
|
69
|
+
"//span[contains(@class, 'BODH1') and starts-with(@id, '#{issue_id}.')]"
|
70
|
+
)
|
71
|
+
|
72
|
+
# If there are no evidence headers inside this issue, this is a
|
73
|
+
# "single evidence" case: our evidence html is the issue html itself
|
74
|
+
if evidence_headers.count.zero?
|
75
|
+
process_html_evidence(html_issue, issue)
|
76
|
+
else
|
77
|
+
evidence_headers.each do |header|
|
78
|
+
evidence_id = header.attr('id')
|
79
|
+
html = extract_html_fragment_for(evidence_id)
|
80
|
+
process_html_evidence(html, issue)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def process_html_evidence(html_evidence, issue)
|
86
|
+
evidence_id = html_evidence.attr('id').value
|
87
|
+
logger.info { "Processing evidence #{evidence_id}" }
|
88
|
+
|
89
|
+
host_td = html_evidence.search("td:starts-with('Host:')").first
|
90
|
+
host_label = host_td.next_element.text.split('//').last
|
91
|
+
host = content_service.create_node(label: host_label, type: :host)
|
92
|
+
|
93
|
+
evidence_text =
|
94
|
+
template_service.process_template(
|
95
|
+
template: 'html_evidence',
|
96
|
+
data: html_evidence
|
97
|
+
)
|
98
|
+
|
99
|
+
content_service.create_evidence(
|
100
|
+
issue: issue,
|
101
|
+
node: host,
|
102
|
+
content: evidence_text
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Html for an issue and evidence is not nested inside an html element.
|
107
|
+
#
|
108
|
+
# An issue is the html fragment from <span id="X"> (where X is a single
|
109
|
+
# integer number: 1, 2, 3...) until the next span like that or the end of
|
110
|
+
# the file.
|
111
|
+
#
|
112
|
+
# An evidence is the html fragment from <span id="X.Y"> (where X is the
|
113
|
+
# issue index and Y the evidence index: 1.1, 1.2,...,2.1, 2.2,...) until
|
114
|
+
# the next evidence span (id="X.Z"), the next issue span (id="Y"), or the
|
115
|
+
# end of the file.
|
116
|
+
#
|
117
|
+
# This method extracts all the html related to as specific issue id or
|
118
|
+
# evidence id.
|
119
|
+
def extract_html_fragment_for(id)
|
120
|
+
next_id = if /\d+\.\d+/ =~ id
|
121
|
+
id_parts = id.split('.')
|
122
|
+
"#{id_parts[0]}.#{id_parts[1].to_i + 1}"
|
123
|
+
else
|
124
|
+
id.to_i + 1
|
125
|
+
end
|
126
|
+
|
127
|
+
start_element = @doc.xpath("//span[@id='#{id}']")
|
128
|
+
return nil if start_element.empty?
|
129
|
+
|
130
|
+
ending_element = @doc.xpath("//span[@id='#{next_id}']")
|
131
|
+
if ending_element.empty? && /\d+\.\d+/ =~ id
|
132
|
+
next_id = id.split('.')[0].to_i + 1
|
133
|
+
ending_element = @doc.xpath("//span[@id='#{next_id}']")
|
134
|
+
end
|
135
|
+
|
136
|
+
xpath = "//*[preceding-sibling::span[@id='#{id}']"
|
137
|
+
xpath += " and following-sibling::span[@id='#{next_id}']" unless ending_element.empty?
|
138
|
+
xpath += ']'
|
139
|
+
|
140
|
+
start_element + @doc.xpath(xpath)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Dradis::Plugins::Burp
|
2
|
+
|
3
|
+
# This module knows how to parse Burp XML format.
|
4
|
+
module Xml
|
5
|
+
def self.meta
|
6
|
+
package = Dradis::Plugins::Burp
|
7
|
+
{
|
8
|
+
name: package::Engine::plugin_name,
|
9
|
+
description: 'Upload Burp Scanner output file (.xml)',
|
10
|
+
version: package.version
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
class Importer < Dradis::Plugins::Upload::Importer
|
15
|
+
BURP_EXTENSION_TYPE = '134217728'.freeze
|
16
|
+
BURP_SEVERITIES = ['Information', 'Low', 'Medium', 'High'].freeze
|
17
|
+
|
18
|
+
def initialize(args={})
|
19
|
+
args[:plugin] = Dradis::Plugins::Burp
|
20
|
+
super(args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def import(params = {})
|
24
|
+
file_content = File.read(params[:file])
|
25
|
+
|
26
|
+
if file_content =~ /base64="false"/
|
27
|
+
error = "Burp input contains HTTP request / response data that hasn't been Base64-encoded.\n"
|
28
|
+
error << 'Please re-export your scanner results making sure the Base-64 encode option is selected.'
|
29
|
+
|
30
|
+
logger.fatal{ error }
|
31
|
+
content_service.create_note text: error
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
|
35
|
+
logger.info { 'Parsing Burp Scanner XML output file...' }
|
36
|
+
doc = Nokogiri::XML(file_content)
|
37
|
+
logger.info { 'Done.' }
|
38
|
+
|
39
|
+
if doc.root.name != 'issues'
|
40
|
+
error = 'Document doesn\'t seem to be in the Burp Scanner XML format.'
|
41
|
+
logger.fatal { error }
|
42
|
+
content_service.create_note text: error
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
# This will be filled in by the Processor while iterating over the issues
|
47
|
+
@issues = []
|
48
|
+
@severities = Hash.new(0)
|
49
|
+
|
50
|
+
# We need to look ahead through all issues to bring the highest severity
|
51
|
+
# of each instance to the Issue level.
|
52
|
+
doc.xpath('issues/issue').each do |xml_issue|
|
53
|
+
issue_id = issue_id_for(xml_issue)
|
54
|
+
issue_severity = BURP_SEVERITIES.index(xml_issue.at('severity').text)
|
55
|
+
|
56
|
+
@severities[issue_id] = issue_severity if issue_severity > @severities[issue_id]
|
57
|
+
@issues << xml_issue
|
58
|
+
end
|
59
|
+
|
60
|
+
@issues.each { |xml_issue| process_issue(xml_issue) }
|
61
|
+
|
62
|
+
logger.info { 'Burp Scanner results successfully imported' }
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def create_issue(affected_host:, id:, xml_issue:)
|
68
|
+
xml_evidence = xml_issue.clone
|
69
|
+
|
70
|
+
# Ensure that the Issue contains the highest Severity value
|
71
|
+
xml_issue.at('severity').content = BURP_SEVERITIES[@severities[id]]
|
72
|
+
|
73
|
+
issue_text =
|
74
|
+
template_service.process_template(
|
75
|
+
template: 'issue',
|
76
|
+
data: xml_issue
|
77
|
+
)
|
78
|
+
|
79
|
+
if issue_text.include?(::Burp::INVALID_UTF_REPLACE)
|
80
|
+
logger.info do
|
81
|
+
"\tdetected invalid UTF-8 bytes in your issue. " \
|
82
|
+
"Replacing them with '#{::Burp::INVALID_UTF_REPLACE}'."
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
issue = content_service.create_issue(text: issue_text, id: id)
|
87
|
+
|
88
|
+
logger.info do
|
89
|
+
"\tadding evidence for this instance to #{affected_host.label}."
|
90
|
+
end
|
91
|
+
|
92
|
+
evidence_text =
|
93
|
+
template_service.process_template(
|
94
|
+
template: 'evidence',
|
95
|
+
data: xml_evidence
|
96
|
+
)
|
97
|
+
|
98
|
+
if evidence_text.include?(::Burp::INVALID_UTF_REPLACE)
|
99
|
+
logger.info do
|
100
|
+
"\tdetected invalid UTF-8 bytes in your evidence. " \
|
101
|
+
"Replacing them with '#{::Burp::INVALID_UTF_REPLACE}'."
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
content_service.create_evidence(
|
106
|
+
issue: issue,
|
107
|
+
node: affected_host,
|
108
|
+
content: evidence_text
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Burp extensions don't follow the "unique type for every Issue" logic
|
113
|
+
# so we have to deal with them separately
|
114
|
+
def issue_id_for(xml_issue)
|
115
|
+
if xml_issue.at('type').text == BURP_EXTENSION_TYPE
|
116
|
+
xml_issue.at('name').text.gsub!(' ', '')
|
117
|
+
else
|
118
|
+
xml_issue.at('type').text.to_i
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Creates the Nodes/properties
|
123
|
+
def process_issue(xml_issue)
|
124
|
+
host_url = xml_issue.at('host').text
|
125
|
+
host_label = xml_issue.at('host')['ip']
|
126
|
+
host_label = host_url if host_label.empty?
|
127
|
+
issue_id = issue_id_for(xml_issue)
|
128
|
+
|
129
|
+
affected_host = content_service.create_node(label: host_label, type: :host)
|
130
|
+
affected_host.set_property(:hostname, host_url)
|
131
|
+
affected_host.save
|
132
|
+
|
133
|
+
logger.info { "Adding #{xml_issue.at('name').text} (#{issue_id})"}
|
134
|
+
logger.info { "\taffects: #{host_label}" }
|
135
|
+
|
136
|
+
create_issue(
|
137
|
+
affected_host: affected_host,
|
138
|
+
id: issue_id,
|
139
|
+
xml_issue: xml_issue
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class BurpTasks < Thor
|
2
|
+
include Rails.application.config.dradis.thor_helper_module
|
3
|
+
|
4
|
+
namespace "dradis:plugins:burp"
|
5
|
+
|
6
|
+
desc "upload FILE", "upload Burp XML or HTML results"
|
7
|
+
def upload(file_path)
|
8
|
+
require 'config/environment'
|
9
|
+
|
10
|
+
unless File.exists?(file_path)
|
11
|
+
$stderr.puts "** the file [#{file_path}] does not exist"
|
12
|
+
exit(-1)
|
13
|
+
end
|
14
|
+
|
15
|
+
detect_and_set_project_scope
|
16
|
+
|
17
|
+
importer =
|
18
|
+
if File.extname(file_path) == '.xml'
|
19
|
+
Dradis::Plugins::Burp::Xml::Importer.new(task_options)
|
20
|
+
elsif File.extname(file_path) == '.html'
|
21
|
+
Dradis::Plugins::Burp::Html::Importer.new(task_options)
|
22
|
+
else
|
23
|
+
$stderr.puts "** Unsupported file. Must be .xml or .html"
|
24
|
+
exit(-2)
|
25
|
+
end
|
26
|
+
|
27
|
+
importer.import(file: file_path)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe 'Burp upload plugin' do
|
5
|
+
|
6
|
+
describe Burp::Xml::Issue do
|
7
|
+
it 'handles invalid utf-8 bytes' do
|
8
|
+
doc = Nokogiri::XML(File.read('spec/fixtures/files/invalid-utf-issue.xml'))
|
9
|
+
xml_issue = doc.xpath('issues/issue').first
|
10
|
+
issue = Burp::Xml::Issue.new(xml_issue)
|
11
|
+
|
12
|
+
expect{ issue.request.encode('utf-8') }.to_not raise_error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Dradis::Plugins::Burp::Xml::Importer do
|
17
|
+
before(:each) do
|
18
|
+
# Stub template service
|
19
|
+
templates_dir = File.expand_path('../../templates', __FILE__)
|
20
|
+
expect_any_instance_of(Dradis::Plugins::TemplateService)
|
21
|
+
.to receive(:default_templates_dir).and_return(templates_dir)
|
22
|
+
|
23
|
+
# Init services
|
24
|
+
plugin = Dradis::Plugins::Burp::Xml
|
25
|
+
|
26
|
+
@content_service = Dradis::Plugins::ContentService::Base.new(
|
27
|
+
logger: Logger.new(STDOUT),
|
28
|
+
plugin: plugin
|
29
|
+
)
|
30
|
+
|
31
|
+
@importer = plugin::Importer.new(
|
32
|
+
content_service: @content_service,
|
33
|
+
)
|
34
|
+
|
35
|
+
# Stub dradis-plugins methods
|
36
|
+
#
|
37
|
+
# They return their argument hashes as objects mimicking
|
38
|
+
# Nodes, Issues, etc
|
39
|
+
allow(@content_service).to receive(:create_node) do |args|
|
40
|
+
obj = OpenStruct.new(args)
|
41
|
+
obj.define_singleton_method(:set_property) { |_, __| }
|
42
|
+
obj
|
43
|
+
end
|
44
|
+
allow(@content_service).to receive(:create_issue) do |args|
|
45
|
+
OpenStruct.new(args)
|
46
|
+
end
|
47
|
+
allow(@content_service).to receive(:create_evidence) do |args|
|
48
|
+
OpenStruct.new(args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'creates nodes, issues, and evidence as needed' do
|
53
|
+
|
54
|
+
# Host node
|
55
|
+
#
|
56
|
+
# create_node should be called once for each issue in the xml,
|
57
|
+
# but ContentService knows it's already created and NOOPs
|
58
|
+
expect(@content_service).to receive(:create_node)
|
59
|
+
.with(hash_including label: '10.0.0.1')
|
60
|
+
.exactly(4).times
|
61
|
+
|
62
|
+
# create_issue should be called once for each issue in the xml
|
63
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
64
|
+
expect(args[:text]).to include("#[Title]#\nIssue 1")
|
65
|
+
expect(args[:id]).to eq(8781630)
|
66
|
+
OpenStruct.new(args)
|
67
|
+
end.once
|
68
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
69
|
+
expect(args[:content]).to include("Lorem ipsum dolor sit amet")
|
70
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 1")
|
71
|
+
expect(args[:node].label).to eq("10.0.0.1")
|
72
|
+
end.once
|
73
|
+
|
74
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
75
|
+
expect(args[:text]).to include("#[Title]#\nIssue 2")
|
76
|
+
expect(args[:id]).to eq(8781631)
|
77
|
+
OpenStruct.new(args)
|
78
|
+
end.once
|
79
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
80
|
+
expect(args[:content]).to include("Lorem ipsum dolor sit amet")
|
81
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 2")
|
82
|
+
expect(args[:node].label).to eq("10.0.0.1")
|
83
|
+
end.once
|
84
|
+
|
85
|
+
# Issue 3 is an Extension finding so we need to confirm
|
86
|
+
# that it triggers process_extension_issues instead of process_burp_issues
|
87
|
+
# and the plugin_id is not set to the Type (134217728)
|
88
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
89
|
+
expect(args[:text]).to include("#[Title]#\nIssue 3")
|
90
|
+
expect(args[:id]).to eq("Issue3")
|
91
|
+
OpenStruct.new(args)
|
92
|
+
end.once
|
93
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
94
|
+
expect(args[:content]).to include("Lorem ipsum dolor sit amet")
|
95
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 3")
|
96
|
+
expect(args[:node].label).to eq("10.0.0.1")
|
97
|
+
end.once
|
98
|
+
|
99
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
100
|
+
expect(args[:text]).to include("#[Title]#\nIssue 4")
|
101
|
+
expect(args[:id]).to eq(8781633)
|
102
|
+
OpenStruct.new(args)
|
103
|
+
end.once
|
104
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
105
|
+
expect(args[:content]).to include("Lorem ipsum dolor sit amet")
|
106
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 4")
|
107
|
+
expect(args[:node].label).to eq("10.0.0.1")
|
108
|
+
end.once
|
109
|
+
|
110
|
+
|
111
|
+
# Run the import
|
112
|
+
@importer.import(file: 'spec/fixtures/files/burp.xml')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns the highest <severity> at the Issue level' do
|
116
|
+
|
117
|
+
# create_issue should be called once for each issue in the xml
|
118
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
119
|
+
expect(args[:id]).to eq(8781630)
|
120
|
+
expect(args[:text]).to include("#[Title]#\nIssue 1")
|
121
|
+
expect(args[:text]).to include("#[Severity]#\nCritical")
|
122
|
+
OpenStruct.new(args)
|
123
|
+
end
|
124
|
+
|
125
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
126
|
+
expect(args[:content]).to include("#[Severity]#\nInformation")
|
127
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 1")
|
128
|
+
expect(args[:node].label).to eq('10.0.0.1')
|
129
|
+
end.once
|
130
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
131
|
+
expect(args[:content]).to include("#[Severity]#\nHigh")
|
132
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 1")
|
133
|
+
expect(args[:node].label).to eq('10.0.0.1')
|
134
|
+
OpenStruct.new(args)
|
135
|
+
end.once
|
136
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
137
|
+
expect(args[:content]).to include("#[Severity]#\nMedium")
|
138
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 1")
|
139
|
+
expect(args[:node].label).to eq('10.0.0.1')
|
140
|
+
end.once
|
141
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
142
|
+
expect(args[:content]).to include("#[Severity]#\nCritical")
|
143
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 1")
|
144
|
+
expect(args[:node].label).to eq('10.0.0.1')
|
145
|
+
end.once
|
146
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
147
|
+
expect(args[:content]).to include("#[Severity]#\nLow")
|
148
|
+
expect(args[:issue].text).to include("#[Title]#\nIssue 1")
|
149
|
+
expect(args[:node].label).to eq('10.0.0.1')
|
150
|
+
end.once
|
151
|
+
|
152
|
+
# Run the import
|
153
|
+
@importer.import(file: 'spec/fixtures/files/burp_issue_severity.xml')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe Dradis::Plugins::Burp::Html::Importer do
|
158
|
+
before(:each) do
|
159
|
+
# Stub template service
|
160
|
+
templates_dir = File.expand_path('../../templates', __FILE__)
|
161
|
+
expect_any_instance_of(Dradis::Plugins::TemplateService)
|
162
|
+
.to receive(:default_templates_dir).and_return(templates_dir)
|
163
|
+
|
164
|
+
# Init services
|
165
|
+
plugin = Dradis::Plugins::Burp::Html
|
166
|
+
|
167
|
+
@content_service = Dradis::Plugins::ContentService::Base.new(
|
168
|
+
logger: Logger.new(STDOUT),
|
169
|
+
plugin: plugin
|
170
|
+
)
|
171
|
+
|
172
|
+
@importer = plugin::Importer.new(
|
173
|
+
content_service: @content_service,
|
174
|
+
)
|
175
|
+
|
176
|
+
# Stub dradis-plugins methods
|
177
|
+
#
|
178
|
+
# They return their argument hashes as objects mimicking
|
179
|
+
# Nodes, Issues, etc
|
180
|
+
allow(@content_service).to receive(:create_node) do |args|
|
181
|
+
obj = OpenStruct.new(args)
|
182
|
+
obj.define_singleton_method(:set_property) { |_, __| }
|
183
|
+
obj
|
184
|
+
end
|
185
|
+
allow(@content_service).to receive(:create_issue) do |args|
|
186
|
+
OpenStruct.new(args)
|
187
|
+
end
|
188
|
+
allow(@content_service).to receive(:create_evidence) do |args|
|
189
|
+
OpenStruct.new(args)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it "creates nodes, issues, and evidence as needed" do
|
194
|
+
|
195
|
+
# Host node
|
196
|
+
#
|
197
|
+
# create_node should be called once for each issue in the xml,
|
198
|
+
# but ContentService knows it's already created and NOOPs
|
199
|
+
expect(@content_service).to receive(:create_node)
|
200
|
+
.with(hash_including label: 'github.com/dradis/dradis-burp')
|
201
|
+
.exactly(1).times
|
202
|
+
|
203
|
+
# # create_issue should be called once for each issue in the xml
|
204
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
205
|
+
expect(args[:text]).to include("#[Title]#\nStrict transport security not enforced")
|
206
|
+
expect(args[:id]).to eq(16777984)
|
207
|
+
OpenStruct.new(args)
|
208
|
+
end.once
|
209
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
210
|
+
expect(args[:content]).to include("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
211
|
+
expect(args[:issue].text).to include("#[Title]#\nStrict transport security not enforced")
|
212
|
+
expect(args[:node].label).to eq("github.com/dradis/dradis-burp")
|
213
|
+
end.once
|
214
|
+
|
215
|
+
# Run the import
|
216
|
+
@importer.import(file: 'spec/fixtures/files/burp.html')
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
end
|