ruby-nikto 0.1.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 +7 -0
- data/.editorconfig +11 -0
- data/.github/workflows/ruby.yml +31 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.specopts +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +9 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +22 -0
- data/README.md +80 -0
- data/Rakefile +10 -0
- data/gemspec.yml +24 -0
- data/lib/nikto/command.rb +188 -0
- data/lib/nikto/version.rb +4 -0
- data/lib/nikto/xml/item.rb +55 -0
- data/lib/nikto/xml/scan_details.rb +175 -0
- data/lib/nikto/xml/statistics.rb +64 -0
- data/lib/nikto/xml.rb +230 -0
- data/lib/nikto.rb +2 -0
- data/ruby-nikto.gemspec +58 -0
- data/spec/command_spec.rb +97 -0
- data/spec/fixtures/nikto.xml +47 -0
- data/spec/nikto_spec.rb +8 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/xml/item_spec.rb +61 -0
- data/spec/xml/scan_details_spec.rb +181 -0
- data/spec/xml/statistics_spec.rb +57 -0
- data/spec/xml_spec.rb +190 -0
- metadata +121 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Nikto
|
4
|
+
class XML
|
5
|
+
#
|
6
|
+
# Represents a `statistics` XML element.
|
7
|
+
#
|
8
|
+
class Statistics
|
9
|
+
|
10
|
+
#
|
11
|
+
# Initializes the statistics object.
|
12
|
+
#
|
13
|
+
# @param [Nokogiri::XML::Node] node
|
14
|
+
# The XML node for the `statistics` XML element.
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
def initialize(node)
|
19
|
+
@node = node
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# The number of seconds elapsed.
|
24
|
+
#
|
25
|
+
# @return [Intger]
|
26
|
+
# The parsed value of the `elapsed` attribute.
|
27
|
+
#
|
28
|
+
def elapsed
|
29
|
+
@elapsed ||= @node['elapsed'].to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# The number of items found.
|
34
|
+
#
|
35
|
+
# @return [Intger]
|
36
|
+
# The parsed value of the `itemsfound` attribute.
|
37
|
+
#
|
38
|
+
def items_found
|
39
|
+
@items_found ||= @node['itemsfound'].to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# The number of items tested.
|
44
|
+
#
|
45
|
+
# @return [Intger]
|
46
|
+
# The parsed value of the `itemstested` attribute.
|
47
|
+
#
|
48
|
+
def items_tested
|
49
|
+
@items_tested ||= @node['itemstested'].to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# The end-time of the scan.
|
54
|
+
#
|
55
|
+
# @return [Time]
|
56
|
+
# The parsed value of the `endtime` attribute.
|
57
|
+
#
|
58
|
+
def end_time
|
59
|
+
@end_time ||= Time.parse(@node['endtime'])
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/nikto/xml.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'nikto/xml/scan_details'
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module Nikto
|
6
|
+
#
|
7
|
+
# Represents an nikto XML file or XML data.
|
8
|
+
#
|
9
|
+
# ## Example
|
10
|
+
#
|
11
|
+
# require 'nikto/xml'
|
12
|
+
#
|
13
|
+
# Nikto::XML.open('nikto.xml') do |xml|
|
14
|
+
# xml.each_scan_details do |scan_details|
|
15
|
+
# puts "#{scan_details.site_name}"
|
16
|
+
#
|
17
|
+
# scan_details.each_item do |item|
|
18
|
+
# puts " #{item.uri}"
|
19
|
+
# puts
|
20
|
+
# puts " #{item.description}"
|
21
|
+
# puts
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
class XML
|
27
|
+
|
28
|
+
# The parsed XML document.
|
29
|
+
#
|
30
|
+
# @return [Nokogiri::XML]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
attr_reader :doc
|
34
|
+
|
35
|
+
# The path to the XML file.
|
36
|
+
#
|
37
|
+
# @return [String, nil]
|
38
|
+
attr_reader :path
|
39
|
+
|
40
|
+
#
|
41
|
+
# Creates a new XML object.
|
42
|
+
#
|
43
|
+
# @param [Nokogiri::XML] doc
|
44
|
+
# The parsed XML document.
|
45
|
+
#
|
46
|
+
# @param [String, nil] path
|
47
|
+
# The path to the XML file.
|
48
|
+
#
|
49
|
+
# @yield [xml]
|
50
|
+
# If a block is given, it will be passed the newly created XML
|
51
|
+
# parser.
|
52
|
+
#
|
53
|
+
# @yieldparam [XML] xml
|
54
|
+
# The newly created XML parser.
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
def initialize(doc, path: nil)
|
59
|
+
@doc = doc
|
60
|
+
@path = File.expand_path(path) if path
|
61
|
+
|
62
|
+
yield self if block_given?
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Parses the given XML String.
|
67
|
+
#
|
68
|
+
# @param [String] xml
|
69
|
+
# The XML String.
|
70
|
+
#
|
71
|
+
# @yield [xml]
|
72
|
+
# If a block is given, it will be passed the newly created XML
|
73
|
+
# parser.
|
74
|
+
#
|
75
|
+
# @yieldparam [XML] xml
|
76
|
+
# The newly created XML parser.
|
77
|
+
#
|
78
|
+
# @return [XML]
|
79
|
+
# The parsed XML.
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
#
|
83
|
+
def self.parse(xml,&block)
|
84
|
+
new(Nokogiri::XML(xml),&block)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Opens an parses an XML file.
|
89
|
+
#
|
90
|
+
# @param [String] path
|
91
|
+
# The path to the XML file.
|
92
|
+
#
|
93
|
+
# @yield [xml]
|
94
|
+
# If a block is given, it will be passed the newly created XML
|
95
|
+
# parser.
|
96
|
+
#
|
97
|
+
# @yieldparam [XML] xml
|
98
|
+
# The newly created XML parser.
|
99
|
+
#
|
100
|
+
# @return [XML]
|
101
|
+
# The parsed XML.
|
102
|
+
#
|
103
|
+
# @api public
|
104
|
+
#
|
105
|
+
def self.open(path,&block)
|
106
|
+
path = File.expand_path(path)
|
107
|
+
doc = Nokogiri::XML(File.open(path))
|
108
|
+
|
109
|
+
new(doc, path: path, &block)
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# The `hoststest` value.
|
114
|
+
#
|
115
|
+
# @return [Integer]
|
116
|
+
# The parsed value of the `hoststest` attribute.
|
117
|
+
#
|
118
|
+
def hosts_test
|
119
|
+
@hosts_test ||= @doc.root['@hoststest'].to_i
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Additional command-line options passed to `nikto`.
|
124
|
+
#
|
125
|
+
# @return [String]
|
126
|
+
# The value of the `options` attribute.
|
127
|
+
#
|
128
|
+
def options
|
129
|
+
@doc.root['options']
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# When the scan started.
|
134
|
+
#
|
135
|
+
# @return [Time]
|
136
|
+
# The parsed value of the `scanstart` attribute.
|
137
|
+
#
|
138
|
+
def scan_start
|
139
|
+
@scan_start ||= Time.parse(@doc.root['scanstart'])
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# When the scan completed.
|
144
|
+
#
|
145
|
+
# @return [Time]
|
146
|
+
# The parsed value `scanned` attribute.
|
147
|
+
#
|
148
|
+
def scan_end
|
149
|
+
@scan_end ||= Time.parse(@doc.root['scanend'])
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# The duration of the scan.
|
154
|
+
#
|
155
|
+
# @return [String]
|
156
|
+
# The value of the `scanelapsed` attribute.
|
157
|
+
#
|
158
|
+
def scan_elapsed
|
159
|
+
@doc.root['scanelapsed']
|
160
|
+
end
|
161
|
+
|
162
|
+
#
|
163
|
+
# The Nikto XML schema version.
|
164
|
+
#
|
165
|
+
# @return [String]
|
166
|
+
# The value of the `nxmlversion` attribute.
|
167
|
+
#
|
168
|
+
def nikto_xml_version
|
169
|
+
@doc.root['nxmlversion']
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Parses each `scandetails` child element.
|
174
|
+
#
|
175
|
+
# @yield [scan_details]
|
176
|
+
# If a block is given, it will be yielded each scan details object.
|
177
|
+
#
|
178
|
+
# @yieldparam [ScanDetails] scan_details
|
179
|
+
# A scan details object.
|
180
|
+
#
|
181
|
+
# @return [Enumerator]
|
182
|
+
# If no block is given, an Enumerator will be returned.
|
183
|
+
#
|
184
|
+
def each_scan_details
|
185
|
+
return enum_for(__method__) unless block_given?
|
186
|
+
|
187
|
+
@doc.xpath('/niktoscan/scandetails').each do |node|
|
188
|
+
yield ScanDetails.new(node)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# The scan details.
|
194
|
+
#
|
195
|
+
# @return [Array<ScanDetails>]
|
196
|
+
#
|
197
|
+
def scan_details
|
198
|
+
each_scan_details.to_a
|
199
|
+
end
|
200
|
+
|
201
|
+
alias each_target each_scan_details
|
202
|
+
|
203
|
+
alias targets scan_details
|
204
|
+
|
205
|
+
#
|
206
|
+
# The first scan details object.
|
207
|
+
#
|
208
|
+
# @return [ScanDetails, nil]
|
209
|
+
#
|
210
|
+
def target
|
211
|
+
each_target.first
|
212
|
+
end
|
213
|
+
|
214
|
+
#
|
215
|
+
# Converts the XML object to a String.
|
216
|
+
#
|
217
|
+
# @return [String]
|
218
|
+
# The path to the XML if {#path} is set, or the XML if the XML was parsed
|
219
|
+
# from a String.
|
220
|
+
#
|
221
|
+
def to_s
|
222
|
+
if @path
|
223
|
+
@path
|
224
|
+
else
|
225
|
+
@doc.to_s
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
end
|
data/lib/nikto.rb
ADDED
data/ruby-nikto.gemspec
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gemspec = YAML.load_file('gemspec.yml')
|
5
|
+
|
6
|
+
gem.name = gemspec.fetch('name')
|
7
|
+
gem.version = gemspec.fetch('version') do
|
8
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
9
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
10
|
+
|
11
|
+
require File.join('nikto','version')
|
12
|
+
Nikto::VERSION
|
13
|
+
end
|
14
|
+
|
15
|
+
gem.summary = gemspec['summary']
|
16
|
+
gem.description = gemspec['description']
|
17
|
+
gem.licenses = Array(gemspec['license'])
|
18
|
+
gem.authors = Array(gemspec['authors'])
|
19
|
+
gem.email = gemspec['email']
|
20
|
+
gem.homepage = gemspec['homepage']
|
21
|
+
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
22
|
+
|
23
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
24
|
+
|
25
|
+
gem.files = if gemspec['files'] then glob[gemspec['files']]
|
26
|
+
else `git ls-files`.split($/)
|
27
|
+
end
|
28
|
+
|
29
|
+
gem.executables = gemspec.fetch('executables') do
|
30
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
31
|
+
end
|
32
|
+
|
33
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
34
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
35
|
+
|
36
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
37
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
38
|
+
})
|
39
|
+
|
40
|
+
gem.requirements = gemspec['requirements']
|
41
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
42
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
43
|
+
gem.post_install_message = gemspec['post_install_message']
|
44
|
+
|
45
|
+
split = lambda { |string| string.split(/,\s*/) }
|
46
|
+
|
47
|
+
if gemspec['dependencies']
|
48
|
+
gemspec['dependencies'].each do |name,versions|
|
49
|
+
gem.add_dependency(name,split[versions])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if gemspec['development_dependencies']
|
54
|
+
gemspec['development_dependencies'].each do |name,versions|
|
55
|
+
gem.add_development_dependency(name,split[versions])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nikto/command'
|
3
|
+
|
4
|
+
describe Nikto::Command do
|
5
|
+
describe described_class::OptionString do
|
6
|
+
let(:map) do
|
7
|
+
{
|
8
|
+
:a => '1',
|
9
|
+
:b => '2',
|
10
|
+
:c => '3'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { described_class.new(map) }
|
15
|
+
|
16
|
+
describe "#initialize" do
|
17
|
+
it "must initialize #type to be a Map of the options" do
|
18
|
+
expect(subject.type).to be_kind_of(CommandMapper::Types::Map)
|
19
|
+
expect(subject.type.map).to eq(map)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "must set #separator to ''" do
|
23
|
+
expect(subject.separator).to eq('')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe described_class::PortList do
|
29
|
+
describe "#validate" do
|
30
|
+
context "when given a single port number" do
|
31
|
+
let(:value) { 443 }
|
32
|
+
|
33
|
+
it "must return true" do
|
34
|
+
expect(subject.validate(value)).to be(true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when given a Range of port numbers" do
|
39
|
+
let(:value) { (1..1024) }
|
40
|
+
|
41
|
+
it "must return true" do
|
42
|
+
expect(subject.validate(value)).to be(true)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when given an Array of port numbers" do
|
47
|
+
let(:value) { [80, 443] }
|
48
|
+
|
49
|
+
it "must return true" do
|
50
|
+
expect(subject.validate(value)).to be(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "and the Array contains Ranges" do
|
54
|
+
let(:value) { [80, (1..42), 443] }
|
55
|
+
|
56
|
+
it "must return true" do
|
57
|
+
expect(subject.validate(value)).to be(true)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#format" do
|
64
|
+
context "when given a single port number" do
|
65
|
+
let(:value) { 443 }
|
66
|
+
|
67
|
+
it "must return the formatted port number" do
|
68
|
+
expect(subject.format(value)).to eq(value.to_s)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when given a Range of port numbers" do
|
73
|
+
let(:value) { (1..1024) }
|
74
|
+
|
75
|
+
it "must return the formatted port number range (ex: 1-102)" do
|
76
|
+
expect(subject.format(value)).to eq("#{value.begin}-#{value.end}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when given an Array of port numbers" do
|
81
|
+
let(:value) { [80, 443] }
|
82
|
+
|
83
|
+
it "must return the formatted list of port numbers" do
|
84
|
+
expect(subject.format(value)).to eq(value.join(','))
|
85
|
+
end
|
86
|
+
|
87
|
+
context "and the Array contains Ranges" do
|
88
|
+
let(:value) { [80, (1..42), 443] }
|
89
|
+
|
90
|
+
it "must return the formatted list of port numbers and port ranges" do
|
91
|
+
expect(subject.format(value)).to eq("#{value[0]},#{value[1].begin}-#{value[1].end},#{value[2]}")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<?xml version="1.0" ?>
|
2
|
+
<!DOCTYPE niktoscan SYSTEM "/usr/share/doc/nikto/nikto.dtd">
|
3
|
+
<niktoscan hoststest="0" options="-host example.com -output nikto.xml" version="2.1.6" scanstart="Mon Nov 29 07:22:56 2021" scanend="Wed Dec 31 16:00:00 1969" scanelapsed=" seconds" nxmlversion="1.2">
|
4
|
+
|
5
|
+
<scandetails targetip="93.184.216.34" targethostname="example.com" targetport="80" targetbanner="ECS (sec/974D)" starttime="2021-11-29 07:22:57" sitename="http://example.com:80/" siteip="http://93.184.216.34:80/" hostheader="example.com" errors="0" checks="4587">
|
6
|
+
|
7
|
+
|
8
|
+
<item id="#ID#" osvdbid="#TEMPL_OSVDB#" osvdblink="#TEMPL_OSVDB_LINK#" method="#TEMPL_HTTP_METHOD#">
|
9
|
+
<description><![CDATA[#TEMPL_MSG#]]></description>
|
10
|
+
<uri><![CDATA[#TEMPL_URI#]]></uri>
|
11
|
+
<namelink><![CDATA[#TEMPL_ITEM_NAME_LINK#]]></namelink>
|
12
|
+
<iplink><![CDATA[#TEMPL_ITEM_IP_LINK#]]></iplink>
|
13
|
+
</item>
|
14
|
+
|
15
|
+
<item id="#ID#" osvdbid="#TEMPL_OSVDB#" osvdblink="#TEMPL_OSVDB_LINK#" method="#TEMPL_HTTP_METHOD#">
|
16
|
+
<description><![CDATA[#TEMPL_MSG#]]></description>
|
17
|
+
<uri><![CDATA[#TEMPL_URI#]]></uri>
|
18
|
+
<namelink><![CDATA[#TEMPL_ITEM_NAME_LINK#]]></namelink>
|
19
|
+
<iplink><![CDATA[#TEMPL_ITEM_IP_LINK#]]></iplink>
|
20
|
+
</item>
|
21
|
+
|
22
|
+
<item id="#ID#" osvdbid="#TEMPL_OSVDB#" osvdblink="#TEMPL_OSVDB_LINK#" method="#TEMPL_HTTP_METHOD#">
|
23
|
+
<description><![CDATA[#TEMPL_MSG#]]></description>
|
24
|
+
<uri><![CDATA[#TEMPL_URI#]]></uri>
|
25
|
+
<namelink><![CDATA[#TEMPL_ITEM_NAME_LINK#]]></namelink>
|
26
|
+
<iplink><![CDATA[#TEMPL_ITEM_IP_LINK#]]></iplink>
|
27
|
+
</item>
|
28
|
+
|
29
|
+
<item id="#ID#" osvdbid="#TEMPL_OSVDB#" osvdblink="#TEMPL_OSVDB_LINK#" method="#TEMPL_HTTP_METHOD#">
|
30
|
+
<description><![CDATA[#TEMPL_MSG#]]></description>
|
31
|
+
<uri><![CDATA[#TEMPL_URI#]]></uri>
|
32
|
+
<namelink><![CDATA[#TEMPL_ITEM_NAME_LINK#]]></namelink>
|
33
|
+
<iplink><![CDATA[#TEMPL_ITEM_IP_LINK#]]></iplink>
|
34
|
+
</item>
|
35
|
+
|
36
|
+
<item id="#ID#" osvdbid="#TEMPL_OSVDB#" osvdblink="#TEMPL_OSVDB_LINK#" method="#TEMPL_HTTP_METHOD#">
|
37
|
+
<description><![CDATA[#TEMPL_MSG#]]></description>
|
38
|
+
<uri><![CDATA[#TEMPL_URI#]]></uri>
|
39
|
+
<namelink><![CDATA[#TEMPL_ITEM_NAME_LINK#]]></namelink>
|
40
|
+
<iplink><![CDATA[#TEMPL_ITEM_IP_LINK#]]></iplink>
|
41
|
+
</item>
|
42
|
+
|
43
|
+
<statistics elapsed="178" itemsfound="5" itemstested="4587" endtime="2021-11-29 07:25:55" />
|
44
|
+
</scandetails>
|
45
|
+
|
46
|
+
|
47
|
+
</niktoscan>
|
data/spec/nikto_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nikto/xml/item'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
describe Nikto::XML::Item do
|
6
|
+
let(:fixtures_dir) { File.expand_path(File.join(__dir__,'..','fixtures')) }
|
7
|
+
let(:path) { File.join(fixtures_dir,'nikto.xml') }
|
8
|
+
let(:xml) { File.read(path) }
|
9
|
+
let(:doc) { Nokogiri::XML(File.open(path)) }
|
10
|
+
let(:node) { doc.at_xpath('/niktoscan/scandetails/item') }
|
11
|
+
|
12
|
+
subject { described_class.new(node) }
|
13
|
+
|
14
|
+
describe "#description" do
|
15
|
+
subject { super().description }
|
16
|
+
|
17
|
+
it "must return a String" do
|
18
|
+
expect(subject).to be_kind_of(String)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "must return the inner text of the 'description' child element" do
|
22
|
+
expect(subject).to eq(node.at_xpath('description').inner_text)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#uri" do
|
27
|
+
subject { super().uri }
|
28
|
+
|
29
|
+
it "must return a String" do
|
30
|
+
expect(subject).to be_kind_of(String)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must return the inner text of the 'uri' child element" do
|
34
|
+
expect(subject).to eq(node.at_xpath('uri').inner_text)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#name_link" do
|
39
|
+
subject { super().name_link }
|
40
|
+
|
41
|
+
it "must return a String" do
|
42
|
+
expect(subject).to be_kind_of(String)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "must return the inner text of the 'namelink' child element" do
|
46
|
+
expect(subject).to eq(node.at_xpath('namelink').inner_text)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#ip_link" do
|
51
|
+
subject { super().ip_link }
|
52
|
+
|
53
|
+
it "must return a String" do
|
54
|
+
expect(subject).to be_kind_of(String)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "must return the inner text of the 'iplink' child element" do
|
58
|
+
expect(subject).to eq(node.at_xpath('iplink').inner_text)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|