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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +13 -0
  4. data/ChangeLog.md +24 -0
  5. data/Gemfile +12 -0
  6. data/LICENSE.txt +1 -1
  7. data/README.md +11 -9
  8. data/Rakefile +20 -24
  9. data/gemspec.yml +1 -3
  10. data/lib/nmap/cpe.rb +2 -0
  11. data/lib/nmap/cpe/cpe.rb +45 -0
  12. data/lib/nmap/cpe/url.rb +78 -0
  13. data/lib/nmap/hop.rb +20 -0
  14. data/lib/nmap/host.rb +69 -15
  15. data/lib/nmap/hostname.rb +20 -0
  16. data/lib/nmap/os.rb +2 -17
  17. data/lib/nmap/os_class.rb +65 -1
  18. data/lib/nmap/port.rb +10 -0
  19. data/lib/nmap/run_stat.rb +20 -0
  20. data/lib/nmap/scan_task.rb +4 -19
  21. data/lib/nmap/sequence.rb +6 -18
  22. data/lib/nmap/service.rb +76 -0
  23. data/lib/nmap/tcp_sequence.rb +1 -1
  24. data/lib/nmap/traceroute.rb +67 -0
  25. data/lib/nmap/uptime.rb +20 -0
  26. data/lib/nmap/version.rb +1 -1
  27. data/lib/nmap/xml.rb +134 -17
  28. data/spec/address_spec.rb +14 -0
  29. data/spec/cpe/url_spec.rb +99 -0
  30. data/spec/cpe_examples.rb +11 -0
  31. data/spec/hop_spec.rb +14 -0
  32. data/spec/host_spec.rb +138 -55
  33. data/spec/hostname_spec.rb +15 -0
  34. data/spec/ip_id_sequence_spec.rb +24 -10
  35. data/spec/os_class_spec.rb +31 -0
  36. data/spec/os_match_spec.rb +15 -0
  37. data/spec/os_spec.rb +23 -20
  38. data/spec/port_spec.rb +14 -15
  39. data/spec/run_stat_spec.rb +21 -0
  40. data/spec/scan.xml +137 -0
  41. data/spec/scan_spec.rb +28 -0
  42. data/spec/scan_task_spec.rb +35 -0
  43. data/spec/scanner_spec.rb +24 -0
  44. data/spec/scripts_examples.rb +10 -0
  45. data/spec/sequence_examples.rb +10 -0
  46. data/spec/service_spec.rb +55 -18
  47. data/spec/spec_helper.rb +30 -3
  48. data/spec/status_spec.rb +15 -0
  49. data/spec/tcp_sequence_spec.rb +32 -19
  50. data/spec/tcp_ts_sequence_spec.rb +24 -10
  51. data/spec/traceroute_spec.rb +31 -0
  52. data/spec/uptime_spec.rb +15 -0
  53. data/spec/xml_spec.rb +172 -31
  54. metadata +50 -54
  55. data/.gemtest +0 -0
  56. data/spec/helpers/nse.xml +0 -94
  57. data/spec/helpers/scan.xml +0 -241
  58. data/spec/helpers/xml.rb +0 -5
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'nmap/scanner'
3
+
4
+ describe Scanner do
5
+ describe "#to_s" do
6
+ let(:name) { 'nmap' }
7
+ let(:version) { '6.01' }
8
+ let(:args) { 'nmap -v -sS -sU -A -O -oX spec/scan.xml scanme.nmap.org' }
9
+ let(:start_time) { Time.new('Sat Jul 20 23:55:27 2013') }
10
+
11
+ subject do
12
+ described_class.new(
13
+ name,
14
+ version,
15
+ args,
16
+ start_time
17
+ )
18
+ end
19
+
20
+ it "should return the scanner command" do
21
+ subject.to_s.should == "#{name} #{args}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+
3
+ shared_examples_for "#scripts" do
4
+ describe "#scripts" do
5
+ subject { super().scripts }
6
+
7
+ it { should be_kind_of(Hash) }
8
+ it { should_not be_empty }
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "Sequence" do
4
+ describe "#values" do
5
+ subject { super().values }
6
+
7
+ it { should have(6).items }
8
+ it { should all_be_between(0,0xFFFFFFFF) }
9
+ end
10
+ end
@@ -1,40 +1,77 @@
1
1
  require 'spec_helper'
2
- require 'helpers/xml'
2
+ require 'cpe_examples'
3
3
 
4
4
  require 'nmap/xml'
5
5
  require 'nmap/service'
6
6
 
7
7
  describe Service do
8
- include Helpers
8
+ subject { @xml.hosts.first.ports.first.service }
9
9
 
10
- before(:all) do
11
- @xml = XML.new(Helpers::SCAN_FILE)
12
- @nse_xml = XML.new(Helpers::NSE_FILE)
10
+ describe "#name" do
11
+ it "should parse the name" do
12
+ subject.name.should == 'ssh'
13
+ end
13
14
  end
14
15
 
15
- subject { @xml.hosts.first.ports.first.service }
16
+ describe "#ssl?" do
17
+ it "should check the tunnel attribute" do
18
+ pending "need a service that uses SSL"
19
+ end
20
+ end
16
21
 
17
- it "should have a name" do
18
- subject.name.should == 'ftp'
22
+ describe "#protocol" do
23
+ it "should parse the proto attribute" do
24
+ pending "need a service with the proto attribute"
25
+ end
19
26
  end
20
27
 
21
- it "should have a fingerprint method" do
22
- subject.fingerprint_method.should == :table
28
+ describe "#product" do
29
+ it "should parse the product name attribute" do
30
+ subject.product.should == 'OpenSSH'
31
+ end
23
32
  end
24
33
 
25
- it "should have a confidence" do
26
- subject.confidence.should == 3
34
+ describe "#version" do
35
+ it "should parse the version attribute" do
36
+ subject.version.should == '5.3p1 Debian 3ubuntu7'
37
+ end
27
38
  end
28
39
 
29
- context "with improved detection" do
30
- subject { @nse_xml.hosts.first.ports.first.service }
40
+ describe "#extra_info" do
41
+ it "should parse the extrainfo attribute" do
42
+ subject.extra_info.should == 'protocol 2.0'
43
+ end
44
+ end
31
45
 
32
- it "should have a product name" do
33
- subject.product.should == 'OpenSSH'
46
+ describe "#hostname" do
47
+ it "should parse the hostname attribute" do
48
+ pending "need a service with the hostname attribute"
34
49
  end
50
+ end
51
+
52
+ describe "#os_type" do
53
+ it "should parse the ostype attribute" do
54
+ subject.os_type.should == 'Linux'
55
+ end
56
+ end
35
57
 
36
- it "should have a version" do
37
- subject.version.should == '5.1p1 Debian 6ubuntu2'
58
+ describe "#device_type" do
59
+ it "should parse the devicetype attribute" do
60
+ pending "need a service with the devicetype attribute"
38
61
  end
39
62
  end
63
+
64
+ describe "#fingerprint_method" do
65
+ it "should parse the method attribute" do
66
+ subject.fingerprint_method.should == :probed
67
+ end
68
+ end
69
+
70
+ describe "#confidence" do
71
+ it "should parse the conf attribute" do
72
+ subject.confidence.should be_between(0,10)
73
+ end
74
+ end
75
+
76
+ it_should_behave_like "CPE"
40
77
  end
@@ -1,12 +1,39 @@
1
- gem 'rspec', '~> 2.4'
2
1
  require 'rspec'
2
+ require 'simplecov'
3
+ SimpleCov.start
3
4
 
4
5
  require 'nmap/version'
5
6
  require 'nmap/xml'
6
7
  include Nmap
7
8
 
8
- require 'helpers/xml'
9
+ RSpec::Matchers.define :be_between do |min,max|
10
+ match do |value|
11
+ (value >= min) && (value <= max)
12
+ end
13
+ end
14
+
15
+ RSpec::Matchers.define :all_be_between do |min,max|
16
+ match do |values|
17
+ values.all? { |value| (value >= min) && (value <= max) }
18
+ end
19
+ end
20
+
21
+ RSpec::Matchers.define :be_one_of do |*values|
22
+ match do |value|
23
+ values.include?(value)
24
+ end
25
+
26
+ description { "be one of: #{expected.join(', ')}" }
27
+ end
28
+
29
+ RSpec::Matchers.define :all_be_kind_of do |base_class|
30
+ match do |values|
31
+ values.all? { |value| value.kind_of?(base_class) }
32
+ end
33
+ end
9
34
 
10
35
  RSpec.configure do |spec|
11
- spec.include Helpers
36
+ spec.before(:all) do
37
+ @xml = XML.new('spec/scan.xml')
38
+ end
12
39
  end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'nmap/status'
3
+
4
+ describe Status do
5
+ describe "#to_s" do
6
+ let(:state) { :up }
7
+ let(:reason) { 'syn-ack' }
8
+
9
+ subject { described_class.new(state,reason) }
10
+
11
+ it "should return the state" do
12
+ subject.to_s.should == state.to_s
13
+ end
14
+ end
15
+ end
@@ -1,31 +1,44 @@
1
1
  require 'spec_helper'
2
+ require 'sequence_examples'
3
+
2
4
  require 'nmap/tcp_sequence'
3
5
 
4
6
  describe TcpSequence do
5
- let(:xml) { XML.new(Helpers::SCAN_FILE) }
6
-
7
- subject { xml.hosts.first.tcp_sequence }
7
+ subject { @xml.hosts.first.tcp_sequence }
8
8
 
9
- it "should be accessible from host objects" do
10
- subject.should be_kind_of(TcpSequence)
9
+ describe "#index" do
10
+ it "should parse the index" do
11
+ subject.index.should be > 0
12
+ end
11
13
  end
12
14
 
13
- it "should parse the index" do
14
- subject.index.should == 25
15
+ describe "#description" do
16
+ it "should parse the difficulty description" do
17
+ subject.difficulty.should == "Good luck!"
18
+ end
15
19
  end
16
20
 
17
- it "should parse the difficulty description" do
18
- subject.difficulty.should == "Good luck!"
19
- end
21
+ describe "#to_s" do
22
+ let(:index_regexp) do
23
+ /\d+/
24
+ end
25
+
26
+ let(:difficulty_regexp) do
27
+ /"(Good luck!)"/
28
+ end
20
29
 
21
- it "should parse the values" do
22
- subject.values.should == [
23
- 0xAF1B39BD,
24
- 0xAF1C33BD,
25
- 0xAF1F21BD,
26
- 0xAF201BBD,
27
- 0xAF2115BD,
28
- 0xAF220FBD
29
- ]
30
+ let(:values_regexp) do
31
+ /\[\d+(, \d+){5}\]/
32
+ end
33
+
34
+ let(:regexp) do
35
+ /^index=#{index_regexp} difficulty=#{difficulty_regexp} values=#{values_regexp}$/
36
+ end
37
+
38
+ it "should contain the description and values" do
39
+ subject.to_s.should =~ regexp
40
+ end
30
41
  end
42
+
43
+ it_should_behave_like "Sequence"
31
44
  end
@@ -1,20 +1,34 @@
1
1
  require 'spec_helper'
2
+ require 'sequence_examples'
3
+
2
4
  require 'nmap/tcp_ts_sequence'
3
5
 
4
6
  describe TcpTsSequence do
5
- let(:xml) { XML.new(Helpers::SCAN_FILE) }
6
-
7
- subject { xml.hosts.first.tcp_ts_sequence }
7
+ subject { @xml.hosts.first.tcp_ts_sequence }
8
8
 
9
- it "should be accessible from host objects" do
10
- subject.should be_kind_of(TcpTsSequence)
9
+ describe "#description" do
10
+ it "should parse the description" do
11
+ subject.description.should == "1000HZ"
12
+ end
11
13
  end
12
14
 
13
- it "should parse the description" do
14
- subject.description.should == "2HZ"
15
- end
15
+ describe "#to_s" do
16
+ let(:description_regexp) do
17
+ /"[^"]+"/
18
+ end
16
19
 
17
- it "should parse the values" do
18
- subject.values.should == [0x1858, 0x1858, 0x1859, 0x1859, 0x1859, 0x1859]
20
+ let(:values_regexp) do
21
+ /\[\d+(, \d+){5}\]/
22
+ end
23
+
24
+ let(:regexp) do
25
+ /^description=#{description_regexp} values=#{values_regexp}$/
26
+ end
27
+
28
+ it "should contain the description and values" do
29
+ subject.to_s.should =~ regexp
30
+ end
19
31
  end
32
+
33
+ it_should_behave_like "Sequence"
20
34
  end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require 'nmap/traceroute'
3
+
4
+ describe Traceroute do
5
+ subject { @xml.hosts.first.traceroute }
6
+
7
+ describe "#port" do
8
+ subject { super().port }
9
+
10
+ it { should be_kind_of(Integer) }
11
+ it { should be > 0 }
12
+ it { should be < 65535 }
13
+ end
14
+
15
+ describe "#protocol" do
16
+ subject { super().protocol }
17
+
18
+ it { should be_kind_of(Symbol) }
19
+ it { should be_one_of(:tcp, :udp) }
20
+ end
21
+
22
+ describe "#each" do
23
+ subject { super().each.first }
24
+
25
+ it { should be_kind_of(Hop) }
26
+
27
+ its(:addr) { should be_kind_of(String) }
28
+ its(:ttl) { should be_kind_of(String) }
29
+ its(:rtt) { should be_kind_of(String) }
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'nmap/uptime'
3
+
4
+ describe Uptime do
5
+ describe "#to_s" do
6
+ let(:seconds) { 920430 }
7
+ let(:last_boot) { Time.parse("2013-07-10 08:34:03 -0700") }
8
+
9
+ subject { described_class.new(seconds,last_boot) }
10
+
11
+ it "should convert the uptipe to a String" do
12
+ subject.to_s.should == "uptime: #{seconds} (#{last_boot})"
13
+ end
14
+ end
15
+ end
@@ -2,34 +2,88 @@ require 'spec_helper'
2
2
  require 'nmap/xml'
3
3
 
4
4
  describe XML do
5
- subject { XML.new(Helpers::SCAN_FILE) }
5
+ let(:path) { File.expand_path('spec/scan.xml') }
6
6
 
7
- it "should have a version" do
8
- subject.version.should == '1.02'
7
+ subject { described_class.new(path) }
8
+
9
+ describe "#initialize" do
10
+ context "when given a Nokogiri::XML::Document" do
11
+ let(:document) { Nokogiri::XML(File.read(path)) }
12
+
13
+ it "should use the document" do
14
+ described_class.new(document).version.should == subject.version
15
+ end
16
+ end
17
+
18
+ context "when given an IO object" do
19
+ let(:io) { File.new(path) }
20
+
21
+ it "should parse the IO object" do
22
+ described_class.new(io).version.should == subject.version
23
+ end
24
+ end
25
+
26
+ context "when given a String" do
27
+ it "should parse the file at the path" do
28
+ described_class.new(path).version.should == subject.version
29
+ end
30
+ end
9
31
  end
10
32
 
11
- it "should parse the scanner version" do
12
- subject.scanner.version == '4.68'
33
+ describe "load" do
34
+ it "should parse the given text" do
35
+ subject.version.should == described_class.load(File.read(path)).version
36
+ end
13
37
  end
14
38
 
15
- it "should parse the scanner name" do
16
- subject.scanner.name.should == 'nmap'
39
+ describe "open" do
40
+ it "should parse the given file" do
41
+ subject.version.should == described_class.open(path).version
42
+ end
43
+
44
+ it "should set the path" do
45
+ subject.path.should == path
46
+ end
17
47
  end
18
48
 
19
- it "should parse the scanner arguments" do
20
- subject.scanner.arguments.should == 'nmap -v -oX samples/backspace.xml -O -P0 -sS 192.168.5.*'
49
+ describe "#version" do
50
+ it "should have a version" do
51
+ subject.version.should == '1.04'
52
+ end
21
53
  end
22
54
 
23
- it "should parse the scanner start time" do
24
- subject.scanner.start_time.should == Time.at(1218934249)
55
+ describe "#scanner" do
56
+ it "should parse the scanner version" do
57
+ subject.scanner.version == '4.68'
58
+ end
59
+
60
+ it "should parse the scanner name" do
61
+ subject.scanner.name.should == 'nmap'
62
+ end
63
+
64
+ it "should parse the scanner arguments" do
65
+ subject.scanner.arguments.should == 'nmap -v -sS -sU -A -O -oX spec/scan.xml scanme.nmap.org'
66
+ end
67
+
68
+ it "should parse the scanner start time" do
69
+ subject.scanner.start_time.should be_kind_of(Time)
70
+ end
25
71
  end
26
72
 
27
- it "should parse the scan information" do
28
- scan_info = subject.scan_info
73
+ describe "#scan_info" do
74
+ subject { super().scan_info.first }
75
+
76
+ it "should parse the type" do
77
+ subject.type.should == :syn
78
+ end
29
79
 
30
- scan_info.length.should == 1
31
- scan_info.first.type.should == :syn
32
- scan_info.first.protocol.should == :tcp
80
+ it "should parse the protocol" do
81
+ subject.protocol.should == :tcp
82
+ end
83
+
84
+ it "should parse the services" do
85
+ subject.services.should_not be_empty
86
+ end
33
87
  end
34
88
 
35
89
  it "should parse the verbose level" do
@@ -40,31 +94,118 @@ describe XML do
40
94
  subject.debugging.should == 0
41
95
  end
42
96
 
43
- it "should parse the scan tasks" do
44
- tasks = subject.tasks
97
+ describe "#each_run_stat" do
98
+ subject { super().each_run_stat.first }
45
99
 
46
- tasks.should_not be_empty
100
+ it "should yield RunStat objects" do
101
+ subject.should be_kind_of(RunStat)
102
+ end
47
103
 
48
- tasks.each do |task|
49
- task.name.should_not be_nil
50
- task.name.should_not be_empty
104
+ it "should parse the end time" do
105
+ subject.end_time.should be_kind_of(Time)
106
+ end
107
+
108
+ it "should parse the time elapsed" do
109
+ subject.elapsed.should_not be_nil
110
+ end
51
111
 
52
- task.start_time.should > Time.at(0)
112
+ it "should parse the summary" do
113
+ subject.summary.should_not be_empty
114
+ end
53
115
 
54
- task.end_time.should > Time.at(0)
55
- task.end_time.should >= task.start_time
116
+ it "should parse the exit status" do
117
+ subject.exit_status.should be_one_of('success', 'failure')
56
118
  end
57
119
  end
58
120
 
59
- it "should parse the hosts" do
60
- subject.hosts.length.should == 10
121
+ describe "#run_stats" do
122
+ subject { super().run_stats }
123
+
124
+ it { should_not be_empty }
125
+ it { should all_be_kind_of(RunStat) }
61
126
  end
62
127
 
63
- it "should iterate over each up host" do
64
- subject.each.all? { |host| host.status.state == :up }.should == true
128
+ describe "#each_task" do
129
+ subject { super().each_task.first }
130
+
131
+ it "should parse task name" do
132
+ subject.name.should == 'Ping Scan'
133
+ end
134
+
135
+ it "should parse the start time" do
136
+ subject.start_time.should be_kind_of(Time)
137
+ end
138
+
139
+ it "should parse the end time" do
140
+ subject.end_time.should be_kind_of(Time)
141
+ subject.end_time.should > subject.start_time
142
+ end
143
+
144
+ it "should parse the extrainfo" do
145
+ subject.extrainfo.should_not be_empty
146
+ end
65
147
  end
66
148
 
67
- it "should convert to a String" do
68
- subject.to_s.should == Helpers::SCAN_FILE
149
+ describe "#tasks" do
150
+ subject { super().tasks }
151
+
152
+ it { should_not be_empty }
153
+ it { should all_be_kind_of(ScanTask) }
154
+ end
155
+
156
+ describe "#each_host" do
157
+ subject { super().each_host.first }
158
+
159
+ it "should yield Host objects" do
160
+ subject.should be_kind_of(Host)
161
+ end
162
+ end
163
+
164
+ describe "#hosts" do
165
+ subject { super().hosts }
166
+
167
+ it { should_not be_empty }
168
+ it { subject.should all_be_kind_of(Host) }
169
+ end
170
+
171
+ describe "#each_up_host" do
172
+ subject { super().each_up_host.first }
173
+
174
+ it "should yield Host objects" do
175
+ subject.should be_kind_of(Host)
176
+ end
177
+
178
+ it "should be up" do
179
+ subject.status.state.should be == :up
180
+ end
181
+ end
182
+
183
+ describe "#up_hosts" do
184
+ subject { super().up_hosts }
185
+
186
+ it { should_not be_empty }
187
+ it { should all_be_kind_of(Host) }
188
+
189
+ it "should contain only up hosts" do
190
+ subject.all? { |host| host.status.state == :up }.should be_true
191
+ end
192
+ end
193
+
194
+ describe "#each" do
195
+ it "should iterate over each up host" do
196
+ subject.each.all? { |host| host.status.state == :up }.should == true
197
+ end
198
+ end
199
+
200
+ describe "#to_s" do
201
+ it "should convert to a String" do
202
+ subject.to_s.should == path
203
+ end
204
+ end
205
+
206
+ describe "#inspect" do
207
+ it "should include the class and path" do
208
+ subject.inspect.should == "#<#{described_class}: #{path}>"
209
+ end
69
210
  end
70
211
  end