metasploit_data_models 0.7.0 → 0.11.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +1 -0
  3. data/app/models/mdm/host.rb +352 -26
  4. data/app/models/mdm/loot.rb +72 -7
  5. data/app/models/mdm/{module_action.rb → module/action.rb} +3 -3
  6. data/app/models/mdm/{module_arch.rb → module/arch.rb} +3 -3
  7. data/app/models/mdm/{module_author.rb → module/author.rb} +3 -3
  8. data/app/models/mdm/module/detail.rb +280 -0
  9. data/app/models/mdm/{module_mixin.rb → module/mixin.rb} +3 -3
  10. data/app/models/mdm/{module_platform.rb → module/platform.rb} +3 -3
  11. data/app/models/mdm/module/ref.rb +48 -0
  12. data/app/models/mdm/{module_target.rb → module/target.rb} +3 -3
  13. data/app/models/mdm/note.rb +61 -6
  14. data/app/models/mdm/ref.rb +39 -1
  15. data/app/models/mdm/service.rb +85 -7
  16. data/app/models/mdm/session.rb +100 -6
  17. data/app/models/mdm/vuln.rb +104 -24
  18. data/db/migrate/20130228214900_change_required_columns_to_null_false_in_web_vulns.rb +1 -17
  19. data/db/migrate/20130412154159_change_foreign_key_in_module_actions.rb +25 -0
  20. data/db/migrate/20130412171844_change_foreign_key_in_module_archs.rb +25 -0
  21. data/db/migrate/20130412173121_change_foreign_key_in_module_authors.rb +25 -0
  22. data/db/migrate/20130412173640_change_foreign_key_in_module_mixins.rb +25 -0
  23. data/db/migrate/20130412174254_change_foreign_key_in_module_platforms.rb +25 -0
  24. data/db/migrate/20130412174719_change_foreign_key_in_module_refs.rb +25 -0
  25. data/db/migrate/20130412175040_change_foreign_key_in_module_targets.rb +25 -0
  26. data/db/migrate/20130430151353_change_required_columns_to_null_false_in_hosts.rb +11 -0
  27. data/db/migrate/20130430162145_enforce_address_uniqueness_in_workspace_in_hosts.rb +23 -0
  28. data/lib/mdm/module.rb +4 -0
  29. data/lib/metasploit_data_models.rb +1 -0
  30. data/lib/metasploit_data_models/change_required_columns_to_null_false.rb +23 -0
  31. data/lib/metasploit_data_models/version.rb +1 -1
  32. data/spec/app/models/mdm/host_spec.rb +411 -0
  33. data/spec/app/models/mdm/host_tag_spec.rb +13 -0
  34. data/spec/app/models/mdm/{module_action_spec.rb → module/action_spec.rb} +6 -6
  35. data/spec/app/models/mdm/{module_arch_spec.rb → module/arch_spec.rb} +6 -6
  36. data/spec/app/models/mdm/{module_author_spec.rb → module/author_spec.rb} +6 -6
  37. data/spec/app/models/mdm/{module_detail_spec.rb → module/detail_spec.rb} +101 -11
  38. data/spec/app/models/mdm/{module_mixin_spec.rb → module/mixin_spec.rb} +6 -6
  39. data/spec/app/models/mdm/{module_platform_spec.rb → module/platform_spec.rb} +6 -6
  40. data/spec/app/models/mdm/module/ref_spec.rb +62 -0
  41. data/spec/app/models/mdm/{module_target_spec.rb → module/target_spec.rb} +6 -6
  42. data/spec/app/models/mdm/ref_spec.rb +62 -0
  43. data/spec/app/models/mdm/tag_spec.rb +13 -0
  44. data/spec/app/models/mdm/vuln_ref_spec.rb +13 -0
  45. data/spec/app/models/mdm/vuln_spec.rb +231 -0
  46. data/spec/dummy/db/schema.rb +20 -20
  47. data/spec/factories/mdm/host_tags.rb +9 -0
  48. data/spec/factories/mdm/hosts.rb +65 -0
  49. data/spec/factories/mdm/module/actions.rb +14 -0
  50. data/spec/factories/mdm/module/archs.rb +14 -0
  51. data/spec/factories/mdm/{module_authors.rb → module/authors.rb} +4 -4
  52. data/spec/factories/mdm/module/details.rb +66 -0
  53. data/spec/factories/mdm/module/mixins.rb +14 -0
  54. data/spec/factories/mdm/module/platforms.rb +14 -0
  55. data/spec/factories/mdm/module/refs.rb +14 -0
  56. data/spec/factories/mdm/{module_targets.rb → module/targets.rb} +3 -3
  57. data/spec/factories/mdm/refs.rb +9 -0
  58. data/spec/factories/mdm/tags.rb +14 -0
  59. data/spec/factories/mdm/vuln_refs.rb +4 -0
  60. data/spec/factories/mdm/vulns.rb +20 -0
  61. metadata +78 -45
  62. data/app/models/mdm/module_detail.rb +0 -59
  63. data/app/models/mdm/module_ref.rb +0 -24
  64. data/spec/app/models/mdm/module_ref_spec.rb +0 -38
  65. data/spec/factories/mdm/module_actions.rb +0 -14
  66. data/spec/factories/mdm/module_archs.rb +0 -14
  67. data/spec/factories/mdm/module_details.rb +0 -9
  68. data/spec/factories/mdm/module_mixins.rb +0 -14
  69. data/spec/factories/mdm/module_platforms.rb +0 -14
  70. data/spec/factories/mdm/module_refs.rb +0 -14
@@ -4,5 +4,5 @@ module MetasploitDataModels
4
4
  # metasploit-framework/data/sql/migrate to db/migrate in this project, not all models have specs that verify the
5
5
  # migrations (with have_db_column and have_db_index) and certain models may not be shared between metasploit-framework
6
6
  # and pro, so models may be removed in the future. Because of the unstable API the version should remain below 1.0.0
7
- VERSION = '0.7.0'
7
+ VERSION = '0.11.2'
8
8
  end
@@ -0,0 +1,411 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mdm::Host do
4
+ subject(:host) do
5
+ FactoryGirl.build(:mdm_host)
6
+ end
7
+
8
+ let(:architectures) do
9
+ [
10
+ 'armbe',
11
+ 'armle',
12
+ 'cbea',
13
+ 'cbea64',
14
+ 'cmd',
15
+ 'java',
16
+ 'mips',
17
+ 'mipsbe',
18
+ 'mipsle',
19
+ 'php',
20
+ 'ppc',
21
+ 'ppc64',
22
+ 'ruby',
23
+ 'sparc',
24
+ 'tty',
25
+ 'x64',
26
+ 'x86',
27
+ 'x86_64'
28
+ ]
29
+ end
30
+
31
+ let(:operating_system_names) do
32
+ [
33
+ 'FreeBSD',
34
+ 'Linux',
35
+ 'Mac OS X',
36
+ 'Microsoft Windows',
37
+ 'NetBSD',
38
+ 'OpenBSD',
39
+ 'Unknown',
40
+ 'VMWare'
41
+ ]
42
+ end
43
+
44
+ let(:states) do
45
+ [
46
+ 'alive',
47
+ 'down',
48
+ 'unknown'
49
+ ]
50
+ end
51
+
52
+ context 'associations' do
53
+ it { should have_many(:creds).class_name('Mdm::Cred').through(:services) }
54
+ it { should have_many(:exploit_attempts).class_name('Mdm::ExploitAttempt').dependent(:destroy) }
55
+ it { should have_many(:exploited_hosts).class_name('Mdm::ExploitedHost').dependent(:destroy) }
56
+ it { should have_many(:host_details).class_name('Mdm::HostDetail').dependent(:destroy) }
57
+ it { should have_many(:hosts_tags).class_name('Mdm::HostTag') }
58
+ it { should have_many(:loots).class_name('Mdm::Loot').dependent(:destroy).order('loots.created_at DESC') }
59
+
60
+ context 'module_details' do
61
+ it { should have_many(:module_details).class_name('Mdm::Module::Detail').through(:module_refs) }
62
+
63
+ context 'with Mdm::Vulns' do
64
+ let!(:vulns) do
65
+ FactoryGirl.create_list(
66
+ :mdm_vuln,
67
+ 2,
68
+ :host => host
69
+ )
70
+ end
71
+
72
+ context 'with Mdm::Ref' do
73
+ let(:name) do
74
+ FactoryGirl.generate :mdm_ref_name
75
+ end
76
+
77
+ let!(:ref) do
78
+ FactoryGirl.create(:mdm_ref, :name => name)
79
+ end
80
+
81
+ context 'with Mdm::VulnRefs' do
82
+ let!(:vuln_refs) do
83
+ vulns.collect { |vuln|
84
+ FactoryGirl.create(:mdm_vuln_ref, :ref => ref, :vuln => vuln)
85
+ }
86
+ end
87
+
88
+ context 'with Mdm::Module::Detail' do
89
+ let!(:module_detail) do
90
+ FactoryGirl.create(
91
+ :mdm_module_detail
92
+ )
93
+ end
94
+
95
+ context 'with Mdm::Module::Ref with same name as Mdm::Ref' do
96
+ let!(:module_ref) do
97
+ FactoryGirl.create(
98
+ :mdm_module_ref,
99
+ :detail => module_detail,
100
+ :name => name
101
+ )
102
+ end
103
+
104
+ it 'should list unique Mdm::Module::Detail' do
105
+ host.module_details.should =~ [module_detail]
106
+ end
107
+
108
+ it 'should have duplicate Mdm::Module::Details if collected through chain' do
109
+ vuln_refs = []
110
+
111
+ host.vulns.each do |vuln|
112
+ # @todo https://www.pivotaltracker.com/story/show/49004623
113
+ vuln_refs += vuln.vulns_refs
114
+ end
115
+
116
+ refs = []
117
+
118
+ vuln_refs.each do |vuln_ref|
119
+ refs << vuln_ref.ref
120
+ end
121
+
122
+ module_refs = []
123
+
124
+ refs.each do |ref|
125
+ module_refs += ref.module_refs
126
+ end
127
+
128
+ module_details = []
129
+
130
+ module_refs.each do |module_ref|
131
+ module_details << module_ref.detail
132
+ end
133
+
134
+ host.module_details.count.should < module_details.length
135
+ module_details.uniq.count.should == host.module_details.count
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ it { should have_many(:module_refs).class_name('Mdm::Module::Ref').through(:refs) }
145
+ it { should have_many(:notes).class_name('Mdm::Note').dependent(:delete_all).order('notes.created_at') }
146
+ it { should have_many(:refs).class_name('Mdm::Ref').through(:vuln_refs) }
147
+ it { should have_many(:services).class_name('Mdm::Service').dependent(:destroy).order('services.port, services.proto') }
148
+ it { should have_many(:service_notes).through(:services) }
149
+ it { should have_many(:sessions).class_name('Mdm::Session').dependent(:destroy).order('sessions.opened_at') }
150
+ it { should have_many(:tags).class_name('Mdm::Tag').through(:hosts_tags) }
151
+ it { should have_many(:vulns).class_name('Mdm::Vuln').dependent(:delete_all) }
152
+ it { should have_many(:vuln_refs).class_name('Mdm::VulnRef') }
153
+ it { should have_many(:web_sites).class_name('Mdm::WebSite').through(:services) }
154
+ it { should belong_to(:workspace).class_name('Mdm::Workspace') }
155
+ end
156
+
157
+ context 'callbacks' do
158
+ context 'before destroy' do
159
+ context 'cleanup_tags' do
160
+ context 'with tags' do
161
+ let!(:tag) do
162
+ FactoryGirl.create(:mdm_tag)
163
+ end
164
+
165
+ let!(:host) do
166
+ FactoryGirl.create(:mdm_host)
167
+ end
168
+
169
+ context 'with only this host' do
170
+ before(:each) do
171
+ FactoryGirl.create(
172
+ :mdm_host_tag,
173
+ :host => host,
174
+ :tag => tag
175
+ )
176
+ end
177
+
178
+ it 'should destroy the tags' do
179
+ expect {
180
+ host.destroy
181
+ }.to change(Mdm::Tag, :count).by(-1)
182
+ end
183
+
184
+ it 'should destroy the host tags' do
185
+ expect {
186
+ host.destroy
187
+ }.to change(Mdm::HostTag, :count).by(-1)
188
+ end
189
+ end
190
+
191
+ context 'with additional hosts' do
192
+ let(:other_host) do
193
+ FactoryGirl.create(:mdm_host)
194
+ end
195
+
196
+ before(:each) do
197
+ FactoryGirl.create(:mdm_host_tag, :host => host, :tag => tag)
198
+ FactoryGirl.create(:mdm_host_tag, :host => other_host, :tag => tag)
199
+ end
200
+
201
+ it 'should not destroy the tag' do
202
+ expect {
203
+ host.destroy
204
+ }.to_not change(Mdm::Tag, :count)
205
+ end
206
+
207
+ it 'should destroy the host tags' do
208
+ expect {
209
+ host.destroy
210
+ }.to change(Mdm::HostTag, :count).by(-1)
211
+ end
212
+
213
+ it "should not destroy the other host's tags" do
214
+ host.destroy
215
+
216
+ other_host.hosts_tags.count.should == 1
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ context 'CONSTANTS' do
225
+ context 'ARCHITECTURES' do
226
+ subject(:architectures) do
227
+ described_class::ARCHITECTURES
228
+ end
229
+
230
+ it 'should be an Array<String>' do
231
+ architectures.should be_an Array
232
+
233
+ architectures.each do |architecture|
234
+ architecture.should be_a String
235
+ end
236
+ end
237
+
238
+ it 'should include both endians of ARM' do
239
+ architectures.should include('armbe')
240
+ architectures.should include('armle')
241
+ end
242
+
243
+ it 'should include 32-bit and 64-bit versions of Cell Broadband Engine Architecture' do
244
+ architectures.should include('cbea')
245
+ architectures.should include('cbea64')
246
+ end
247
+
248
+ it 'should include cmd for command shell' do
249
+ architectures.should include('cmd')
250
+ end
251
+
252
+ it 'should include java for Java Virtual Machine' do
253
+ architectures.should include('java')
254
+ end
255
+
256
+ it 'should include plain and endian-ware MIPS' do
257
+ architectures.should include('mips')
258
+ architectures.should include('mipsbe')
259
+ architectures.should include('mipsle')
260
+ end
261
+
262
+ it 'should include php for PHP code' do
263
+ architectures.should include('php')
264
+ end
265
+
266
+ it 'should include 32-bit and 64-bit PowerPC' do
267
+ architectures.should include('ppc')
268
+ architectures.should include('ppc64')
269
+ end
270
+
271
+ it 'should include ruby for Ruby code' do
272
+ architectures.should include('ruby')
273
+ end
274
+
275
+ it 'should include sparc for Sparc' do
276
+ architectures.should include('sparc')
277
+ end
278
+
279
+ it 'should include tty for Terminals' do
280
+ architectures.should include('tty')
281
+ end
282
+
283
+ it 'should include 32-bit and 64-bit x86' do
284
+ architectures.should include('x64')
285
+ architectures.should include('x86')
286
+ architectures.should include('x86_64')
287
+ end
288
+ end
289
+
290
+ it 'should define OPERATING_SYSTEM_NAMES in any order' do
291
+ described_class::OPERATING_SYSTEM_NAMES.should =~ operating_system_names
292
+ end
293
+
294
+ context 'SEARCH_FIELDS' do
295
+ subject(:search_fields) do
296
+ described_class::SEARCH_FIELDS
297
+ end
298
+
299
+ it 'should be an Array<String>' do
300
+ search_fields.should be_an Array
301
+
302
+ search_fields.each { |search_field|
303
+ search_field.should be_a String
304
+ }
305
+ end
306
+
307
+ it 'should cast address to text' do
308
+ search_fields.should include('address::text')
309
+ end
310
+
311
+ it { should include('comments') }
312
+ it { should include('mac') }
313
+ it { should include('name') }
314
+ it { should include('os_flavor') }
315
+ it { should include('os_name') }
316
+ it { should include('os_sp') }
317
+ it { should include('purpose') }
318
+ end
319
+
320
+ it 'should define STATES in any order' do
321
+ described_class::STATES.should =~ states
322
+ end
323
+ end
324
+
325
+ context 'database' do
326
+ context 'columns' do
327
+ it { should have_db_column(:address).of_type(:string).with_options(:null => false) }
328
+ it { should have_db_column(:arch).of_type(:string) }
329
+ it { should have_db_column(:comm).of_type(:string) }
330
+ it { should have_db_column(:comments).of_type(:text) }
331
+ it { should have_db_column(:info).of_type(:string).with_options(:limit => 2 ** 16) }
332
+ it { should have_db_column(:mac).of_type(:string) }
333
+ it { should have_db_column(:name).of_type(:string) }
334
+ it { should have_db_column(:os_flavor).of_type(:string) }
335
+ it { should have_db_column(:os_lang).of_type(:string) }
336
+ it { should have_db_column(:os_name).of_type(:string) }
337
+ it { should have_db_column(:os_sp).of_type(:string) }
338
+ it { should have_db_column(:purpose).of_type(:text) }
339
+ it { should have_db_column(:scope).of_type(:text) }
340
+ it { should have_db_column(:state).of_type(:string) }
341
+ it { should have_db_column(:virtual_host).of_type(:text) }
342
+ it { should have_db_column(:workspace_id).of_type(:integer).with_options(:null => false) }
343
+
344
+ context 'counter caches' do
345
+ it { should have_db_column(:exploit_attempt_count).of_type(:integer).with_options(:default => 0) }
346
+ it { should have_db_column(:host_detail_count).of_type(:integer).with_options(:default => 0) }
347
+ it { should have_db_column(:note_count).of_type(:integer).with_options(:default => 0) }
348
+ it { should have_db_column(:service_count).of_type(:integer).with_options(:default => 0) }
349
+ it { should have_db_column(:vuln_count).of_type(:integer).with_options(:default => 0) }
350
+ end
351
+
352
+ context 'timestamps' do
353
+ it { should have_db_column(:created_at).of_type(:datetime) }
354
+ it { should have_db_column(:updated_at).of_type(:datetime) }
355
+ end
356
+ end
357
+
358
+ context 'indices' do
359
+ it { should have_db_index([:workspace_id, :address]).unique(true) }
360
+ it { should have_db_index(:name) }
361
+ it { should have_db_index(:os_flavor) }
362
+ it { should have_db_index(:os_name) }
363
+ it { should have_db_index(:purpose) }
364
+ it { should have_db_index(:state) }
365
+ end
366
+ end
367
+
368
+ context 'factories' do
369
+ context 'full_mdm_host' do
370
+ subject(:full_mdm_host) do
371
+ FactoryGirl.build(:full_mdm_host)
372
+ end
373
+
374
+ it { should be_valid }
375
+ end
376
+
377
+ context 'mdm_host' do
378
+ subject(:mdm_host) do
379
+ FactoryGirl.build(:mdm_host)
380
+ end
381
+
382
+ it { should be_valid }
383
+ end
384
+ end
385
+
386
+ context 'validations' do
387
+ context 'address' do
388
+ it { should ensure_exclusion_of(:address).in_array(['127.0.0.1']) }
389
+ it { should validate_presence_of(:address) }
390
+
391
+ # can't use validate_uniqueness_of(:address).scoped_to(:workspace_id) because it will attempt to set workspace_id
392
+ # to `nil`, which will make the `:null => false` constraint on hosts.workspace_id to fail.
393
+ it 'should validate uniqueness of address scoped to workspace_id' do
394
+ address = '192.168.0.1'
395
+
396
+ workspace = FactoryGirl.create(:mdm_workspace)
397
+ FactoryGirl.create(:mdm_host, :address => address, :workspace => workspace)
398
+
399
+ duplicate_host = FactoryGirl.build(:mdm_host, :address => address, :workspace => workspace)
400
+
401
+ duplicate_host.should_not be_valid
402
+ duplicate_host.errors[:address].should include('has already been taken')
403
+ end
404
+ end
405
+
406
+ it { should ensure_inclusion_of(:arch).in_array(architectures).allow_nil }
407
+ it { should ensure_inclusion_of(:os_name).in_array(operating_system_names).allow_nil }
408
+ it { should ensure_inclusion_of(:state).in_array(states).allow_nil }
409
+ it { should validate_presence_of(:workspace) }
410
+ end
411
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mdm::HostTag do
4
+ context 'factories' do
5
+ context 'mdm_host_tag' do
6
+ subject(:mdm_host_tag) do
7
+ FactoryGirl.build(:mdm_host_tag)
8
+ end
9
+
10
+ it { should be_valid }
11
+ end
12
+ end
13
+ end
@@ -1,18 +1,18 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Mdm::ModuleAction do
3
+ describe Mdm::Module::Action do
4
4
  context 'associations' do
5
- it { should belong_to(:module_detail).class_name('Mdm::ModuleDetail') }
5
+ it { should belong_to(:detail).class_name('Mdm::Module::Detail') }
6
6
  end
7
7
 
8
8
  context 'database' do
9
9
  context 'columns' do
10
- it { should have_db_column(:module_detail_id).of_type(:integer) }
10
+ it { should have_db_column(:detail_id).of_type(:integer) }
11
11
  it { should have_db_column(:name).of_type(:text) }
12
12
  end
13
13
 
14
14
  context 'indices' do
15
- it { should have_db_index(:module_detail_id) }
15
+ it { should have_db_index(:detail_id) }
16
16
  end
17
17
  end
18
18
 
@@ -27,12 +27,12 @@ describe Mdm::ModuleAction do
27
27
  end
28
28
 
29
29
  context 'mass assignment security' do
30
- it { should_not allow_mass_assignment_of(:module_detail_id) }
30
+ it { should_not allow_mass_assignment_of(:detail_id) }
31
31
  it { should allow_mass_assignment_of(:name) }
32
32
  end
33
33
 
34
34
  context 'validations' do
35
- it { should validate_presence_of(:module_detail) }
35
+ it { should validate_presence_of(:detail) }
36
36
  it { should validate_presence_of(:name) }
37
37
  end
38
38
  end