metasploit-credential 0.14.7 → 0.14.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +41 -9
- data/lib/metasploit/credential/exporter/core.rb +2 -2
- data/lib/metasploit/credential/exporter/pwdump.rb +2 -2
- data/lib/metasploit/credential/migrator.rb +1 -1
- data/lib/metasploit/credential/version.rb +12 -21
- data/spec/dummy/db/structure.sql +0 -1
- data/spec/lib/metasploit/credential/creation_spec.rb +8 -6
- data/spec/lib/metasploit/credential/exporter/core_spec.rb +85 -100
- data/spec/lib/metasploit/credential/exporter/pwdump_spec.rb +16 -14
- data/spec/lib/metasploit/credential/importer/core_spec.rb +12 -10
- data/spec/lib/metasploit/credential/importer/multi_spec.rb +6 -4
- data/spec/lib/metasploit/credential/importer/pwdump_spec.rb +13 -11
- data/spec/lib/metasploit/credential/importer/zip_spec.rb +7 -5
- data/spec/lib/metasploit/credential/migrator_spec.rb +13 -13
- data/spec/lib/metasploit/credential/version_spec.rb +141 -3
- data/spec/lib/metasploit/credential_spec.rb +15 -4
- data/spec/models/mdm/service_spec.rb +5 -3
- data/spec/models/mdm/session_spec.rb +4 -2
- data/spec/models/mdm/task_spec.rb +6 -4
- data/spec/models/mdm/user_spec.rb +4 -2
- data/spec/models/mdm/workspace_spec.rb +4 -2
- data/spec/models/metasploit/credential/blank_username_spec.rb +7 -5
- data/spec/models/metasploit/credential/core_spec.rb +45 -43
- data/spec/models/metasploit/credential/login/status_spec.rb +21 -19
- data/spec/models/metasploit/credential/login_spec.rb +38 -36
- data/spec/models/metasploit/credential/nonreplayable_hash_spec.rb +5 -3
- data/spec/models/metasploit/credential/ntlm_hash_spec.rb +15 -13
- data/spec/models/metasploit/credential/origin/cracked_password_spec.rb +7 -5
- data/spec/models/metasploit/credential/origin/import_spec.rb +10 -8
- data/spec/models/metasploit/credential/origin/manual_spec.rb +9 -7
- data/spec/models/metasploit/credential/origin/service_spec.rb +12 -10
- data/spec/models/metasploit/credential/origin/session_spec.rb +13 -11
- data/spec/models/metasploit/credential/password_hash_spec.rb +6 -4
- data/spec/models/metasploit/credential/password_spec.rb +5 -3
- data/spec/models/metasploit/credential/postgres_md5_spec.rb +6 -4
- data/spec/models/metasploit/credential/private_spec.rb +10 -8
- data/spec/models/metasploit/credential/public_spec.rb +7 -5
- data/spec/models/metasploit/credential/realm_spec.rb +16 -14
- data/spec/models/metasploit/credential/replayable_hash_spec.rb +5 -3
- data/spec/models/metasploit/credential/ssh_key_spec.rb +17 -15
- data/spec/models/metasploit/credential/username_spec.rb +8 -6
- data/spec/models/metasploit_data_models/search/visitor/relation_spec.rb +3 -1
- data/spec/spec_helper.rb +25 -95
- data/spec/support/shared/contexts/mdm/workspace.rb +1 -1
- data/spec/support/shared/examples/core_validations.rb +42 -117
- data/spec/support/shared/examples/single_table_inheritance_database_columns.rb +2 -2
- data/spec/support/shared/examples/timestamp_database_column.rb +2 -2
- metadata +8 -22
@@ -1,10 +1,12 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::Credential::Login do
|
2
4
|
it_should_behave_like 'Metasploit::Concern.run'
|
3
5
|
|
4
6
|
context 'associations' do
|
5
|
-
it {
|
6
|
-
it {
|
7
|
-
it {
|
7
|
+
it { should belong_to(:core).class_name('Metasploit::Credential::Core') }
|
8
|
+
it { should have_one(:host).class_name('Mdm::Host') }
|
9
|
+
it { should belong_to(:service).class_name('Mdm::Service')}
|
8
10
|
end
|
9
11
|
|
10
12
|
context 'callbacks' do
|
@@ -37,7 +39,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
37
39
|
''
|
38
40
|
end
|
39
41
|
|
40
|
-
it {
|
42
|
+
it { should be_nil }
|
41
43
|
end
|
42
44
|
|
43
45
|
context 'with nil' do
|
@@ -45,7 +47,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
45
47
|
nil
|
46
48
|
end
|
47
49
|
|
48
|
-
it {
|
50
|
+
it { should be_nil }
|
49
51
|
end
|
50
52
|
|
51
53
|
context 'with present' do
|
@@ -64,21 +66,21 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
64
66
|
|
65
67
|
context 'database' do
|
66
68
|
context 'columns' do
|
67
|
-
it {
|
68
|
-
it {
|
69
|
-
it {
|
69
|
+
it { should have_db_column(:access_level).of_type(:string).with_options(null: true) }
|
70
|
+
it { should have_db_column(:last_attempted_at).of_type(:datetime).with_options(null: true) }
|
71
|
+
it { should have_db_column(:status).of_type(:string).with_options(null: false) }
|
70
72
|
|
71
73
|
it_should_behave_like 'timestamp database columns'
|
72
74
|
|
73
75
|
context 'foreign keys' do
|
74
|
-
it {
|
75
|
-
it {
|
76
|
+
it { should have_db_column(:core_id).of_type(:integer).with_options(null: false) }
|
77
|
+
it { should have_db_column(:service_id).of_type(:integer).with_options(null: false) }
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
79
81
|
context 'indices' do
|
80
|
-
it {
|
81
|
-
it {
|
82
|
+
it { should have_db_index([:core_id, :service_id]).unique(true) }
|
83
|
+
it { should have_db_index([:service_id, :core_id]).unique(true) }
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
@@ -90,7 +92,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
90
92
|
FactoryGirl.build(:metasploit_credential_login)
|
91
93
|
end
|
92
94
|
|
93
|
-
it {
|
95
|
+
it { should be_valid }
|
94
96
|
|
95
97
|
context '#status' do
|
96
98
|
subject(:metasploit_credential_login) do
|
@@ -105,7 +107,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
105
107
|
Metasploit::Model::Login::Status::DENIED_ACCESS
|
106
108
|
end
|
107
109
|
|
108
|
-
it {
|
110
|
+
it { should be_valid }
|
109
111
|
end
|
110
112
|
|
111
113
|
context 'with Metasploit::Model::Login::Status::DISABLED' do
|
@@ -113,7 +115,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
113
115
|
Metasploit::Model::Login::Status::DISABLED
|
114
116
|
end
|
115
117
|
|
116
|
-
it {
|
118
|
+
it { should be_valid }
|
117
119
|
end
|
118
120
|
|
119
121
|
context 'with Metasploit::Model::Login::Status::LOCKED_OUT' do
|
@@ -121,7 +123,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
121
123
|
Metasploit::Model::Login::Status::LOCKED_OUT
|
122
124
|
end
|
123
125
|
|
124
|
-
it {
|
126
|
+
it { should be_valid }
|
125
127
|
end
|
126
128
|
|
127
129
|
context 'with Metasploit::Model::Login::Status::SUCCESSFUL' do
|
@@ -129,7 +131,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
129
131
|
Metasploit::Model::Login::Status::SUCCESSFUL
|
130
132
|
end
|
131
133
|
|
132
|
-
it {
|
134
|
+
it { should be_valid }
|
133
135
|
end
|
134
136
|
|
135
137
|
context 'with Metasploit::Model::Login::Status::UNABLE_TO_CONNECT' do
|
@@ -137,7 +139,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
137
139
|
Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
138
140
|
end
|
139
141
|
|
140
|
-
it {
|
142
|
+
it { should be_valid }
|
141
143
|
end
|
142
144
|
|
143
145
|
context 'with Metasploit::Model::Login::Status::UNTRIED' do
|
@@ -145,21 +147,21 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
145
147
|
Metasploit::Model::Login::Status::UNTRIED
|
146
148
|
end
|
147
149
|
|
148
|
-
it {
|
150
|
+
it { should be_valid }
|
149
151
|
end
|
150
152
|
end
|
151
153
|
end
|
152
154
|
end
|
153
155
|
|
154
156
|
context 'mass assignment security' do
|
155
|
-
it {
|
157
|
+
it { should allow_mass_assignment_of(:access_level) }
|
156
158
|
it { should_not allow_mass_assignment_of(:core) }
|
157
159
|
it { should_not allow_mass_assignment_of(:core_id) }
|
158
160
|
it { should_not allow_mass_assignment_of(:created_at) }
|
159
|
-
it {
|
161
|
+
it { should allow_mass_assignment_of(:last_attempted_at) }
|
160
162
|
it { should_not allow_mass_assignment_of(:service) }
|
161
163
|
it { should_not allow_mass_assignment_of(:service_id) }
|
162
|
-
it {
|
164
|
+
it { should allow_mass_assignment_of(:status) }
|
163
165
|
it { should_not allow_mass_assignment_of(:updated_at) }
|
164
166
|
end
|
165
167
|
|
@@ -189,7 +191,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
189
191
|
end
|
190
192
|
|
191
193
|
context 'validations' do
|
192
|
-
it {
|
194
|
+
it { should validate_presence_of :core }
|
193
195
|
|
194
196
|
context 'with existent Metasploit::Credential::Login' do
|
195
197
|
include_context 'Mdm::Workspace'
|
@@ -202,11 +204,11 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
202
204
|
)
|
203
205
|
end
|
204
206
|
|
205
|
-
it {
|
207
|
+
it { should validate_uniqueness_of(:core_id).scoped_to(:service_id) }
|
206
208
|
end
|
207
209
|
|
208
|
-
it {
|
209
|
-
it {
|
210
|
+
it { should validate_presence_of :service }
|
211
|
+
it { should ensure_inclusion_of(:status).in_array(Metasploit::Model::Login::Status::ALL) }
|
210
212
|
|
211
213
|
context '#consistent_last_attempted_at' do
|
212
214
|
include_context 'Mdm::Workspace'
|
@@ -250,7 +252,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
250
252
|
DateTime.now.utc
|
251
253
|
end
|
252
254
|
|
253
|
-
it {
|
255
|
+
it { should include(error) }
|
254
256
|
end
|
255
257
|
|
256
258
|
context 'without #last_attempted' do
|
@@ -288,7 +290,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
288
290
|
nil
|
289
291
|
end
|
290
292
|
|
291
|
-
it {
|
293
|
+
it { should include(error) }
|
292
294
|
end
|
293
295
|
end
|
294
296
|
end
|
@@ -361,7 +363,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
361
363
|
FactoryGirl.build(:mdm_workspace)
|
362
364
|
end
|
363
365
|
|
364
|
-
it {
|
366
|
+
it { should include(error) }
|
365
367
|
end
|
366
368
|
end
|
367
369
|
|
@@ -370,7 +372,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
370
372
|
nil
|
371
373
|
end
|
372
374
|
|
373
|
-
it {
|
375
|
+
it { should include(error) }
|
374
376
|
end
|
375
377
|
end
|
376
378
|
|
@@ -379,7 +381,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
379
381
|
nil
|
380
382
|
end
|
381
383
|
|
382
|
-
it {
|
384
|
+
it { should include(error) }
|
383
385
|
end
|
384
386
|
end
|
385
387
|
|
@@ -388,7 +390,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
388
390
|
nil
|
389
391
|
end
|
390
392
|
|
391
|
-
it {
|
393
|
+
it { should include(error) }
|
392
394
|
end
|
393
395
|
end
|
394
396
|
|
@@ -420,7 +422,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
420
422
|
FactoryGirl.build(:mdm_workspace)
|
421
423
|
end
|
422
424
|
|
423
|
-
it {
|
425
|
+
it { should include(error) }
|
424
426
|
end
|
425
427
|
|
426
428
|
context 'without Mdm::Host#workspace' do
|
@@ -477,7 +479,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
477
479
|
FactoryGirl.build(:mdm_workspace)
|
478
480
|
end
|
479
481
|
|
480
|
-
it {
|
482
|
+
it { should include(error) }
|
481
483
|
end
|
482
484
|
|
483
485
|
context 'without Mdm::Host#workspace' do
|
@@ -520,7 +522,7 @@ RSpec.describe Metasploit::Credential::Login, type: :model do
|
|
520
522
|
subject(:login){ FactoryGirl.create :metasploit_credential_login, core: core}
|
521
523
|
|
522
524
|
it 'should find the right objects' do
|
523
|
-
|
525
|
+
Metasploit::Credential::Login.in_workspace_including_hosts_and_services(service.host.workspace).should include(login)
|
524
526
|
end
|
525
527
|
end
|
526
528
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::Credential::NonreplayableHash do
|
2
4
|
it_should_behave_like 'Metasploit::Concern.run'
|
3
5
|
|
4
|
-
it {
|
6
|
+
it { should be_a Metasploit::Credential::PasswordHash }
|
5
7
|
|
6
8
|
context 'factories' do
|
7
9
|
context 'metasploit_credential_nonreplayable_hash' do
|
@@ -9,7 +11,7 @@ RSpec.describe Metasploit::Credential::NonreplayableHash, type: :model do
|
|
9
11
|
FactoryGirl.build(:metasploit_credential_nonreplayable_hash)
|
10
12
|
end
|
11
13
|
|
12
|
-
it {
|
14
|
+
it { should be_valid }
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::Credential::NTLMHash do
|
2
4
|
it_should_behave_like 'Metasploit::Concern.run'
|
3
5
|
|
4
|
-
it {
|
6
|
+
it { should be_a Metasploit::Credential::ReplayableHash }
|
5
7
|
|
6
8
|
context 'CONSTANTS' do
|
7
9
|
context 'DATA_REGEXP' do
|
@@ -36,7 +38,7 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
|
|
36
38
|
described_class::LAN_MANAGER_MAX_CHARACTERS
|
37
39
|
end
|
38
40
|
|
39
|
-
it {
|
41
|
+
it { should == 14 }
|
40
42
|
end
|
41
43
|
|
42
44
|
context 'LAN_MANAGER_HEX_DIGEST_REGEXP' do
|
@@ -99,7 +101,7 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
|
|
99
101
|
nil
|
100
102
|
end
|
101
103
|
|
102
|
-
it {
|
104
|
+
it { should be_nil }
|
103
105
|
end
|
104
106
|
|
105
107
|
context 'with upper case characters' do
|
@@ -131,7 +133,7 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
|
|
131
133
|
FactoryGirl.build(:metasploit_credential_ntlm_hash)
|
132
134
|
end
|
133
135
|
|
134
|
-
it {
|
136
|
+
it { should be_valid }
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
@@ -189,7 +191,7 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
|
|
189
191
|
super().gsub(':', '')
|
190
192
|
end
|
191
193
|
|
192
|
-
it {
|
194
|
+
it { should include(error) }
|
193
195
|
end
|
194
196
|
|
195
197
|
context 'without LAN Manager hex_digest' do
|
@@ -197,7 +199,7 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
|
|
197
199
|
":#{nt_lan_manager_hex_digest}"
|
198
200
|
end
|
199
201
|
|
200
|
-
it {
|
202
|
+
it { should include(error) }
|
201
203
|
end
|
202
204
|
|
203
205
|
context 'with incorrect hash length(s)' do
|
@@ -205,7 +207,7 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
|
|
205
207
|
"123456:abcdef"
|
206
208
|
end
|
207
209
|
|
208
|
-
it {
|
210
|
+
it { should include(error) }
|
209
211
|
end
|
210
212
|
end
|
211
213
|
end
|
@@ -373,26 +375,26 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
|
|
373
375
|
context 'blank_password?' do
|
374
376
|
|
375
377
|
it 'returns true if the hash is for a blank password' do
|
376
|
-
expect(blank_password_hash.blank_password?).to
|
378
|
+
expect(blank_password_hash.blank_password?).to be_true
|
377
379
|
end
|
378
380
|
|
379
381
|
it 'returns false if the hash is not for a blank password' do
|
380
|
-
expect(non_blank_password.blank_password?).to
|
382
|
+
expect(non_blank_password.blank_password?).to be_false
|
381
383
|
end
|
382
384
|
|
383
385
|
it 'returns false if the nt hash is not blank but the lm hash is' do
|
384
|
-
expect(no_lm_hash.blank_password?).to
|
386
|
+
expect(no_lm_hash.blank_password?).to be_false
|
385
387
|
end
|
386
388
|
end
|
387
389
|
|
388
390
|
context 'lm_hash_present?' do
|
389
391
|
|
390
392
|
it 'returns false if the lm_hash is blank' do
|
391
|
-
expect(no_lm_hash.lm_hash_present?).to
|
393
|
+
expect(no_lm_hash.lm_hash_present?).to be_false
|
392
394
|
end
|
393
395
|
|
394
396
|
it 'returns true if the lm_hash is not blank' do
|
395
|
-
expect(non_blank_password.lm_hash_present?).to
|
397
|
+
expect(non_blank_password.lm_hash_present?).to be_true
|
396
398
|
end
|
397
399
|
end
|
398
400
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::Credential::Origin::CrackedPassword do
|
2
4
|
it_should_behave_like 'Metasploit::Concern.run'
|
3
5
|
|
4
6
|
context 'associations' do
|
5
|
-
it {
|
6
|
-
it {
|
7
|
+
it { should have_many(:cores).class_name('Metasploit::Credential::Core').dependent(:destroy) }
|
8
|
+
it { should belong_to(:originating_core).class_name('Metasploit::Credential::Core') }
|
7
9
|
end
|
8
10
|
|
9
11
|
context 'database' do
|
@@ -11,13 +13,13 @@ RSpec.describe Metasploit::Credential::Origin::CrackedPassword, type: :model do
|
|
11
13
|
it_should_behave_like 'timestamp database columns'
|
12
14
|
|
13
15
|
context 'foreign keys' do
|
14
|
-
it {
|
16
|
+
it { should have_db_column(:metasploit_credential_core_id).of_type(:integer).with_options(null: false) }
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
context 'indices' do
|
19
21
|
context 'foreign keys' do
|
20
|
-
it {
|
22
|
+
it { should have_db_index(:metasploit_credential_core_id) }
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
@@ -1,25 +1,27 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::Credential::Origin::Import do
|
2
4
|
it_should_behave_like 'Metasploit::Concern.run'
|
3
5
|
|
4
6
|
context 'associations' do
|
5
|
-
it {
|
6
|
-
it {
|
7
|
+
it { should have_many(:cores).class_name('Metasploit::Credential::Core').dependent(:destroy) }
|
8
|
+
it { should belong_to(:task).class_name('Mdm::Task') }
|
7
9
|
end
|
8
10
|
|
9
11
|
context 'database' do
|
10
12
|
context 'columns' do
|
11
|
-
it {
|
13
|
+
it { should have_db_column(:filename).of_type(:text).with_options(null: false) }
|
12
14
|
|
13
15
|
it_should_behave_like 'timestamp database columns'
|
14
16
|
|
15
17
|
context 'foreign keys' do
|
16
|
-
it {
|
18
|
+
it { should have_db_column(:task_id).of_type(:integer) }
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
22
|
context 'indices' do
|
21
23
|
context 'foreign keys' do
|
22
|
-
it {
|
24
|
+
it { should have_db_index(:task_id) }
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
@@ -31,12 +33,12 @@ RSpec.describe Metasploit::Credential::Origin::Import, type: :model do
|
|
31
33
|
FactoryGirl.build(:metasploit_credential_origin_import)
|
32
34
|
end
|
33
35
|
|
34
|
-
it {
|
36
|
+
it { should be_valid }
|
35
37
|
end
|
36
38
|
|
37
39
|
context 'mass assignment security' do
|
38
40
|
it { should_not allow_mass_assignment_of :created_at }
|
39
|
-
it {
|
41
|
+
it { should allow_mass_assignment_of :filename }
|
40
42
|
it { should_not allow_mass_assignment_of :task }
|
41
43
|
it { should_not allow_mass_assignment_of :task_id }
|
42
44
|
it { should_not allow_mass_assignment_of :updated_at }
|
@@ -1,15 +1,17 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::Credential::Origin::Manual do
|
2
4
|
it_should_behave_like 'Metasploit::Concern.run'
|
3
5
|
|
4
6
|
context 'associations' do
|
5
|
-
it {
|
6
|
-
it {
|
7
|
+
it { should have_many(:cores).class_name('Metasploit::Credential::Core').dependent(:destroy) }
|
8
|
+
it { should belong_to(:user).class_name('Mdm::User') }
|
7
9
|
end
|
8
10
|
|
9
11
|
context 'database' do
|
10
12
|
context 'columns' do
|
11
13
|
context 'foreign keys' do
|
12
|
-
it {
|
14
|
+
it { should have_db_column(:user_id).of_type(:integer).with_options(null: false) }
|
13
15
|
end
|
14
16
|
|
15
17
|
it_should_behave_like 'timestamp database columns'
|
@@ -17,7 +19,7 @@ RSpec.describe Metasploit::Credential::Origin::Manual, type: :model do
|
|
17
19
|
|
18
20
|
context 'indices' do
|
19
21
|
context 'foreign keys' do
|
20
|
-
it {
|
22
|
+
it { should have_db_index(:user_id) }
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
@@ -28,11 +30,11 @@ RSpec.describe Metasploit::Credential::Origin::Manual, type: :model do
|
|
28
30
|
FactoryGirl.build(:metasploit_credential_origin_manual)
|
29
31
|
end
|
30
32
|
|
31
|
-
it {
|
33
|
+
it { should be_valid }
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
37
|
context 'validations' do
|
36
|
-
it {
|
38
|
+
it { should validate_presence_of :user }
|
37
39
|
end
|
38
40
|
end
|