ruby-nmap 0.6.0 → 0.7.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/.gitignore +3 -0
- data/.travis.yml +13 -0
- data/ChangeLog.md +24 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +1 -1
- data/README.md +11 -9
- data/Rakefile +20 -24
- data/gemspec.yml +1 -3
- data/lib/nmap/cpe.rb +2 -0
- data/lib/nmap/cpe/cpe.rb +45 -0
- data/lib/nmap/cpe/url.rb +78 -0
- data/lib/nmap/hop.rb +20 -0
- data/lib/nmap/host.rb +69 -15
- data/lib/nmap/hostname.rb +20 -0
- data/lib/nmap/os.rb +2 -17
- data/lib/nmap/os_class.rb +65 -1
- data/lib/nmap/port.rb +10 -0
- data/lib/nmap/run_stat.rb +20 -0
- data/lib/nmap/scan_task.rb +4 -19
- data/lib/nmap/sequence.rb +6 -18
- data/lib/nmap/service.rb +76 -0
- data/lib/nmap/tcp_sequence.rb +1 -1
- data/lib/nmap/traceroute.rb +67 -0
- data/lib/nmap/uptime.rb +20 -0
- data/lib/nmap/version.rb +1 -1
- data/lib/nmap/xml.rb +134 -17
- data/spec/address_spec.rb +14 -0
- data/spec/cpe/url_spec.rb +99 -0
- data/spec/cpe_examples.rb +11 -0
- data/spec/hop_spec.rb +14 -0
- data/spec/host_spec.rb +138 -55
- data/spec/hostname_spec.rb +15 -0
- data/spec/ip_id_sequence_spec.rb +24 -10
- data/spec/os_class_spec.rb +31 -0
- data/spec/os_match_spec.rb +15 -0
- data/spec/os_spec.rb +23 -20
- data/spec/port_spec.rb +14 -15
- data/spec/run_stat_spec.rb +21 -0
- data/spec/scan.xml +137 -0
- data/spec/scan_spec.rb +28 -0
- data/spec/scan_task_spec.rb +35 -0
- data/spec/scanner_spec.rb +24 -0
- data/spec/scripts_examples.rb +10 -0
- data/spec/sequence_examples.rb +10 -0
- data/spec/service_spec.rb +55 -18
- data/spec/spec_helper.rb +30 -3
- data/spec/status_spec.rb +15 -0
- data/spec/tcp_sequence_spec.rb +32 -19
- data/spec/tcp_ts_sequence_spec.rb +24 -10
- data/spec/traceroute_spec.rb +31 -0
- data/spec/uptime_spec.rb +15 -0
- data/spec/xml_spec.rb +172 -31
- metadata +50 -54
- data/.gemtest +0 -0
- data/spec/helpers/nse.xml +0 -94
- data/spec/helpers/scan.xml +0 -241
- data/spec/helpers/xml.rb +0 -5
data/lib/nmap/uptime.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Nmap
|
2
|
+
#
|
3
|
+
# Wraps a `uptime` XML element.
|
4
|
+
#
|
5
|
+
# @since 0.7.0
|
6
|
+
#
|
7
|
+
class Uptime < Struct.new(:seconds, :last_boot)
|
8
|
+
|
9
|
+
#
|
10
|
+
# Converts the uptime object to a String.
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
# The String form of the Uptime.
|
14
|
+
#
|
15
|
+
def to_s
|
16
|
+
"uptime: #{self.seconds} (#{self.last_boot})"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/nmap/version.rb
CHANGED
data/lib/nmap/xml.rb
CHANGED
@@ -2,6 +2,7 @@ require 'nmap/scanner'
|
|
2
2
|
require 'nmap/scan_task'
|
3
3
|
require 'nmap/scan'
|
4
4
|
require 'nmap/host'
|
5
|
+
require 'nmap/run_stat'
|
5
6
|
|
6
7
|
require 'nokogiri'
|
7
8
|
|
@@ -19,8 +20,8 @@ module Nmap
|
|
19
20
|
#
|
20
21
|
# Creates a new XML object.
|
21
22
|
#
|
22
|
-
# @param [String]
|
23
|
-
# The path to the Nmap XML scan file.
|
23
|
+
# @param [Nokogiri::XML::Document, IO, String] document
|
24
|
+
# The path to the Nmap XML scan file or Nokogiri::XML::Document.
|
24
25
|
#
|
25
26
|
# @yield [xml]
|
26
27
|
# If a block is given, it will be passed the new XML object.
|
@@ -28,13 +29,56 @@ module Nmap
|
|
28
29
|
# @yieldparam [XML] xml
|
29
30
|
# The newly created XML object.
|
30
31
|
#
|
31
|
-
def initialize(
|
32
|
-
|
33
|
-
|
32
|
+
def initialize(document)
|
33
|
+
case document
|
34
|
+
when Nokogiri::XML::Document
|
35
|
+
@doc = document
|
36
|
+
when IO, StringIO
|
37
|
+
@doc = Nokogiri::XML(document)
|
38
|
+
else
|
39
|
+
@path = File.expand_path(document)
|
40
|
+
@doc = Nokogiri::XML(open(@path))
|
41
|
+
end
|
34
42
|
|
35
43
|
yield self if block_given?
|
36
44
|
end
|
37
45
|
|
46
|
+
#
|
47
|
+
# Creates a new XML object from XML text.
|
48
|
+
#
|
49
|
+
# @param [String] text
|
50
|
+
# XML text of the scan file
|
51
|
+
#
|
52
|
+
# @yield [xml]
|
53
|
+
# If a block is given, it will be passed the new XML object.
|
54
|
+
#
|
55
|
+
# @yieldparam [XML] xml
|
56
|
+
# The newly created XML object.
|
57
|
+
#
|
58
|
+
# @since 0.7.0
|
59
|
+
#
|
60
|
+
def self.load(text,&block)
|
61
|
+
new(Nokogiri::XML(text), &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Creates a new XML object from the file.
|
66
|
+
#
|
67
|
+
# @param [String] path
|
68
|
+
# The path to the XML file.
|
69
|
+
#
|
70
|
+
# @yield [xml]
|
71
|
+
# If a block is given, it will be passed the new XML object.
|
72
|
+
#
|
73
|
+
# @yieldparam [XML] xml
|
74
|
+
# The newly created XML object.
|
75
|
+
#
|
76
|
+
# @since 0.7.0
|
77
|
+
#
|
78
|
+
def self.open(path,&block)
|
79
|
+
new(path,&block)
|
80
|
+
end
|
81
|
+
|
38
82
|
#
|
39
83
|
# Parses the scanner information.
|
40
84
|
#
|
@@ -82,6 +126,47 @@ module Nmap
|
|
82
126
|
end
|
83
127
|
end
|
84
128
|
|
129
|
+
#
|
130
|
+
# Parses the essential runstats information.
|
131
|
+
#
|
132
|
+
# @yield [run_stat]
|
133
|
+
# The given block will be passed each runstat.
|
134
|
+
#
|
135
|
+
# @yieldparam [RunStat] run_stat
|
136
|
+
# A runstat.
|
137
|
+
#
|
138
|
+
# @return [Enumerator]
|
139
|
+
# If no block is given, an enumerator will be returned.
|
140
|
+
#
|
141
|
+
# @since 0.7.0
|
142
|
+
#
|
143
|
+
def each_run_stat
|
144
|
+
return enum_for(__method__) unless block_given?
|
145
|
+
|
146
|
+
@doc.xpath('/nmaprun/runstats/finished').each do |run_stat|
|
147
|
+
yield RunStat.new(
|
148
|
+
Time.at(run_stat['time'].to_i),
|
149
|
+
run_stat['elapsed'],
|
150
|
+
run_stat['summary'],
|
151
|
+
run_stat['exit']
|
152
|
+
)
|
153
|
+
end
|
154
|
+
|
155
|
+
return self
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Parses the essential runstats information.
|
160
|
+
#
|
161
|
+
# @return [Array<RunStat>]
|
162
|
+
# The runstats.
|
163
|
+
#
|
164
|
+
# @since 0.7.0
|
165
|
+
#
|
166
|
+
def run_stats
|
167
|
+
each_run_stat.to_a
|
168
|
+
end
|
169
|
+
|
85
170
|
#
|
86
171
|
# Parses the verbose level.
|
87
172
|
#
|
@@ -105,22 +190,44 @@ module Nmap
|
|
105
190
|
#
|
106
191
|
# Parses the tasks of the scan.
|
107
192
|
#
|
108
|
-
# @
|
109
|
-
# The
|
193
|
+
# @yield [task]
|
194
|
+
# The given block will be passed each scan task.
|
110
195
|
#
|
111
|
-
# @
|
196
|
+
# @yieldparam [ScanTask] task
|
197
|
+
# A task from the scan.
|
112
198
|
#
|
113
|
-
|
114
|
-
|
199
|
+
# @return [Enumerator]
|
200
|
+
# If no block is given, an enumerator will be returned.
|
201
|
+
#
|
202
|
+
# @since 0.7.0
|
203
|
+
#
|
204
|
+
def each_task
|
205
|
+
return enum_for(__method__) unless block_given?
|
206
|
+
|
207
|
+
@doc.xpath('/nmaprun/taskbegin').each do |task_begin|
|
115
208
|
task_end = task_begin.xpath('following-sibling::taskend').first
|
116
209
|
|
117
|
-
ScanTask.new(
|
210
|
+
yield ScanTask.new(
|
118
211
|
task_begin['task'],
|
119
212
|
Time.at(task_begin['time'].to_i),
|
120
213
|
Time.at(task_end['time'].to_i),
|
121
214
|
task_end['extrainfo']
|
122
215
|
)
|
123
216
|
end
|
217
|
+
|
218
|
+
return self
|
219
|
+
end
|
220
|
+
|
221
|
+
#
|
222
|
+
# Parses the tasks of the scan.
|
223
|
+
#
|
224
|
+
# @return [Array<ScanTask>]
|
225
|
+
# The tasks of the scan.
|
226
|
+
#
|
227
|
+
# @since 0.1.2
|
228
|
+
#
|
229
|
+
def tasks
|
230
|
+
each_task.to_a
|
124
231
|
end
|
125
232
|
|
126
233
|
#
|
@@ -136,11 +243,11 @@ module Nmap
|
|
136
243
|
# The XML object. If no block was given, an enumerator object will
|
137
244
|
# be returned.
|
138
245
|
#
|
139
|
-
def each_host
|
140
|
-
return enum_for(__method__) unless
|
246
|
+
def each_host
|
247
|
+
return enum_for(__method__) unless block_given?
|
141
248
|
|
142
249
|
@doc.xpath('/nmaprun/host').each do |host|
|
143
|
-
Host.new(host
|
250
|
+
yield Host.new(host)
|
144
251
|
end
|
145
252
|
|
146
253
|
return self
|
@@ -169,11 +276,11 @@ module Nmap
|
|
169
276
|
# The XML parser. If no block was given, an enumerator object will
|
170
277
|
# be returned.
|
171
278
|
#
|
172
|
-
def each_up_host
|
173
|
-
return enum_for(__method__) unless
|
279
|
+
def each_up_host
|
280
|
+
return enum_for(__method__) unless block_given?
|
174
281
|
|
175
282
|
@doc.xpath("/nmaprun/host[status[@state='up']]").each do |host|
|
176
|
-
Host.new(host
|
283
|
+
yield Host.new(host)
|
177
284
|
end
|
178
285
|
|
179
286
|
return self
|
@@ -208,5 +315,15 @@ module Nmap
|
|
208
315
|
@path.to_s
|
209
316
|
end
|
210
317
|
|
318
|
+
#
|
319
|
+
# Inspects the XML file.
|
320
|
+
#
|
321
|
+
# @return [String]
|
322
|
+
# The inspected XML file.
|
323
|
+
#
|
324
|
+
def inspect
|
325
|
+
"#<#{self.class}: #{self}>"
|
326
|
+
end
|
327
|
+
|
211
328
|
end
|
212
329
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nmap/address'
|
3
|
+
|
4
|
+
describe Address do
|
5
|
+
describe "#to_s" do
|
6
|
+
let(:addr) { '127.0.0.1' }
|
7
|
+
|
8
|
+
subject { described_class.new(:ipv4, addr) }
|
9
|
+
|
10
|
+
it "should return the address" do
|
11
|
+
subject.to_s.should == addr
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CPE::URL do
|
4
|
+
describe "parse" do
|
5
|
+
context "when the URL does not start with 'cpe:'" do
|
6
|
+
it "should raise an ArgumentError" do
|
7
|
+
lambda {
|
8
|
+
described_class.parse('foo:')
|
9
|
+
}.should raise_error(ArgumentError)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when blank fields are omitted" do
|
14
|
+
let(:vendor) { :linux }
|
15
|
+
let(:product) { :linux_kernel }
|
16
|
+
let(:version) { '2.6.39' }
|
17
|
+
|
18
|
+
subject do
|
19
|
+
described_class.parse("cpe:/o:#{vendor}:#{product}:#{version}")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should leave them nil" do
|
23
|
+
subject.vendor.should == vendor
|
24
|
+
subject.product.should == product
|
25
|
+
subject.version.should == version
|
26
|
+
|
27
|
+
subject.update.should be_nil
|
28
|
+
subject.edition.should be_nil
|
29
|
+
subject.language.should be_nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when part is /h" do
|
34
|
+
subject { described_class.parse("cpe:/h:foo:bar:baz") }
|
35
|
+
|
36
|
+
it "should parse it as :hardware" do
|
37
|
+
subject.part.should == :hardware
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when part is /a" do
|
42
|
+
subject { described_class.parse("cpe:/a:foo:bar:baz") }
|
43
|
+
|
44
|
+
it "should parse it as :application" do
|
45
|
+
subject.part.should == :application
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when part is /o" do
|
50
|
+
subject { described_class.parse("cpe:/o:foo:bar:baz") }
|
51
|
+
|
52
|
+
it "should parse it as :os" do
|
53
|
+
subject.part.should == :os
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#to_s" do
|
59
|
+
let(:vendor) { :linux }
|
60
|
+
let(:product) { :linux_kernel }
|
61
|
+
let(:version) { '2.6.39' }
|
62
|
+
|
63
|
+
it "should add the scheme 'cpe:'" do
|
64
|
+
subject.to_s.should start_with('cpe:')
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when fields are nil" do
|
68
|
+
subject { described_class.new(:os,vendor,product,version) }
|
69
|
+
|
70
|
+
it "should omit them" do
|
71
|
+
subject.to_s.should == "cpe:/o:#{vendor}:#{product}:#{version}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when part is :hardware" do
|
76
|
+
subject { described_class.new(:hardware) }
|
77
|
+
|
78
|
+
it "should map it to /h" do
|
79
|
+
subject.to_s.should == "cpe:/h"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when part is :application" do
|
84
|
+
subject { described_class.new(:application) }
|
85
|
+
|
86
|
+
it "should map it to /h" do
|
87
|
+
subject.to_s.should == "cpe:/a"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when part is :os" do
|
92
|
+
subject { described_class.new(:os) }
|
93
|
+
|
94
|
+
it "should map it to /h" do
|
95
|
+
subject.to_s.should == "cpe:/o"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/spec/hop_spec.rb
ADDED
data/spec/host_spec.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'scripts_examples'
|
2
3
|
|
3
4
|
require 'nmap/xml'
|
4
5
|
require 'nmap/host'
|
5
6
|
|
6
7
|
describe Host do
|
7
|
-
|
8
|
-
|
9
|
-
subject { xml.hosts.first }
|
8
|
+
subject { @xml.hosts.first }
|
10
9
|
|
11
10
|
it "should parse the start_time" do
|
12
11
|
subject.start_time.should > Time.at(0)
|
@@ -17,91 +16,175 @@ describe Host do
|
|
17
16
|
subject.end_time.should > subject.start_time
|
18
17
|
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
describe "#uptime" do
|
20
|
+
subject { super().uptime }
|
21
|
+
|
22
|
+
it "should return an Uptime object" do
|
23
|
+
subject.should be_kind_of(Uptime)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should parse the seconds attribute" do
|
27
|
+
subject.seconds.should be > 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should parse the lastboot attribute" do
|
31
|
+
subject.last_boot.should be_kind_of(Time)
|
32
|
+
end
|
25
33
|
end
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
addresses.length.should == 2
|
35
|
+
describe "#tcp_sequence" do
|
36
|
+
subject { super().tcp_sequence }
|
31
37
|
|
32
|
-
|
33
|
-
|
38
|
+
it { should be_kind_of(TcpSequence) }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#ip_ip_sequence" do
|
42
|
+
subject { super().ip_id_sequence }
|
34
43
|
|
35
|
-
|
36
|
-
addresses[1].addr.should == '00:1D:7E:EF:2A:E5'
|
44
|
+
it { should be_kind_of(IpIdSequence) }
|
37
45
|
end
|
38
46
|
|
39
|
-
|
40
|
-
subject.
|
47
|
+
describe "#tcp_ts_sequence" do
|
48
|
+
subject { super().tcp_ts_sequence }
|
49
|
+
|
50
|
+
it { should be_kind_of(TcpTsSequence) }
|
41
51
|
end
|
42
52
|
|
43
|
-
|
44
|
-
subject
|
53
|
+
describe "#status" do
|
54
|
+
subject { super().status }
|
55
|
+
|
56
|
+
it "should parse the state" do
|
57
|
+
subject.state.should be_one_of(:up, :down)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should parse the reason" do
|
61
|
+
subject.reason.should be_one_of(
|
62
|
+
'syn-ack',
|
63
|
+
'timestamp-reply',
|
64
|
+
'echo-reply',
|
65
|
+
'reset'
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#addresses" do
|
71
|
+
subject { super().addresses.first }
|
72
|
+
|
73
|
+
it "should parse the type" do
|
74
|
+
subject.type.should == :ipv4
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should parser the addr" do
|
78
|
+
subject.addr.should == '74.207.244.221'
|
79
|
+
end
|
45
80
|
end
|
46
81
|
|
47
|
-
|
48
|
-
|
82
|
+
describe "#mac" do
|
83
|
+
it "should parse the first MAC address" do
|
84
|
+
pending "need a host with address[@addrtype='mac']"
|
85
|
+
end
|
49
86
|
end
|
50
87
|
|
51
|
-
|
52
|
-
|
88
|
+
describe "#ipv6" do
|
89
|
+
it "should parse the first IPv6 address" do
|
90
|
+
pending "need a host with address[@addrtype='ipv6']"
|
91
|
+
end
|
53
92
|
end
|
54
93
|
|
55
|
-
|
56
|
-
|
94
|
+
describe "#ipv4" do
|
95
|
+
it "should parse the IPv4 address" do
|
96
|
+
subject.ipv4.should == '74.207.244.221'
|
97
|
+
end
|
57
98
|
end
|
58
99
|
|
59
|
-
|
60
|
-
|
100
|
+
describe "#ip" do
|
101
|
+
it "should have an IP" do
|
102
|
+
subject.ip.should == '74.207.244.221'
|
103
|
+
end
|
61
104
|
end
|
62
105
|
|
63
|
-
|
64
|
-
|
106
|
+
describe "#address" do
|
107
|
+
it "should have an address" do
|
108
|
+
subject.address.should == '74.207.244.221'
|
109
|
+
end
|
65
110
|
end
|
66
111
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
112
|
+
describe "#hostnames" do
|
113
|
+
subject { super().hostnames }
|
114
|
+
|
115
|
+
it { should_not be_empty }
|
116
|
+
|
117
|
+
it "should parse the type" do
|
118
|
+
subject.all? { |hostname| hostname.type }.should be_true
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should parse the name" do
|
122
|
+
subject.all? { |hostname| hostname.name }.should be_true
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should include a user hostname" do
|
126
|
+
subject.any? { |hostname| hostname.type == 'user' }.should be_true
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should include a PTR hostname" do
|
130
|
+
subject.any? { |hostname| hostname.type == 'PTR' }.should be_true
|
131
|
+
end
|
71
132
|
end
|
72
133
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
134
|
+
describe "#os" do
|
135
|
+
subject { super().os }
|
136
|
+
|
137
|
+
it { should_not be_nil }
|
138
|
+
it { should be_kind_of(OS) }
|
78
139
|
end
|
79
140
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
ports.all? { |port| port.protocol == :tcp }.should == true
|
141
|
+
describe "#ports" do
|
142
|
+
subject { super().ports }
|
143
|
+
|
144
|
+
it { should_not be_empty }
|
85
145
|
end
|
86
146
|
|
87
|
-
|
88
|
-
|
147
|
+
describe "#open_ports" do
|
148
|
+
subject { super().open_ports }
|
149
|
+
|
150
|
+
it { should_not be_empty }
|
151
|
+
|
152
|
+
it "should list the open ports" do
|
153
|
+
subject.all? { |port| port.state == :open }.should be_true
|
154
|
+
end
|
89
155
|
end
|
90
156
|
|
91
|
-
|
92
|
-
subject
|
157
|
+
describe "#tcp_ports" do
|
158
|
+
subject { super().tcp_ports }
|
159
|
+
|
160
|
+
it { should_not be_empty }
|
161
|
+
|
162
|
+
it "should contain TCP ports" do
|
163
|
+
subject.all? { |port| port.protocol == :tcp }.should be_true
|
164
|
+
end
|
93
165
|
end
|
94
166
|
|
95
|
-
|
96
|
-
|
167
|
+
describe "#udp_ports" do
|
168
|
+
subject { super().udp_ports }
|
169
|
+
|
170
|
+
it { should_not be_empty }
|
97
171
|
|
98
|
-
|
172
|
+
it "should contain UDP ports" do
|
173
|
+
subject.all? { |port| port.protocol == :udp }.should be_true
|
174
|
+
end
|
175
|
+
end
|
99
176
|
|
100
|
-
|
101
|
-
|
177
|
+
it "should convert to a String" do
|
178
|
+
subject.to_s.should == '74.207.244.221'
|
179
|
+
end
|
102
180
|
|
103
|
-
|
104
|
-
|
181
|
+
describe "#inspect" do
|
182
|
+
it "should include the address" do
|
183
|
+
subject.inspect.should include(subject.address)
|
105
184
|
end
|
106
185
|
end
|
186
|
+
|
187
|
+
pending "scan.xml does not currently include any hostscripts" do
|
188
|
+
include_examples "#scripts"
|
189
|
+
end
|
107
190
|
end
|