dradis-nexpose 4.15.0 → 4.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/dradis/plugins/nexpose/gem_version.rb +1 -1
- data/lib/dradis/plugins/nexpose/xml_formatter.rb +81 -0
- data/lib/dradis/plugins/nexpose.rb +1 -0
- data/lib/nexpose/vulnerability.rb +12 -53
- data/spec/fixtures/files/full.xml +18 -2
- data/spec/fixtures/files/lists.xml +26 -0
- data/spec/nexpose_upload_spec.rb +152 -0
- data/spec/xml_formatter_spec.rb +23 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0dd759ff5965a397c6dc53aa7fe958a472377df1e4b88598c084ef710bbf7325
|
4
|
+
data.tar.gz: cc1caab7f91bf7a934b940bda4773ea0af137218320c26fe4b33e89bfe212499
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11ab68a5ada00fd69fc77b7f41b3e08441dc7e0ac9fdbad90ff006b62a307fd6a4a44d05d534e954c88391ce63ec5fc28b4682e4e6e4f4c488f7a489192c0e6f
|
7
|
+
data.tar.gz: f610891a766cdb60ac5f1eccb90bfc497b2ee087f3c5ef3bd52037a11e342118c4ea62938ce3e7170409682c7a78302ecbbf06fe6d2450dde8a12584a6544ceb
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,81 @@
|
|
1
|
+
module Dradis::Plugins::Nexpose
|
2
|
+
class XmlFormatter
|
3
|
+
def format_html_content(source)
|
4
|
+
result = format_list(source)
|
5
|
+
|
6
|
+
cleanup_html(result)
|
7
|
+
end
|
8
|
+
|
9
|
+
def cleanup_html(source)
|
10
|
+
result = source.to_s
|
11
|
+
result.gsub!(/<ContainerBlockElement>(.*?)<\/ContainerBlockElement>/m) { |m| "#{ $1 }" }
|
12
|
+
result.gsub!(/<Paragraph preformat=\"true\">(\s*)<Paragraph preformat=\"true\">(.*?)<\/Paragraph>(\s*)<\/Paragraph>/mi) do
|
13
|
+
text = $2
|
14
|
+
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
|
15
|
+
end
|
16
|
+
result.gsub!(/<Paragraph preformat=\"true\">(.*?)<\/Paragraph>/mi) do
|
17
|
+
text = $1
|
18
|
+
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
|
19
|
+
end
|
20
|
+
result.gsub!(/<Paragraph>(.*?)<\/Paragraph>/m) { |m| "#{ $1 }\n" }
|
21
|
+
result.gsub!(/<Paragraph>|<\/Paragraph>/, '')
|
22
|
+
result.gsub!(/ /, '')
|
23
|
+
result.gsub!(/ /, '')
|
24
|
+
result.gsub!(/\t\t/, '')
|
25
|
+
result.gsub!(/<URLLink(.*)LinkURL=\"(.*?)\"(.*?)>(.*?)<\/URLLink>/im) { "\"#{$4.strip}\":#{$2.strip} " }
|
26
|
+
result.gsub!(/<URLLink LinkTitle=\"(.*?)\"(.*?)LinkURL=\"(.*?)\"\/>/i) { "\"#{$1.strip}\":#{$3.strip} " }
|
27
|
+
result.gsub!(/<URLLink LinkURL=\"(.*?)\"(.*?)LinkTitle=\"(.*?)\"\/>/i) { "\"#{$3.strip}\":#{$1.strip} " }
|
28
|
+
result.gsub!(/>/, '>')
|
29
|
+
result.gsub!(/</, '<')
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
def cleanup_nested(source)
|
34
|
+
result = source.to_s
|
35
|
+
result.gsub!(/<references>/, '')
|
36
|
+
result.gsub!(/<\/references>/, '')
|
37
|
+
result.gsub!(/<reference source=\"(.*?)\">(.*?)<\/reference>/i) { "#{$1.strip}: #{$2.strip}\n" }
|
38
|
+
result.gsub!(/<tags>/, '')
|
39
|
+
result.gsub!(/<\/tags>/, '')
|
40
|
+
result.gsub!(/<tag>(.*?)<\/tag>/) { "#{$1}\n" }
|
41
|
+
result.gsub!(/ /, '')
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def format_list(source)
|
48
|
+
if source.xpath('./UnorderedList | ./OrderedList').any?
|
49
|
+
format_nexpose_list(source)
|
50
|
+
else
|
51
|
+
source
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def format_nexpose_list(xml, depth = 1)
|
56
|
+
xml.xpath('./UnorderedList | ./OrderedList').map do |list|
|
57
|
+
list_item_element = list.name == 'UnorderedList' ? '*' : '#'
|
58
|
+
|
59
|
+
list.xpath('./ListItem').map do |list_item|
|
60
|
+
paragraphs = list_item.xpath('./Paragraph')
|
61
|
+
|
62
|
+
list_item_text =
|
63
|
+
if paragraphs.any?
|
64
|
+
paragraphs.map do |paragraph|
|
65
|
+
# <Paragraph> nodes can either have more lists or just text
|
66
|
+
if paragraph.xpath('./UnorderedList | ./OrderedList').any?
|
67
|
+
format_nexpose_list(paragraph, depth + 1)
|
68
|
+
else
|
69
|
+
cleanup_html(paragraph.to_s).chomp
|
70
|
+
end
|
71
|
+
end.join("\n")
|
72
|
+
else
|
73
|
+
list_item.text
|
74
|
+
end
|
75
|
+
|
76
|
+
''.ljust(depth, list_item_element) + ' ' + list_item_text
|
77
|
+
end.join("\n")
|
78
|
+
end.join("\n")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -20,7 +20,7 @@ module Nexpose
|
|
20
20
|
def supported_tags
|
21
21
|
[
|
22
22
|
# attributes
|
23
|
-
:added, :cvss_score, :cvss_vector, :modified, :nexpose_id, :pci_severity,
|
23
|
+
:added, :cvss_score, :cvss_vector, :modified, :nexpose_id, :pci_severity,
|
24
24
|
:published, :risk_score, :severity, :title,
|
25
25
|
|
26
26
|
# simple tags
|
@@ -34,10 +34,9 @@ module Nexpose
|
|
34
34
|
]
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
37
|
# This allows external callers (and specs) to check for implemented
|
39
38
|
# properties
|
40
|
-
def respond_to?(method, include_private=false)
|
39
|
+
def respond_to?(method, include_private = false)
|
41
40
|
return true if supported_tags.include?(method.to_sym)
|
42
41
|
super
|
43
42
|
end
|
@@ -49,7 +48,6 @@ module Nexpose
|
|
49
48
|
# attribute, simple descendent or collection that it maps to in the XML
|
50
49
|
# tree.
|
51
50
|
def method_missing(method, *args)
|
52
|
-
|
53
51
|
# We could remove this check and return nil for any non-recognized tag.
|
54
52
|
# The problem would be that it would make tricky to debug problems with
|
55
53
|
# typos. For instance: <>.potr would return nil instead of raising an
|
@@ -62,11 +60,11 @@ module Nexpose
|
|
62
60
|
# First we try the attributes. In Ruby we use snake_case, but in XML
|
63
61
|
# CamelCase is used for some attributes
|
64
62
|
translations_table = {
|
65
|
-
:
|
66
|
-
:
|
67
|
-
:
|
68
|
-
:
|
69
|
-
:
|
63
|
+
nexpose_id: 'id',
|
64
|
+
pci_severity: 'pciSeverity',
|
65
|
+
risk_score: 'riskScore',
|
66
|
+
cvss_score: 'cvssScore',
|
67
|
+
cvss_vector: 'cvssVector'
|
70
68
|
}
|
71
69
|
|
72
70
|
method_name = translations_table.fetch(method, method.to_s)
|
@@ -78,13 +76,14 @@ module Nexpose
|
|
78
76
|
nest = @xml.xpath("./#{method_name}").first
|
79
77
|
|
80
78
|
# We need to clean up tags that have HTML content in them
|
79
|
+
formatter = Dradis::Plugins::Nexpose::XmlFormatter.new
|
81
80
|
if tags_with_html_content.include?(method)
|
82
|
-
result =
|
81
|
+
result = formatter.format_html_content(tag)
|
83
82
|
result = add_bc_to_ssl_cipher_list(result) if SSL_CIPHER_VULN_IDS.include?(@xml.attributes['id'].value)
|
84
83
|
return result
|
85
84
|
# And we need to clean up the tags with nested content in them
|
86
85
|
elsif tags_with_nested_content.include?(method)
|
87
|
-
return cleanup_nested(nest)
|
86
|
+
return formatter.cleanup_nested(nest)
|
88
87
|
else
|
89
88
|
return tag
|
90
89
|
end
|
@@ -96,7 +95,7 @@ module Nexpose
|
|
96
95
|
return @xml.xpath("//test[@id='#{vuln_id}']/Paragraph").
|
97
96
|
text.split("\n").
|
98
97
|
collect(&:strip).
|
99
|
-
reject{|line| line.empty?}.join("\n")
|
98
|
+
reject { |line| line.empty? }.join("\n")
|
100
99
|
end
|
101
100
|
|
102
101
|
nil
|
@@ -106,46 +105,7 @@ module Nexpose
|
|
106
105
|
|
107
106
|
def add_bc_to_ssl_cipher_list(source)
|
108
107
|
result = source.to_s
|
109
|
-
result.gsub!(/\n(.*?)!(.*?)/){"\nbc. #{ $1 }!#{ $2 }\n"}
|
110
|
-
result
|
111
|
-
end
|
112
|
-
|
113
|
-
def cleanup_html(source)
|
114
|
-
result = source.to_s
|
115
|
-
result.gsub!(/<ContainerBlockElement>(.*?)<\/ContainerBlockElement>/m){|m| "#{ $1 }"}
|
116
|
-
result.gsub!(/<Paragraph preformat=\"true\">(\s*)<Paragraph preformat=\"true\">(.*?)<\/Paragraph>(\s*)<\/Paragraph>/mi) do
|
117
|
-
text = $2
|
118
|
-
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
|
119
|
-
end
|
120
|
-
result.gsub!(/<Paragraph preformat=\"true\">(.*?)<\/Paragraph>/mi) do
|
121
|
-
text = $1
|
122
|
-
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
|
123
|
-
end
|
124
|
-
result.gsub!(/<Paragraph>(.*?)<\/Paragraph>/m){|m| "#{ $1 }\n"}
|
125
|
-
result.gsub!(/<Paragraph>|<\/Paragraph>/, '')
|
126
|
-
result.gsub!(/<UnorderedList(.*?)>(.*?)<\/UnorderedList>/m){|m| "#{ $2 }"}
|
127
|
-
result.gsub!(/<OrderedList(.*?)>(.*?)<\/OrderedList>/m){|m| "#{ $2 }"}
|
128
|
-
result.gsub!(/<ListItem>|<\/ListItem>/, '')
|
129
|
-
result.gsub!(/ /, '')
|
130
|
-
result.gsub!(/ /, '')
|
131
|
-
result.gsub!(/\t\t/, '')
|
132
|
-
result.gsub!(/<URLLink(.*)LinkURL=\"(.*?)\"(.*?)>(.*?)<\/URLLink>/im) { "\"#{$4.strip}\":#{$2.strip} " }
|
133
|
-
result.gsub!(/<URLLink LinkTitle=\"(.*?)\"(.*?)LinkURL=\"(.*?)\"\/>/i) { "\"#{$1.strip}\":#{$3.strip} " }
|
134
|
-
result.gsub!(/<URLLink LinkURL=\"(.*?)\"(.*?)LinkTitle=\"(.*?)\"\/>/i) { "\"#{$3.strip}\":#{$1.strip} " }
|
135
|
-
result.gsub!(/>/, '>')
|
136
|
-
result.gsub!(/</, '<')
|
137
|
-
result
|
138
|
-
end
|
139
|
-
|
140
|
-
def cleanup_nested(source)
|
141
|
-
result = source.to_s
|
142
|
-
result.gsub!(/<references>/, '')
|
143
|
-
result.gsub!(/<\/references>/, '')
|
144
|
-
result.gsub!(/<reference source=\"(.*?)\">(.*?)<\/reference>/i) {"#{$1.strip}: #{$2.strip}\n"}
|
145
|
-
result.gsub!(/<tags>/, '')
|
146
|
-
result.gsub!(/<\/tags>/, '')
|
147
|
-
result.gsub!(/<tag>(.*?)<\/tag>/) {"#{$1}\n"}
|
148
|
-
result.gsub!(/ /, '')
|
108
|
+
result.gsub!(/\n(.*?)!(.*?)/) { "\nbc. #{ $1 }!#{ $2 }\n" }
|
149
109
|
result
|
150
110
|
end
|
151
111
|
|
@@ -156,6 +116,5 @@ module Nexpose
|
|
156
116
|
def tags_with_nested_content
|
157
117
|
[:references, :tags]
|
158
118
|
end
|
159
|
-
|
160
119
|
end
|
161
120
|
end
|
@@ -127,11 +127,27 @@
|
|
127
127
|
<ContainerBlockElement>
|
128
128
|
<UnorderedList>
|
129
129
|
<ListItem>
|
130
|
+
<Paragraph>Microsoft Windows</Paragraph>
|
130
131
|
<Paragraph>
|
131
|
-
<
|
132
|
-
|
132
|
+
<OrderedList>
|
133
|
+
<ListItem>Open the Windows Control Panel.</ListItem>
|
134
|
+
<ListItem>Select "Administrative Tools".</ListItem>
|
135
|
+
</OrderedList>
|
133
136
|
</Paragraph>
|
134
137
|
</ListItem>
|
138
|
+
<ListItem>
|
139
|
+
<Paragraph>Microsoft Windows</Paragraph>
|
140
|
+
<Paragraph>
|
141
|
+
<OrderedList>
|
142
|
+
<ListItem>Open the "Performance and Maintenance" control panel.</ListItem>
|
143
|
+
<ListItem>Select "Administrative Tools".</ListItem>
|
144
|
+
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
|
145
|
+
<ListItem>
|
146
|
+
<Paragraph>Microsoft Windows</Paragraph>
|
147
|
+
<Paragraph>
|
148
|
+
<OrderedList>
|
149
|
+
<ListItem>Open the "Administrative Tools" control panel.</ListItem>
|
150
|
+
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
|
135
151
|
<ListItem>
|
136
152
|
<Paragraph>OpenBSD</Paragraph>
|
137
153
|
<Paragraph>Download and apply the patch from:
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<ContainerBlockElement>
|
2
|
+
<UnorderedList>
|
3
|
+
<ListItem>
|
4
|
+
<Paragraph>Microsoft Windows</Paragraph>
|
5
|
+
<Paragraph>
|
6
|
+
<OrderedList>
|
7
|
+
<ListItem>Open the Windows Control Panel.</ListItem>
|
8
|
+
<ListItem>Select "Administrative Tools".</ListItem>
|
9
|
+
</OrderedList>
|
10
|
+
</Paragraph>
|
11
|
+
</ListItem>
|
12
|
+
<ListItem>
|
13
|
+
<Paragraph>Microsoft Windows</Paragraph>
|
14
|
+
<Paragraph>
|
15
|
+
<OrderedList>
|
16
|
+
<ListItem>Open the "Performance and Maintenance" control panel.</ListItem>
|
17
|
+
<ListItem>Select "Administrative Tools".</ListItem>
|
18
|
+
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
|
19
|
+
<ListItem>
|
20
|
+
<Paragraph>Microsoft Windows</Paragraph>
|
21
|
+
<Paragraph>
|
22
|
+
<OrderedList>
|
23
|
+
<ListItem>Open the "Administrative Tools" control panel.</ListItem>
|
24
|
+
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
|
25
|
+
</UnorderedList>
|
26
|
+
</ContainerBlockElement>
|
data/spec/nexpose_upload_spec.rb
CHANGED
@@ -54,5 +54,157 @@ describe 'Nexpose upload plugin' do
|
|
54
54
|
expect(@result).to include('n/a')
|
55
55
|
end
|
56
56
|
end
|
57
|
+
|
58
|
+
describe 'Importer: Full' do
|
59
|
+
it 'creates nodes, issues, notes and an evidences as needed' do
|
60
|
+
expect(@content_service).to receive(:create_node).with(hash_including label: 'Nexpose Scan Summary').once
|
61
|
+
expect(@content_service).to receive(:create_note) do |args|
|
62
|
+
expect(args[:text]).to include("#[Title]#\nUSDA_Internal (4)")
|
63
|
+
expect(args[:node].label).to eq('Nexpose Scan Summary')
|
64
|
+
end.once
|
65
|
+
|
66
|
+
expect(@content_service).to receive(:create_node) do |args|
|
67
|
+
expect(args[:label]).to eq('1.1.1.1')
|
68
|
+
expect(args[:type]).to eq(:host)
|
69
|
+
create(:node, args.except(:type))
|
70
|
+
end
|
71
|
+
|
72
|
+
expect(@content_service).to receive(:create_note) do |args|
|
73
|
+
expect(args[:text]).to include("#[Title]#\n1.1.1.1")
|
74
|
+
expect(args[:node].label).to eq('1.1.1.1')
|
75
|
+
end.once
|
76
|
+
|
77
|
+
expect(@content_service).to receive(:create_note) do |args|
|
78
|
+
expect(args[:text]).to include("#[Title]#\nService name: NTP")
|
79
|
+
expect(args[:node].label).to eq('1.1.1.1')
|
80
|
+
end.once
|
81
|
+
|
82
|
+
expect(@content_service).to receive(:create_note) do |args|
|
83
|
+
expect(args[:text]).to include("#[Title]#\nService name: SNMP")
|
84
|
+
expect(args[:node].label).to eq('1.1.1.1')
|
85
|
+
end.once
|
86
|
+
|
87
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
88
|
+
expect(args[:text]).to include("#[Title]#\nApache HTTPD: error responses can expose cookies (CVE-2012-0053)")
|
89
|
+
expect(args[:id]).to eq('ntp-clock-variables-disclosure')
|
90
|
+
OpenStruct.new(args)
|
91
|
+
end.once
|
92
|
+
|
93
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
94
|
+
expect(args[:text]).to include("#[Title]#\nApache HTTPD: ETag Inode Information Leakage (CVE-2003-1418)")
|
95
|
+
expect(args[:id]).to eq('test-02')
|
96
|
+
OpenStruct.new(args)
|
97
|
+
end.once
|
98
|
+
|
99
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
100
|
+
expect(args[:content]).to include("#[ID]#\nntp-clock-variables-disclosure\n\n")
|
101
|
+
expect(args[:issue].id).to eq('ntp-clock-variables-disclosure')
|
102
|
+
expect(args[:node].label).to eq('1.1.1.1')
|
103
|
+
end.once
|
104
|
+
|
105
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
106
|
+
expect(args[:content]).to include("#[ID]#\ntest-02\n\n")
|
107
|
+
expect(args[:issue].id).to eq('test-02')
|
108
|
+
expect(args[:node].label).to eq('1.1.1.1')
|
109
|
+
end.once
|
110
|
+
|
111
|
+
@importer.import(file: @fixtures_dir + '/full.xml')
|
112
|
+
|
113
|
+
expect(Node.find_by(label: '1.1.1.1').properties[:os]).to eq('IOS')
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'wraps ciphers inside ssl issues in code blocks' do
|
117
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
118
|
+
expect(args[:text]).to include('bc. ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256')
|
119
|
+
OpenStruct.new(args)
|
120
|
+
end.once
|
121
|
+
|
122
|
+
@importer.import(file: @fixtures_dir + '/ssl.xml')
|
123
|
+
end
|
124
|
+
|
125
|
+
# Regression test for github.com/dradis/dradis-nexpose/issues/1
|
126
|
+
it 'populates solutions regardless of if they are wrapped in paragraphs or lists' do
|
127
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
128
|
+
expect(args[:text]).to include("#[Solution]#\n\nApache HTTPD >= 2.0 and < 2.0.65")
|
129
|
+
OpenStruct.new(args)
|
130
|
+
end.once
|
131
|
+
|
132
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
133
|
+
expect(args[:text]).to include("#[Solution]#\n")
|
134
|
+
expect(args[:text]).to include('You can remove inode information from the ETag header')
|
135
|
+
OpenStruct.new(args)
|
136
|
+
end.once
|
137
|
+
|
138
|
+
@importer.import(file: @fixtures_dir + '/full.xml')
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'populates tests regardless of if they contain paragraphs or containerblockelements' do
|
142
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
143
|
+
expect(args[:content]).to include("#[Content]#\nThe following NTP variables")
|
144
|
+
OpenStruct.new(args)
|
145
|
+
end.once
|
146
|
+
|
147
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
148
|
+
expect(args[:content]).to include("#[Content]#\nVulnerable URL:")
|
149
|
+
OpenStruct.new(args)
|
150
|
+
end.once
|
151
|
+
|
152
|
+
@importer.import(file: @fixtures_dir + '/full.xml')
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'transforms html entities (< and >)' do
|
156
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
157
|
+
expect(args[:text]).to include("#[Solution]#\n\nApache HTTPD >= 2.0 and < 2.0.65")
|
158
|
+
OpenStruct.new(args)
|
159
|
+
end
|
160
|
+
|
161
|
+
@importer.import(file: @fixtures_dir + '/full.xml')
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'formats the list to textile lists' do
|
165
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
166
|
+
expect(args[:text]).to include("#[Title]#\nApache HTTPD: error responses can expose cookies (CVE-2012-0053)")
|
167
|
+
OpenStruct.new(args)
|
168
|
+
end.once
|
169
|
+
|
170
|
+
expect(@content_service).to receive(:create_issue) do |args|
|
171
|
+
expect(args[:text]).to include('* Microsoft Windows')
|
172
|
+
expect(args[:text]).to include('## Open the Windows Control Panel.')
|
173
|
+
expect(args[:text]).to include('## Select "Administrative Tools".')
|
174
|
+
OpenStruct.new(args)
|
175
|
+
end.once
|
176
|
+
|
177
|
+
@importer.import(file: @fixtures_dir + '/full.xml')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe 'Importer: Full with duplicate nodes' do
|
182
|
+
it 'creates evidence for each instance of the node' do
|
183
|
+
expect(@content_service).to receive(:create_node).with(hash_including label: 'Nexpose Scan Summary').once
|
184
|
+
expect(@content_service).to receive(:create_node) do |args|
|
185
|
+
expect(args[:label]).to eq('1.1.1.1')
|
186
|
+
expect(args[:type]).to eq(:host)
|
187
|
+
create(:node, args.except(:type))
|
188
|
+
end
|
189
|
+
|
190
|
+
expect(@content_service).to receive(:create_evidence) do |args|
|
191
|
+
expect(args[:content]).to include("#[ID]#\nntp-clock-variables-disclosure\n\n")
|
192
|
+
expect(args[:issue].id).to eq('ntp-clock-variables-disclosure')
|
193
|
+
expect(args[:node].label).to eq('1.1.1.1')
|
194
|
+
end.twice
|
195
|
+
|
196
|
+
@importer.import(file: @fixtures_dir + '/full_with_duplicate_node.xml')
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'parses the fingerprints field' do
|
202
|
+
doc = Nokogiri::XML(File.read(@fixtures_dir + '/full.xml'))
|
203
|
+
|
204
|
+
ts = Dradis::Plugins::TemplateService.new(plugin: Dradis::Plugins::Nexpose)
|
205
|
+
ts.set_template(template: 'full_node', content: "#[Fingerprints]#\n%node.fingerprints%\n")
|
206
|
+
result = ts.process_template(data: doc.at_xpath('//nodes/node'), template: 'full_node')
|
207
|
+
|
208
|
+
expect(result).to include('IOS')
|
57
209
|
end
|
58
210
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Run the spec by running the command: rspec spec/xml_formatter_spec.rb"
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Dradis::Plugins::Nexpose::XmlFormatter do
|
6
|
+
let(:source) { File.read(File.expand_path('../fixtures/files/lists.xml', __FILE__)) }
|
7
|
+
|
8
|
+
it 'parses the <UnorderedList> and <OrderedList> elements' do
|
9
|
+
xml = Nokogiri::XML(source).at_xpath('./ContainerBlockElement')
|
10
|
+
expect(Dradis::Plugins::Nexpose::XmlFormatter.new.format_html_content(xml)).to eq(
|
11
|
+
"* Microsoft Windows\n"\
|
12
|
+
"## Open the Windows Control Panel.\n"\
|
13
|
+
"## Select \"Administrative Tools\".\n"\
|
14
|
+
"* Microsoft Windows\n"\
|
15
|
+
"## Open the \"Performance and Maintenance\" control panel.\n"\
|
16
|
+
"## Select \"Administrative Tools\".\n"\
|
17
|
+
"## Restart the system for the changes to take effect.\n"\
|
18
|
+
"* Microsoft Windows\n"\
|
19
|
+
"## Open the \"Administrative Tools\" control panel.\n"\
|
20
|
+
'## Restart the system for the changes to take effect.'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dradis-nexpose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.16.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:
|
11
|
+
date: 2025-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dradis-plugins
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- lib/dradis/plugins/nexpose/mapping.rb
|
123
123
|
- lib/dradis/plugins/nexpose/simple/importer.rb
|
124
124
|
- lib/dradis/plugins/nexpose/version.rb
|
125
|
+
- lib/dradis/plugins/nexpose/xml_formatter.rb
|
125
126
|
- lib/nexpose/endpoint.rb
|
126
127
|
- lib/nexpose/node.rb
|
127
128
|
- lib/nexpose/scan.rb
|
@@ -131,12 +132,14 @@ files:
|
|
131
132
|
- lib/tasks/thorfile.rb
|
132
133
|
- spec/fixtures/files/full.xml
|
133
134
|
- spec/fixtures/files/full_with_duplicate_node.xml
|
135
|
+
- spec/fixtures/files/lists.xml
|
134
136
|
- spec/fixtures/files/simple.xml
|
135
137
|
- spec/fixtures/files/ssl.xml
|
136
138
|
- spec/nexpose/full/importer_spec.rb
|
137
139
|
- spec/nexpose/simple/importer_spec.rb
|
138
140
|
- spec/nexpose_upload_spec.rb
|
139
141
|
- spec/spec_helper.rb
|
142
|
+
- spec/xml_formatter_spec.rb
|
140
143
|
- templates/full_evidence.sample
|
141
144
|
- templates/full_node.sample
|
142
145
|
- templates/full_scan.sample
|
@@ -169,9 +172,11 @@ summary: Nexpose add-on for the Dradis Framework.
|
|
169
172
|
test_files:
|
170
173
|
- spec/fixtures/files/full.xml
|
171
174
|
- spec/fixtures/files/full_with_duplicate_node.xml
|
175
|
+
- spec/fixtures/files/lists.xml
|
172
176
|
- spec/fixtures/files/simple.xml
|
173
177
|
- spec/fixtures/files/ssl.xml
|
174
178
|
- spec/nexpose/full/importer_spec.rb
|
175
179
|
- spec/nexpose/simple/importer_spec.rb
|
176
180
|
- spec/nexpose_upload_spec.rb
|
177
181
|
- spec/spec_helper.rb
|
182
|
+
- spec/xml_formatter_spec.rb
|