metasploit_data_models 0.15.2 → 0.16.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.
- data/Gemfile +2 -0
- data/app/models/mdm/client.rb +0 -1
- data/app/models/mdm/cred.rb +1 -1
- data/app/models/mdm/exploited_host.rb +0 -1
- data/app/models/mdm/listener.rb +2 -1
- data/app/models/mdm/nexpose_console.rb +2 -2
- data/app/models/mdm/session.rb +5 -1
- data/app/models/mdm/workspace.rb +0 -1
- data/db/migrate/20130525015035_remove_campaign_id_from_clients.rb +9 -0
- data/db/migrate/20130525212420_drop_table_imported_creds.rb +14 -0
- data/db/migrate/20130531144949_making_host_tags_a_real_ar_model.rb +6 -0
- data/lib/mdm/host/operating_system_normalization.rb +4 -4
- data/lib/metasploit_data_models/version.rb +1 -1
- data/spec/app/models/mdm/client_spec.rb +43 -0
- data/spec/app/models/mdm/cred_spec.rb +211 -0
- data/spec/app/models/mdm/events_spec.rb +85 -0
- data/spec/app/models/mdm/exploit_attempt_spec.rb +60 -0
- data/spec/app/models/mdm/exploited_host_spec.rb +44 -0
- data/spec/app/models/mdm/host_detail_spec.rb +49 -0
- data/spec/app/models/mdm/host_spec.rb +468 -0
- data/spec/app/models/mdm/host_tag_spec.rb +26 -0
- data/spec/app/models/mdm/listener_spec.rb +108 -0
- data/spec/app/models/mdm/loot_spec.rb +77 -0
- data/spec/app/models/mdm/nexpose_console_spec.rb +128 -0
- data/spec/app/models/mdm/note_spec.rb +84 -0
- data/spec/app/models/mdm/ref_spec.rb +13 -0
- data/spec/app/models/mdm/report_spec.rb +104 -0
- data/spec/app/models/mdm/report_template_spec.rb +52 -0
- data/spec/app/models/mdm/route_spec.rb +36 -0
- data/spec/app/models/mdm/service_spec.rb +70 -15
- data/spec/app/models/mdm/session_event_spec.rb +42 -0
- data/spec/app/models/mdm/session_spec.rb +114 -0
- data/spec/app/models/mdm/tag_spec.rb +104 -0
- data/spec/app/models/mdm/task_creds_spec.rb +32 -0
- data/spec/app/models/mdm/task_host_spec.rb +33 -0
- data/spec/app/models/mdm/task_service_spec.rb +33 -0
- data/spec/app/models/mdm/task_spec.rb +59 -5
- data/spec/app/models/mdm/user_spec.rb +51 -0
- data/spec/app/models/mdm/vuln_attempt_spec.rb +54 -0
- data/spec/app/models/mdm/vuln_details_spec.rb +66 -0
- data/spec/app/models/mdm/vuln_ref_spec.rb +24 -0
- data/spec/app/models/mdm/vuln_spec.rb +25 -0
- data/spec/app/models/mdm/web_form_spec.rb +47 -0
- data/spec/app/models/mdm/web_page_spec.rb +55 -0
- data/spec/app/models/mdm/web_site_spec.rb +86 -0
- data/spec/app/models/mdm/web_vuln_spec.rb +12 -0
- data/spec/app/models/mdm/workspace_spec.rb +567 -0
- data/spec/dummy/db/schema.rb +5 -13
- data/spec/factories/mdm/addresses.rb +5 -0
- data/spec/factories/mdm/clients.rb +8 -0
- data/spec/factories/mdm/events.rb +15 -0
- data/spec/factories/mdm/exploit_attempts.rb +8 -0
- data/spec/factories/mdm/exploited_hosts.rb +7 -0
- data/spec/factories/mdm/fingerprints/nessus_fingerprints.rb +6 -0
- data/spec/factories/mdm/fingerprints/nexpose_fingerprints.rb +6 -0
- data/spec/factories/mdm/fingerprints/nmap_fingerprints.rb +6 -0
- data/spec/factories/mdm/fingerprints/retina_fingerprints.rb +6 -0
- data/spec/factories/mdm/fingerprints/session_fingerprints.rb +6 -0
- data/spec/factories/mdm/host_details.rb +8 -0
- data/spec/factories/mdm/listeners.rb +12 -0
- data/spec/factories/mdm/loots.rb +11 -0
- data/spec/factories/mdm/nexpose_consoles.rb +15 -0
- data/spec/factories/mdm/notes.rb +12 -0
- data/spec/factories/mdm/report_templates.rb +8 -0
- data/spec/factories/mdm/reports.rb +13 -0
- data/spec/factories/mdm/routes.rb +36 -0
- data/spec/factories/mdm/session_events.rb +8 -0
- data/spec/factories/mdm/sessions.rb +13 -0
- data/spec/factories/mdm/vuln_attempts.rb +8 -0
- data/spec/factories/mdm/vuln_details.rb +8 -0
- data/spec/factories/mdm/web_forms.rb +33 -0
- data/spec/factories/mdm/web_pages.rb +64 -0
- metadata +95 -5
- data/app/models/mdm/imported_cred.rb +0 -10
@@ -18,5 +18,29 @@ describe Mdm::VulnRef do
|
|
18
18
|
it { should have_db_column(:vuln_id).of_type(:integer) }
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
context 'associations' do
|
23
|
+
it { should belong_to(:vuln).class_name('Mdm::Vuln') }
|
24
|
+
it { should belong_to(:ref).class_name('Mdm::Ref') }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'factory' do
|
28
|
+
it 'should be valid' do
|
29
|
+
vuln_ref = FactoryGirl.build(:mdm_vuln_ref)
|
30
|
+
vuln_ref.should be_valid
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#destroy' do
|
35
|
+
it 'should successfully destroy the object' do
|
36
|
+
vuln_ref = FactoryGirl.create(:mdm_vuln_ref)
|
37
|
+
expect {
|
38
|
+
vuln_ref.destroy
|
39
|
+
}.to_not raise_error
|
40
|
+
expect {
|
41
|
+
vuln_ref.reload
|
42
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
43
|
+
end
|
44
|
+
end
|
21
45
|
|
22
46
|
end
|
@@ -5,6 +5,31 @@ describe Mdm::Vuln do
|
|
5
5
|
FactoryGirl.build(:mdm_vuln)
|
6
6
|
end
|
7
7
|
|
8
|
+
context '#destroy' do
|
9
|
+
it 'should successfully destroy the object and dependent objects' do
|
10
|
+
vuln = FactoryGirl.create(:mdm_vuln)
|
11
|
+
vuln_attempt = FactoryGirl.create(:mdm_vuln_attempt, :vuln => vuln)
|
12
|
+
vuln_detail = FactoryGirl.create(:mdm_vuln_detail, :vuln => vuln)
|
13
|
+
vuln_ref = FactoryGirl.create(:mdm_vuln_ref, :vuln => vuln)
|
14
|
+
expect {
|
15
|
+
vuln.destroy
|
16
|
+
}.to_not raise_error
|
17
|
+
expect {
|
18
|
+
vuln.reload
|
19
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
20
|
+
expect {
|
21
|
+
vuln_attempt.reload
|
22
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
23
|
+
expect {
|
24
|
+
vuln_detail.reload
|
25
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
26
|
+
expect {
|
27
|
+
vuln_ref.reload
|
28
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
8
33
|
context 'associations' do
|
9
34
|
it { should belong_to(:host).class_name('Mdm::Host') }
|
10
35
|
it { should belong_to(:service).class_name('Mdm::Service') }
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mdm::WebForm do
|
4
|
+
|
5
|
+
context 'associations' do
|
6
|
+
it { should belong_to(:web_site).class_name('Mdm::WebSite') }
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'database' do
|
10
|
+
|
11
|
+
context 'timestamps'do
|
12
|
+
it { should have_db_column(:created_at).of_type(:datetime).with_options(:null => false) }
|
13
|
+
it { should have_db_column(:updated_at).of_type(:datetime).with_options(:null => false) }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'columns' do
|
17
|
+
it { should have_db_column(:web_site_id).of_type(:integer).with_options(:null => false) }
|
18
|
+
it { should have_db_column(:path).of_type(:text) }
|
19
|
+
it { should have_db_column(:method).of_type(:string) }
|
20
|
+
it { should have_db_column(:params).of_type(:text) }
|
21
|
+
it { should have_db_column(:query).of_type(:text) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'indices' do
|
25
|
+
it { should have_db_index(:path) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'factory' do
|
30
|
+
it 'should be valid' do
|
31
|
+
web_form = FactoryGirl.build(:mdm_web_form)
|
32
|
+
web_form.should be_valid
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context '#destroy' do
|
37
|
+
it 'should successfully destroy the object' do
|
38
|
+
web_form = FactoryGirl.create(:mdm_web_form)
|
39
|
+
expect {
|
40
|
+
web_form.destroy
|
41
|
+
}.to_not raise_error
|
42
|
+
expect {
|
43
|
+
web_form.reload
|
44
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mdm::WebPage do
|
4
|
+
|
5
|
+
context 'associations' do
|
6
|
+
it { should belong_to(:web_site).class_name('Mdm::WebSite') }
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'database' do
|
10
|
+
|
11
|
+
context 'timestamps'do
|
12
|
+
it { should have_db_column(:created_at).of_type(:datetime).with_options(:null => false) }
|
13
|
+
it { should have_db_column(:updated_at).of_type(:datetime).with_options(:null => false) }
|
14
|
+
it { should have_db_column(:mtime).of_type(:datetime) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'columns' do
|
18
|
+
it { should have_db_column(:web_site_id).of_type(:integer).with_options(:null => false) }
|
19
|
+
it { should have_db_column(:path).of_type(:text) }
|
20
|
+
it { should have_db_column(:query).of_type(:text) }
|
21
|
+
it { should have_db_column(:code).of_type(:integer).with_options(:null => false) }
|
22
|
+
it { should have_db_column(:cookie).of_type(:text) }
|
23
|
+
it { should have_db_column(:auth).of_type(:text) }
|
24
|
+
it { should have_db_column(:ctype).of_type(:text) }
|
25
|
+
it { should have_db_column(:location).of_type(:text) }
|
26
|
+
it { should have_db_column(:headers).of_type(:text) }
|
27
|
+
it { should have_db_column(:body).of_type(:binary) }
|
28
|
+
it { should have_db_column(:request).of_type(:binary) }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'indices' do
|
32
|
+
it { should have_db_index(:path) }
|
33
|
+
it { should have_db_index(:query) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'factory' do
|
38
|
+
it 'should be valid' do
|
39
|
+
web_page = FactoryGirl.build(:mdm_web_page)
|
40
|
+
web_page.should be_valid
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '#destroy' do
|
45
|
+
it 'should successfully destroy the object' do
|
46
|
+
web_page = FactoryGirl.create(:mdm_web_page)
|
47
|
+
expect {
|
48
|
+
web_page.destroy
|
49
|
+
}.to_not raise_error
|
50
|
+
expect {
|
51
|
+
web_page.reload
|
52
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mdm::WebSite do
|
4
|
+
|
5
|
+
context 'factory' do
|
6
|
+
it 'should be valid' do
|
7
|
+
web_site = FactoryGirl.build(:mdm_web_site)
|
8
|
+
web_site.should be_valid
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'database' do
|
13
|
+
|
14
|
+
context 'timestamps'do
|
15
|
+
it { should have_db_column(:created_at).of_type(:datetime).with_options(:null => false) }
|
16
|
+
it { should have_db_column(:updated_at).of_type(:datetime).with_options(:null => false) }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'columns' do
|
20
|
+
it { should have_db_column(:service_id).of_type(:integer).with_options(:null => false) }
|
21
|
+
it { should have_db_column(:vhost).of_type(:string) }
|
22
|
+
it { should have_db_column(:comments).of_type(:text) }
|
23
|
+
it { should have_db_column(:options).of_type(:text) }
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'indices' do
|
27
|
+
it { should have_db_index(:comments) }
|
28
|
+
it { should have_db_index(:options) }
|
29
|
+
it { should have_db_index(:vhost) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context '#destroy' do
|
34
|
+
it 'should successfully destroy the object' do
|
35
|
+
web_site = FactoryGirl.create(:mdm_web_site)
|
36
|
+
expect {
|
37
|
+
web_site.destroy
|
38
|
+
}.to_not raise_error
|
39
|
+
expect {
|
40
|
+
web_site.reload
|
41
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'associations' do
|
46
|
+
it { should belong_to(:service).class_name('Mdm::Service') }
|
47
|
+
it { should have_many(:web_forms).class_name('Mdm::WebForm').dependent(:destroy) }
|
48
|
+
it { should have_many(:web_pages).class_name('Mdm::WebPage').dependent(:destroy) }
|
49
|
+
it { should have_many(:web_vulns).class_name('Mdm::WebVuln').dependent(:destroy) }
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'methods' do
|
53
|
+
context '#form_count' do
|
54
|
+
it 'should return an accurate count of associated Webforms' do
|
55
|
+
mysite = FactoryGirl.create(:mdm_web_site)
|
56
|
+
FactoryGirl.create(:mdm_web_form, :web_site => mysite)
|
57
|
+
FactoryGirl.create(:mdm_web_form, :web_site => mysite)
|
58
|
+
mysite.form_count.should == 2
|
59
|
+
FactoryGirl.create(:mdm_web_form, :web_site => mysite)
|
60
|
+
mysite.form_count.should == 3
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context '#page_count' do
|
65
|
+
it 'should return an accurate count of associated Webpages' do
|
66
|
+
mysite = FactoryGirl.create(:mdm_web_site)
|
67
|
+
FactoryGirl.create(:mdm_web_page, :web_site => mysite)
|
68
|
+
FactoryGirl.create(:mdm_web_page, :web_site => mysite)
|
69
|
+
mysite.page_count.should == 2
|
70
|
+
FactoryGirl.create(:mdm_web_page, :web_site => mysite)
|
71
|
+
mysite.page_count.should == 3
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context '#vuln_count' do
|
76
|
+
it 'should return an accurate count of associated Webvulns' do
|
77
|
+
mysite = FactoryGirl.create(:mdm_web_site)
|
78
|
+
FactoryGirl.create(:mdm_web_vuln, :web_site => mysite)
|
79
|
+
FactoryGirl.create(:mdm_web_vuln, :web_site => mysite)
|
80
|
+
mysite.vuln_count.should == 2
|
81
|
+
FactoryGirl.create(:mdm_web_vuln, :web_site => mysite)
|
82
|
+
mysite.vuln_count.should == 3
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -44,6 +44,18 @@ describe Mdm::WebVuln do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
context '#destroy' do
|
48
|
+
it 'should successfully destroy the object' do
|
49
|
+
web_vuln = FactoryGirl.create(:mdm_web_vuln)
|
50
|
+
expect {
|
51
|
+
web_vuln.destroy
|
52
|
+
}.to_not raise_error
|
53
|
+
expect {
|
54
|
+
web_vuln.reload
|
55
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
47
59
|
context 'database' do
|
48
60
|
context 'columns' do
|
49
61
|
it { should have_db_column(:blame).of_type(:text) }
|
@@ -0,0 +1,567 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mdm::Workspace do
|
4
|
+
subject(:workspace) do
|
5
|
+
FactoryGirl.build(:mdm_workspace)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:default) do
|
9
|
+
'default'
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'factory' do
|
13
|
+
it 'should be valid' do
|
14
|
+
workspace = FactoryGirl.build(:mdm_workspace)
|
15
|
+
workspace.should be_valid
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context '#destroy' do
|
20
|
+
it 'should successfully destroy the object and dependent objects' do
|
21
|
+
workspace = FactoryGirl.create(:mdm_workspace)
|
22
|
+
listener = FactoryGirl.create(:mdm_listener, :workspace => workspace)
|
23
|
+
report_template = FactoryGirl.create(:mdm_report_template, :workspace => workspace)
|
24
|
+
report = FactoryGirl.create(:mdm_report, :workspace => workspace)
|
25
|
+
task = FactoryGirl.create(:mdm_task, :workspace => workspace)
|
26
|
+
|
27
|
+
expect {
|
28
|
+
workspace.destroy
|
29
|
+
}.to_not raise_error
|
30
|
+
expect {
|
31
|
+
workspace.reload
|
32
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
33
|
+
expect {
|
34
|
+
listener.reload
|
35
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
36
|
+
expect {
|
37
|
+
report_template.reload
|
38
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
39
|
+
expect {
|
40
|
+
report.reload
|
41
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
42
|
+
expect {
|
43
|
+
task.reload
|
44
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'associations' do
|
49
|
+
it { should have_many(:clients).class_name('Mdm::Client').through(:hosts) }
|
50
|
+
it { should have_many(:creds).class_name('Mdm::Cred').through(:services) }
|
51
|
+
it { should have_many(:events).class_name('Mdm::Event') }
|
52
|
+
it { should have_many(:exploited_hosts).class_name('Mdm::ExploitedHost').through(:hosts) }
|
53
|
+
it { should have_many(:hosts).class_name('Mdm::Host') }
|
54
|
+
it { should have_many(:listeners).class_name('Mdm::Listener').dependent(:destroy) }
|
55
|
+
it { should have_many(:loots).class_name('Mdm::Loot').through(:hosts) }
|
56
|
+
it { should have_many(:notes).class_name('Mdm::Note') }
|
57
|
+
it { should belong_to(:owner).class_name('Mdm::User').with_foreign_key('owner_id') }
|
58
|
+
it { should have_many(:report_templates).class_name('Mdm::ReportTemplate').dependent(:destroy) }
|
59
|
+
it { should have_many(:reports).class_name('Mdm::Report').dependent(:destroy) }
|
60
|
+
it { should have_many(:services).class_name('Mdm::Service').through(:hosts).with_foreign_key('service_id') }
|
61
|
+
it { should have_many(:sessions).class_name('Mdm::Session').through(:hosts) }
|
62
|
+
it { should have_many(:tasks).class_name('Mdm::Task').dependent(:destroy).order('created_at DESC') }
|
63
|
+
it { should have_and_belong_to_many(:users).class_name('Mdm::User') }
|
64
|
+
it { should have_many(:vulns).class_name('Mdm::Vuln').through(:hosts) }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'callbacks' do
|
68
|
+
context 'before_save' do
|
69
|
+
context '#normalize' do
|
70
|
+
it 'should be called' do
|
71
|
+
workspace.should_receive(:normalize)
|
72
|
+
workspace.run_callbacks(:save, false)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'columns' do
|
79
|
+
it { should have_db_column(:boundary).of_type(:string).with_options(:limit => 4 * (2 ** 10)) }
|
80
|
+
it { should have_db_column(:description).of_type(:string).with_options(:limit => 4 * (2 ** 10)) }
|
81
|
+
it { should have_db_column(:limit_to_network).of_type(:boolean).with_options(:default => false, :null => false) }
|
82
|
+
it { should have_db_column(:name).of_type(:string) }
|
83
|
+
it { should have_db_column(:owner_id).of_type(:integer) }
|
84
|
+
|
85
|
+
context 'timestamps' do
|
86
|
+
it { should have_db_column(:created_at).of_type(:datetime).with_options(:null => false) }
|
87
|
+
it { should have_db_column(:updated_at).of_type(:datetime).with_options(:null => false) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'CONSTANTS' do
|
92
|
+
it 'should define the DEFAULT name' do
|
93
|
+
described_class::DEFAULT.should == default
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'validations' do
|
98
|
+
context 'boundary' do
|
99
|
+
let(:boundary) do
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
let(:error) do
|
104
|
+
'must be a valid IP range'
|
105
|
+
end
|
106
|
+
|
107
|
+
before(:each) do
|
108
|
+
workspace.boundary = boundary
|
109
|
+
workspace.valid?
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should validate using #valid_ip_or_range?', :pending => 'https://www.pivotaltracker.com/story/show/43244445' do
|
113
|
+
workspace.should_receive(:valid_ip_or_range?).with(boundary).and_return(false)
|
114
|
+
|
115
|
+
workspace.valid?
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'with valid IP' do
|
119
|
+
let(:boundary) do
|
120
|
+
'192.168.0.1'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should not record an error' do
|
124
|
+
workspace.errors[:boundary].should_not include(error)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'with valid range' do
|
129
|
+
let(:boundary) do
|
130
|
+
'192.168.0.1/24'
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should not record an error' do
|
134
|
+
workspace.errors[:boundary].should_not include(error)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'with invalid IP or range' do
|
139
|
+
let(:boundary) do
|
140
|
+
'192.168'
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should record error that boundary must be a valid IP range', :pending => 'https://www.pivotaltracker.com/story/show/43171927' do
|
144
|
+
workspace.should_not be_valid
|
145
|
+
workkspace.errors[:boundary].should include(error)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'description' do
|
151
|
+
it { should ensure_length_of(:description).is_at_most(4 * (2 ** 10)) }
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'name' do
|
155
|
+
it { should ensure_length_of(:name).is_at_most(2**8 - 1) }
|
156
|
+
it { should validate_presence_of :name }
|
157
|
+
it { should validate_uniqueness_of :name }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'methods' do
|
162
|
+
let(:hosts) do
|
163
|
+
FactoryGirl.create_list(:mdm_host, 2, :workspace => workspace)
|
164
|
+
end
|
165
|
+
|
166
|
+
let(:other_hosts) do
|
167
|
+
FactoryGirl.create_list(:mdm_host, 2, :workspace => other_workspace)
|
168
|
+
end
|
169
|
+
|
170
|
+
let(:other_services) do
|
171
|
+
other_hosts.collect do |host|
|
172
|
+
FactoryGirl.create(:mdm_service, :host => host)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
let(:other_web_sites) do
|
177
|
+
other_services.collect { |service|
|
178
|
+
FactoryGirl.create(:mdm_web_site, :service => service)
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
let(:other_workspace) do
|
183
|
+
FactoryGirl.create(:mdm_workspace)
|
184
|
+
end
|
185
|
+
|
186
|
+
let(:services) do
|
187
|
+
hosts.collect do |host|
|
188
|
+
FactoryGirl.create(:mdm_service, :host => host)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
let(:web_sites) do
|
193
|
+
services.collect { |service|
|
194
|
+
FactoryGirl.create(:mdm_web_site, :service => service)
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
context '#creds' do
|
199
|
+
#
|
200
|
+
# Let!s (let + before(:each))
|
201
|
+
#
|
202
|
+
|
203
|
+
let!(:creds) do
|
204
|
+
services.collect do |service|
|
205
|
+
FactoryGirl.create(:mdm_cred, :service => service)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
let!(:other_creds) do
|
210
|
+
other_services.collect do |service|
|
211
|
+
FactoryGirl.create(:mdm_cred, :service => service)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'should be an ActiveRecord::Relation', :pending => 'https://www.pivotaltracker.com/story/show/43219917' do
|
216
|
+
should be_a ActiveRecord::Relation
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should include services' do
|
220
|
+
# to_a to make query return instances
|
221
|
+
found_creds = workspace.creds.to_a
|
222
|
+
|
223
|
+
found_creds.length.should > 0
|
224
|
+
found_cred = found_creds.first
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'should include hosts' do
|
229
|
+
found_creds = workspace.creds.to_a
|
230
|
+
|
231
|
+
found_creds.length.should > 0
|
232
|
+
found_cred = found_creds.first
|
233
|
+
service = found_cred.service
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should return only Mdm::Creds from hosts in workspace' do
|
237
|
+
found_creds = workspace.creds
|
238
|
+
|
239
|
+
found_creds.length.should == creds.length
|
240
|
+
|
241
|
+
found_creds.all? { |cred|
|
242
|
+
cred.service.host.workspace == workspace
|
243
|
+
}.should be_true
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context 'default' do
|
248
|
+
context 'with default workspace' do
|
249
|
+
before(:each) do
|
250
|
+
FactoryGirl.create(
|
251
|
+
:mdm_workspace,
|
252
|
+
:name => default
|
253
|
+
)
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should not create workspace' do
|
257
|
+
workspace = nil
|
258
|
+
|
259
|
+
expect {
|
260
|
+
workspace = described_class.default
|
261
|
+
}.to change(Mdm::Workspace, :count).by(0)
|
262
|
+
|
263
|
+
workspace.should be_default
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'without default workspace' do
|
268
|
+
it 'should create workspace' do
|
269
|
+
workspace = nil
|
270
|
+
|
271
|
+
expect {
|
272
|
+
workspace = described_class.default
|
273
|
+
}.to change(Mdm::Workspace, :count).by(1)
|
274
|
+
|
275
|
+
workspace.should be_default
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context '#default?' do
|
281
|
+
subject do
|
282
|
+
workspace.default?
|
283
|
+
end
|
284
|
+
|
285
|
+
context 'with DEFAULT name' do
|
286
|
+
before(:each) do
|
287
|
+
workspace.name = default
|
288
|
+
end
|
289
|
+
|
290
|
+
it {
|
291
|
+
should be_true
|
292
|
+
}
|
293
|
+
end
|
294
|
+
|
295
|
+
context 'without DEFAULT name' do
|
296
|
+
it { should be_false }
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
context '#each_cred' do
|
301
|
+
it 'should pass each of the #creds to the block' do
|
302
|
+
creds = FactoryGirl.create_list(:mdm_cred, 2)
|
303
|
+
workspace.stub(:creds => creds)
|
304
|
+
|
305
|
+
expect { |block|
|
306
|
+
workspace.each_cred(&block)
|
307
|
+
}.to yield_successive_args(*creds)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context '#each_host_tag' do
|
312
|
+
it 'should pass each of the #host_tags to the block' do
|
313
|
+
tags = FactoryGirl.create_list(:mdm_tag, 2)
|
314
|
+
workspace.stub(:host_tags => tags)
|
315
|
+
|
316
|
+
expect { |block|
|
317
|
+
workspace.each_host_tag(&block)
|
318
|
+
}.to yield_successive_args(*tags)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context '#host_tags' do
|
323
|
+
let(:other_tags) do
|
324
|
+
FactoryGirl.create_list(
|
325
|
+
:mdm_tag,
|
326
|
+
2
|
327
|
+
)
|
328
|
+
end
|
329
|
+
|
330
|
+
let(:tags) do
|
331
|
+
FactoryGirl.create_list(
|
332
|
+
:mdm_tag,
|
333
|
+
2
|
334
|
+
)
|
335
|
+
end
|
336
|
+
|
337
|
+
subject(:host_tags) do
|
338
|
+
workspace.host_tags
|
339
|
+
end
|
340
|
+
|
341
|
+
#
|
342
|
+
# Let!s (let + before(:each))
|
343
|
+
#
|
344
|
+
|
345
|
+
let!(:host_tags) do
|
346
|
+
host_tags = []
|
347
|
+
|
348
|
+
hosts.zip(tags) do |host, tag|
|
349
|
+
host_tag = FactoryGirl.create(:mdm_host_tag, :host => host, :tag => tag)
|
350
|
+
|
351
|
+
host_tags << host_tag
|
352
|
+
end
|
353
|
+
|
354
|
+
host_tags
|
355
|
+
end
|
356
|
+
|
357
|
+
let!(:other_host_tags) do
|
358
|
+
host_tags = []
|
359
|
+
|
360
|
+
other_hosts.zip(other_tags) do |host, tag|
|
361
|
+
host_tag = FactoryGirl.create(:mdm_host_tag, :host => host, :tag => tag)
|
362
|
+
|
363
|
+
host_tags << host_tag
|
364
|
+
end
|
365
|
+
|
366
|
+
host_tags
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'should return an ActiveRecord::Relation', :pending => 'https://www.pivotaltracker.com/story/show/43219917' do
|
370
|
+
should be_a ActiveRecord::Relation
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'should include hosts' do
|
374
|
+
found_tags = workspace.host_tags.to_a
|
375
|
+
|
376
|
+
found_tags.length.should > 0
|
377
|
+
|
378
|
+
tag = found_tags.first
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'should return only Mdm::Tags from hosts in the workspace' do
|
382
|
+
found_tags = workspace.host_tags
|
383
|
+
|
384
|
+
found_tags.length.should == tags.length
|
385
|
+
|
386
|
+
found_tags.all? { |tag|
|
387
|
+
tag.hosts.any? { |host|
|
388
|
+
host.workspace == workspace
|
389
|
+
}
|
390
|
+
}.should be_true
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
context '#normalize' do
|
395
|
+
let(:normalize) do
|
396
|
+
workspace.send(:normalize)
|
397
|
+
end
|
398
|
+
|
399
|
+
before(:each) do
|
400
|
+
workspace.boundary = boundary
|
401
|
+
end
|
402
|
+
|
403
|
+
context 'with boundary' do
|
404
|
+
let(:boundary) do
|
405
|
+
" #{stripped_boundary} "
|
406
|
+
end
|
407
|
+
|
408
|
+
let(:stripped_boundary) do
|
409
|
+
'192.168.0.1'
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should remove spaces" do
|
413
|
+
normalize
|
414
|
+
|
415
|
+
workspace.boundary.should == stripped_boundary
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
context 'without boundary' do
|
420
|
+
let(:boundary) do
|
421
|
+
nil
|
422
|
+
end
|
423
|
+
|
424
|
+
it 'should not raise error' do
|
425
|
+
expect {
|
426
|
+
normalize
|
427
|
+
}.to_not raise_error
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context '#web_forms' do
|
433
|
+
|
434
|
+
subject do
|
435
|
+
workspace.web_forms
|
436
|
+
end
|
437
|
+
|
438
|
+
#
|
439
|
+
# Let!s (let + before(:each))
|
440
|
+
#
|
441
|
+
|
442
|
+
let!(:other_web_forms) do
|
443
|
+
other_web_sites.collect { |web_site|
|
444
|
+
FactoryGirl.create(:web_form, :web_site => web_site)
|
445
|
+
}
|
446
|
+
end
|
447
|
+
|
448
|
+
let!(:web_forms) do
|
449
|
+
web_sites.collect { |web_site|
|
450
|
+
FactoryGirl.create(:web_form, :web_site => web_site)
|
451
|
+
}
|
452
|
+
end
|
453
|
+
|
454
|
+
it 'should return an ActiveRecord:Relation',
|
455
|
+
:pending => 'https://www.pivotaltracker.com/story/show/43219917' do
|
456
|
+
should be_a ActiveRecord::Relation
|
457
|
+
end
|
458
|
+
|
459
|
+
it 'should return only Mdm::WebPages from hosts in the workspace' do
|
460
|
+
found_web_forms = workspace.web_forms
|
461
|
+
|
462
|
+
found_web_forms.length.should == web_forms.length
|
463
|
+
|
464
|
+
found_web_forms.all? { |web_form|
|
465
|
+
web_form.web_site.service.host.workspace == workspace
|
466
|
+
}.should be_true
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
context '#web_sites' do
|
471
|
+
subject do
|
472
|
+
workspace.web_sites
|
473
|
+
end
|
474
|
+
|
475
|
+
#
|
476
|
+
# Let!s (let + before(:each))
|
477
|
+
#
|
478
|
+
|
479
|
+
before(:each) do
|
480
|
+
other_web_sites
|
481
|
+
web_sites
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'should return an ActiveRecord:Relation',
|
485
|
+
:pending => 'https://www.pivotaltracker.com/story/show/43219917' do
|
486
|
+
should be_a ActiveRecord::Relation
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'should return only Mdm::WebVulns from hosts in the workspace' do
|
490
|
+
# there are more web sites than those in the workspace
|
491
|
+
Mdm::WebSite.count.should > web_sites.count
|
492
|
+
|
493
|
+
found_web_sites = workspace.web_sites
|
494
|
+
|
495
|
+
found_web_sites.length.should == web_sites.count
|
496
|
+
|
497
|
+
found_web_sites.all? { |web_site|
|
498
|
+
web_site.service.host.workspace == workspace
|
499
|
+
}.should be_true
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
context '#web_vulns' do
|
504
|
+
subject do
|
505
|
+
workspace.web_vulns
|
506
|
+
end
|
507
|
+
|
508
|
+
#
|
509
|
+
# Let!s (let + before(:each))
|
510
|
+
#
|
511
|
+
|
512
|
+
let!(:other_web_vulns) do
|
513
|
+
other_web_sites.collect { |web_site|
|
514
|
+
FactoryGirl.create(:mdm_web_vuln, :web_site => web_site)
|
515
|
+
}
|
516
|
+
end
|
517
|
+
|
518
|
+
let!(:web_vulns) do
|
519
|
+
web_sites.collect { |web_site|
|
520
|
+
FactoryGirl.create(:mdm_web_vuln, :web_site => web_site)
|
521
|
+
}
|
522
|
+
end
|
523
|
+
|
524
|
+
it 'should return an ActiveRecord:Relation',
|
525
|
+
:pending => 'https://www.pivotaltracker.com/story/show/43219917' do
|
526
|
+
should be_a ActiveRecord::Relation
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'should return only Mdm::WebVulns from hosts in the workspace' do
|
530
|
+
Mdm::WebVuln.count.should > web_vulns.length
|
531
|
+
|
532
|
+
found_web_vulns = workspace.web_vulns
|
533
|
+
|
534
|
+
found_web_vulns.length.should == web_vulns.length
|
535
|
+
|
536
|
+
found_web_vulns.all? { |web_vuln|
|
537
|
+
web_vuln.web_site.service.host.workspace == workspace
|
538
|
+
}.should be_true
|
539
|
+
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
context '#web_unique_forms' do
|
544
|
+
let(:rejected_address) do
|
545
|
+
hosts[1].address
|
546
|
+
end
|
547
|
+
|
548
|
+
let(:selected_address) do
|
549
|
+
hosts[0].address
|
550
|
+
end
|
551
|
+
|
552
|
+
it 'should return an ActiveRecord:Relation',
|
553
|
+
:pending => 'https://www.pivotaltracker.com/story/show/43219917' do
|
554
|
+
should be_a ActiveRecord::Relation
|
555
|
+
end
|
556
|
+
|
557
|
+
it "should reject #unique_web_forms from host addresses that aren't in addresses" do
|
558
|
+
web_forms = workspace.web_unique_forms([selected_address])
|
559
|
+
|
560
|
+
web_forms.all? { |web_form|
|
561
|
+
web_form.web_site.service.host.address.should == selected_address
|
562
|
+
}.should be_true
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
end
|