metasploit_data_models 0.17.1 → 0.17.2.pre.metasploit.pre.data.pre.models.pre.search

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +4 -0
  3. data/Gemfile +2 -0
  4. data/app/models/mdm/host.rb +24 -0
  5. data/app/models/mdm/service.rb +9 -0
  6. data/app/models/metasploit_data_models/search/visitor/attribute.rb +13 -0
  7. data/app/models/metasploit_data_models/search/visitor/includes.rb +28 -0
  8. data/app/models/metasploit_data_models/search/visitor/joins.rb +47 -0
  9. data/app/models/metasploit_data_models/search/visitor/method.rb +13 -0
  10. data/app/models/metasploit_data_models/search/visitor/relation.rb +87 -0
  11. data/app/models/metasploit_data_models/search/visitor/where.rb +65 -0
  12. data/config/locales/en.yml +7 -0
  13. data/lib/metasploit_data_models.rb +1 -0
  14. data/lib/metasploit_data_models/search.rb +6 -0
  15. data/lib/metasploit_data_models/search/visitor.rb +8 -0
  16. data/lib/metasploit_data_models/version.rb +1 -1
  17. data/metasploit_data_models.gemspec +1 -0
  18. data/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb +74 -0
  19. data/spec/app/models/metasploit_data_models/search/visitor/includes_spec.rb +124 -0
  20. data/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb +292 -0
  21. data/spec/app/models/metasploit_data_models/search/visitor/method_spec.rb +33 -0
  22. data/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb +253 -0
  23. data/spec/app/models/metasploit_data_models/search/visitor/where_spec.rb +147 -0
  24. data/spec/dummy/config/initializers/active_record_migrations.rb +4 -0
  25. data/spec/factories/mdm/services.rb +5 -0
  26. data/spec/support/shared/examples/metasploit_data_models/search/visitor/includes/visit/with_children.rb +38 -0
  27. data/spec/support/shared/examples/metasploit_data_models/search/visitor/includes/visit/with_metasploit_model_search_operation_base.rb +26 -0
  28. data/spec/support/shared/examples/metasploit_data_models/search/visitor/relation/visit/matching_record.rb +37 -0
  29. data/spec/support/shared/examples/metasploit_data_models/search/visitor/relation/visit/matching_record/with_metasploit_model_search_opeator_deprecated_platform.rb +31 -0
  30. data/spec/support/shared/examples/metasploit_data_models/search/visitor/relation/visit/matching_record/with_metasploit_model_search_operator_deprecated_authority.rb +78 -0
  31. data/spec/support/shared/examples/metasploit_data_models/search/visitor/where/visit/with_equality.rb +34 -0
  32. data/spec/support/shared/examples/metasploit_data_models/search/visitor/where/visit/with_metasploit_model_search_group_base.rb +50 -0
  33. metadata +61 -4
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YzgwYmExOTgyYjQyMTk5YzVlYjM0MWI2ZGEzNmQ5ZDU2NTQ3ZGI5Yw==
4
+ ZTk1ODlmNzUyMDg2MjZlMjZkNzUyMWVmZWQ4NTVlMzhlZmU2OWM2OA==
5
5
  data.tar.gz: !binary |-
6
- MzcxMjM2YmVkOTE4OWIwODRjYjE2NjIyMDMyZDBmY2I3OWQzYWZlNg==
6
+ MDUzNTY1ZjRjNzU5NWYzOTMyODE1NGIxNzI0MzAwMjRlOWNjY2I2NQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODM4ZTQwMzc4MWNlNTIzM2Q4ZjY1OTU1ZGQ3MGViNTg5MzBlZjc1YjE3MGZl
10
- YTkxYjdlNzY3MmQyZTU2Y2NlNmJlNmNkN2U3OWY5ZWUxMGI1ZTIyMWRlYmU3
11
- YmVhNDZjMjc0MTA3MWJlOGQ1ZTY5NjFjYWI3MTlkYmNkYjVlNmI=
9
+ YTlhMTU5NTRiYTI5Y2YwZjYwOTMzNWIxN2NjOTY4YWVjNmE4Y2Q2OWFiNzc2
10
+ YzQ4NTJhNDRlOGYzODdkNjQ4MDljNDJlZTZlNTIxZmFjMTk5YTdjODU4NGVj
11
+ NjUzZTQyZTU1MmIzNGQ2NDg5MjdlZTk0MDJhM2Q4Mjc4MzY3NWI=
12
12
  data.tar.gz: !binary |-
13
- ZGI0M2RlMDNlNzhhYjA5ZWUyOTJmNDIyNDZhYzIwNGI3ODAzN2I0NjkzMWYy
14
- MTc3N2U1MWQxYzc4ZThjYTRhYmJhMTBjY2MyMzJmN2FjNzEzNDBjMmU2MDdk
15
- ZWQ5YThhMDdiZDk5MGFhNzUxMjNhYjhiZDYxYTdjYTFhNzEzMTk=
13
+ NTFiMTFkODU2ZTZmZjE5ZGY1NjQwYjZkMDJkNjY2NjBlMGU4YzhmODQzNzJi
14
+ NWRjY2M5MjI4NDhhM2ZiM2ZmZGRhNWNhZjRlNDBhNzc0ODY3OTRkNjI0NzE3
15
+ MWIyZDUyZjg3NWU2NzEwMWVkMjc0MTYwNzMwZWMyYjQ0ZGFkNzQ=
@@ -2,6 +2,10 @@ before_script:
2
2
  - cp spec/dummy/config/database.yml.travis spec/dummy/config/database.yml
3
3
  - rake db:setup
4
4
  language: ruby
5
+ matrix:
6
+ allow_failures:
7
+ # Rubinius.mri_backtrace primitive failed (PrimitiveFailure)
8
+ - rvm: 'rbx-2.2'
5
9
  rvm:
6
10
  - '1.9.3'
7
11
  - '2.0'
data/Gemfile CHANGED
@@ -32,6 +32,8 @@ group :test do
32
32
  gem 'shoulda-matchers'
33
33
  # code coverage of tests
34
34
  gem 'simplecov', :require => false
35
+ # @todo Update specs for rspec 3.0.0 compatibility and remove this gem in favor of just rspec-rails
36
+ gem 'rspec-core', '< 3.0.0'
35
37
  # need rspec-rails >= 2.12.0 as 2.12.0 adds support for redefining named subject in nested context that uses the
36
38
  # named subject from the outer context without causing a stack overflow.
37
39
  gem 'rspec-rails', '>= 2.12.0'
@@ -1,6 +1,7 @@
1
1
  # A system with an {#address IP address} on the network that has been discovered in some way.
2
2
  class Mdm::Host < ActiveRecord::Base
3
3
  include Mdm::Host::OperatingSystemNormalization
4
+ include Metasploit::Model::Search
4
5
 
5
6
  #
6
7
  # CONSTANTS
@@ -462,6 +463,29 @@ class Mdm::Host < ActiveRecord::Base
462
463
  scope :tag_search,
463
464
  lambda { |*args| where("tags.name" => args[0]).includes(:tags) }
464
465
 
466
+ #
467
+ #
468
+ # Search
469
+ #
470
+ #
471
+
472
+ #
473
+ # Search Associations
474
+ #
475
+
476
+ search_association :services
477
+
478
+ #
479
+ # Search Attributes
480
+ #
481
+
482
+ search_attribute :name,
483
+ type: :string
484
+
485
+ #
486
+ # Instance Methods
487
+ #
488
+
465
489
  # Returns whether 'host.updated.<attr>' {#notes note} is {Mdm::Note#data locked}.
466
490
  #
467
491
  # @return [true] if Mdm::Note with 'host.updated.<attr>' as {Mdm::Note#name} exists and data[:locked] is `true`.
@@ -1,5 +1,7 @@
1
1
  # A service, such as an ssh server or web server, running on a {#host}.
2
2
  class Mdm::Service < ActiveRecord::Base
3
+ include Metasploit::Model::Search
4
+
3
5
  #
4
6
  # CONSTANTS
5
7
  #
@@ -174,6 +176,13 @@ class Mdm::Service < ActiveRecord::Base
174
176
  ])
175
177
  }
176
178
 
179
+ #
180
+ # Search Attributes
181
+ #
182
+
183
+ search_attribute :name,
184
+ type: :string
185
+
177
186
  #
178
187
  # Validations
179
188
  #
@@ -0,0 +1,13 @@
1
+ # Extracts the `Arel::Attribute` objects from `Metasploit::Model::Search::Operator::Base` subclasses.
2
+ class MetasploitDataModels::Search::Visitor::Attribute
3
+ include Metasploit::Model::Visitation::Visit
4
+
5
+ visit 'Metasploit::Model::Search::Operator::Association' do |operator|
6
+ visit operator.attribute_operator
7
+ end
8
+
9
+ visit 'Metasploit::Model::Search::Operator::Attribute' do |operator|
10
+ table = operator.klass.arel_table
11
+ table[operator.attribute]
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ # Gathers all the association names to pass to `ActiveRecord::Relation#includes` from a
2
+ # `Metasploit::Model::Search::Query`
3
+ class MetasploitDataModels::Search::Visitor::Includes
4
+ include Metasploit::Model::Visitation::Visit
5
+
6
+ #
7
+ # Visitors
8
+ #
9
+
10
+ visit 'Metasploit::Model::Search::Group::Base',
11
+ 'Metasploit::Model::Search::Operation::Union' do |parent|
12
+ parent.children.flat_map { |child|
13
+ visit child
14
+ }
15
+ end
16
+
17
+ visit 'Metasploit::Model::Search::Operation::Base' do |operation|
18
+ visit operation.operator
19
+ end
20
+
21
+ visit 'Metasploit::Model::Search::Operator::Association' do |operator|
22
+ [operator.association]
23
+ end
24
+
25
+ visit 'Metasploit::Model::Search::Operator::Attribute' do |_operator|
26
+ []
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ # Gathers all the association names to pass to `ActiveRecord::Relation#joins` from a `Metasploit::Model::Search::Query`
2
+ class MetasploitDataModels::Search::Visitor::Joins
3
+ include Metasploit::Model::Visitation::Visit
4
+
5
+ #
6
+ # Visitors
7
+ #
8
+
9
+ visit 'Metasploit::Model::Search::Group::Intersection' do |parent|
10
+ parent.children.flat_map { |child|
11
+ visit child
12
+ }
13
+ end
14
+
15
+ visit 'Metasploit::Model::Search::Group::Union',
16
+ 'Metasploit::Model::Search::Operation::Union' do |parent|
17
+ # A Set<Set> because if all children have multiple joins, but those multiple joins contain the same elements for
18
+ # all children, then all joins can be counted as common:
19
+ #
20
+ # (a.b:1 && c.d:2) || (a.b:3 && c.d:4) should return [:a, :c] since its common to both
21
+ # (a.b:1 && c.d:2 && e.f:3) || (a.b:3 && c.d:4) should return [:a, :c] since its the common _subset_
22
+ join_set_set = parent.children.each_with_object(Set.new) { |child, set|
23
+ child_joins = visit child
24
+ child_join_set = Set.new child_joins
25
+
26
+ set.add child_join_set
27
+ }
28
+
29
+ common_join_set = join_set_set.reduce { |common_subset, set|
30
+ common_subset & set
31
+ }
32
+
33
+ common_join_set.to_a
34
+ end
35
+
36
+ visit 'Metasploit::Model::Search::Operation::Base' do |operation|
37
+ visit operation.operator
38
+ end
39
+
40
+ visit 'Metasploit::Model::Search::Operator::Association' do |operator|
41
+ [operator.association]
42
+ end
43
+
44
+ visit 'Metasploit::Model::Search::Operator::Attribute' do |_|
45
+ []
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ # Extracts which AREL method to use as a translation for `Metasploit::Model::Search::Group::Base` subclasses.
2
+ class MetasploitDataModels::Search::Visitor::Method
3
+ include Metasploit::Model::Visitation::Visit
4
+
5
+ visit 'Metasploit::Model::Search::Group::Intersection' do
6
+ :and
7
+ end
8
+
9
+ visit 'Metasploit::Model::Search::Group::Union',
10
+ 'Metasploit::Model::Search::Operation::Union' do
11
+ :or
12
+ end
13
+ end
@@ -0,0 +1,87 @@
1
+ # Generates a `ActiveRecord::Relation` from an `Metasploit::Model::Search::Query#tree`
2
+ class MetasploitDataModels::Search::Visitor::Relation < Metasploit::Model::Base
3
+ #
4
+ # CONSTANTS
5
+ #
6
+
7
+ # `ActiveRecord::Relation` methods that can compute their argument with a visitor under the
8
+ # {MetasploitDataModels::Search::Visitor} namespace.
9
+ RELATION_METHODS = [
10
+ :joins,
11
+ :includes,
12
+ :where
13
+ ]
14
+
15
+ #
16
+ # Attributes
17
+ #
18
+
19
+ # @!attribute [rw] query
20
+ # The query to visit. Query supplies Class with #scope upon which to build `ActiveRecord::Relation`.
21
+ #
22
+ # @return [Metasploit::Model::Search::Query]
23
+ attr_accessor :query
24
+
25
+ #
26
+ # Validations
27
+ #
28
+
29
+ validate :valid_query
30
+
31
+ validates :query,
32
+ :presence => true
33
+
34
+ #
35
+ # Methods
36
+ #
37
+
38
+ # Visits {#query} tree to produce an `ActiveRecord::Relation` on the `Metasploit::Model::Search::Query#klass`.
39
+ #
40
+ # @return [ActiveRecord::Relation]
41
+ def visit
42
+ tree = query.tree
43
+
44
+ # Enumerable#inject does not support 3 arity for Hashes so need to unpack pair
45
+ visitor_by_relation_method.inject(query.klass.scoped) do |relation, pair|
46
+ relation_method, visitor = pair
47
+ visited = visitor.visit(tree)
48
+ relation.send(relation_method, visited)
49
+ end
50
+ end
51
+
52
+ # Map method on `ActiveRecord::Relation` to visitor that can visit `Metasploit::Model::Search::Query#tree` to
53
+ # produce the arguments to the method on `ActiveRecord::Relation`.
54
+ #
55
+ # @return [Hash{Symbol => #visit}]
56
+ def visitor_by_relation_method
57
+ # Enumerable#each_with_object does not support 3 arity for Hashes so need to unpack pair
58
+ @visitor_by_relation_method ||= self.class.visitor_class_by_relation_method.each_with_object({}) { |pair, visitor_by_relation_method|
59
+ relation_method, visitor_class = pair
60
+ visitor_by_relation_method[relation_method] = visitor_class.new
61
+ }
62
+ end
63
+
64
+ # Maps method on `ActiveRecord::Relation` to the `Class` under {MetasploitDataModels::Search::Visitor} whose
65
+ # `#visit` method can produce the arguments to the `ActiveRecord::Relation` method.
66
+ #
67
+ # @return [Hash{Symbol => Class}]
68
+ def self.visitor_class_by_relation_method
69
+ @relation_method_by_visitor_class ||= RELATION_METHODS.each_with_object({}) { |relation_method, relation_method_by_visitor_class|
70
+ visitor_class_name = "#{parent.name}::#{relation_method.to_s.camelize}"
71
+ visitor_class = visitor_class_name.constantize
72
+
73
+ relation_method_by_visitor_class[relation_method] = visitor_class
74
+ }
75
+ end
76
+
77
+ private
78
+
79
+ # Validates that {#query} is valid.
80
+ #
81
+ # @return [void]
82
+ def valid_query
83
+ if query and !query.valid?
84
+ errors.add(:query, :invalid)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,65 @@
1
+ # Generates AREL to pass to `ActiveRecord::Relation#where` from a `Metasploit::Model::Search::Query`.
2
+ class MetasploitDataModels::Search::Visitor::Where
3
+ include Metasploit::Model::Visitation::Visit
4
+
5
+ #
6
+ # CONSTANTS
7
+ #
8
+
9
+ # `Metasploit::Model::Search::Operation::Base` subclasses that check their value with the equality operator in
10
+ # AREL
11
+ EQUALITY_OPERATION_CLASS_NAMES = [
12
+ 'Metasploit::Model::Search::Operation::Boolean',
13
+ 'Metasploit::Model::Search::Operation::Date',
14
+ 'Metasploit::Model::Search::Operation::Integer',
15
+ 'Metasploit::Model::Search::Operation::Set'
16
+ ]
17
+
18
+ #
19
+ # Visitor
20
+ #
21
+
22
+ visit 'Metasploit::Model::Search::Group::Base',
23
+ 'Metasploit::Model::Search::Operation::Union' do |parent|
24
+ method = method_visitor.visit parent
25
+
26
+ children_arel = parent.children.collect { |child|
27
+ visit child
28
+ }
29
+
30
+ children_arel.inject { |group_arel, child_arel|
31
+ group_arel.send(method, child_arel)
32
+ }
33
+ end
34
+
35
+ visit *EQUALITY_OPERATION_CLASS_NAMES do |operation|
36
+ attribute = attribute_visitor.visit operation.operator
37
+
38
+ attribute.eq(operation.value)
39
+ end
40
+
41
+ visit 'Metasploit::Model::Search::Operation::String' do |operation|
42
+ attribute = attribute_visitor.visit operation.operator
43
+ match_value = "%#{operation.value}%"
44
+
45
+ attribute.matches(match_value)
46
+ end
47
+
48
+ #
49
+ # Methods
50
+ #
51
+
52
+ # Visitor for `Metasploit::Model::Search::Operator::Base` subclasses to generate `Arel::Attributes::Attribute`.
53
+ #
54
+ # @return [MetasploitDataModels::Search::Visitor::Attribute]
55
+ def attribute_visitor
56
+ @attribute_visitor ||= MetasploitDataModels::Search::Visitor::Attribute.new
57
+ end
58
+
59
+ # Visitor for `Metasploit::Model::Search::Group::Base` subclasses to generate equivalent AREL node methods.
60
+ #
61
+ # @return [MetasploitDataModels::Search::Visitor::Method]
62
+ def method_visitor
63
+ @method_visitor ||= MetasploitDataModels::Search::Visitor::Method.new
64
+ end
65
+ end
@@ -0,0 +1,7 @@
1
+ en:
2
+ metasploit:
3
+ model:
4
+ errors:
5
+ messages:
6
+ # have to duplicate activerecord.model.errors.message.taken because of the different i18n_scope
7
+ taken: "has already been taken"
@@ -10,6 +10,7 @@ require 'active_record'
10
10
  require 'active_support'
11
11
  require 'active_support/all'
12
12
  require 'active_support/dependencies'
13
+ require 'metasploit/model'
13
14
 
14
15
  #
15
16
  # Project
@@ -0,0 +1,6 @@
1
+ module MetasploitDataModels
2
+ # Namespace that deals with search {Mdm} models.
3
+ module Search
4
+
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module MetasploitDataModels
2
+ module Search
3
+ # Namespace for all visitors of `Metasploit::Model::Search::Query` that help search {Mdm} models.
4
+ module Visitor
5
+
6
+ end
7
+ end
8
+ end
@@ -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.17.1'
7
+ VERSION = '0.17.2-metasploit-data-models-search'
8
8
  end
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
38
38
  # @see MSP-2971
39
39
  s.add_runtime_dependency 'activerecord', '>= 3.2.13', '< 4.0.0'
40
40
  s.add_runtime_dependency 'activesupport'
41
+ s.add_runtime_dependency 'metasploit-model', '>= 0.24.1.pre.semantic.pre.versioning.pre.2.pre.0', '< 0.25'
41
42
 
42
43
  if RUBY_PLATFORM =~ /java/
43
44
  # markdown formatting for yard
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe MetasploitDataModels::Search::Visitor::Attribute do
4
+ subject(:visitor) do
5
+ described_class.new
6
+ end
7
+
8
+ context '#visit' do
9
+ subject(:visit) do
10
+ visitor.visit(node)
11
+ end
12
+
13
+ context 'with Metasploit::Model::Search::Operator::Association' do
14
+ let(:attribute_operator) do
15
+ double('Attribute Operator')
16
+ end
17
+
18
+ let(:node) do
19
+ Metasploit::Model::Search::Operator::Association.new(
20
+ :attribute_operator => attribute_operator
21
+ )
22
+ end
23
+
24
+ it 'should visit Metasploit::Model::Search::Operator::Association#attribute_operator' do
25
+ visitor.should_receive(:visit).with(node).and_call_original
26
+ visitor.should_receive(:visit).with(attribute_operator)
27
+
28
+ visit
29
+ end
30
+
31
+ it 'should return visit of Metasploit::Model::Search::Operator::Association#attribute_operator' do
32
+ visitor.should_receive(:visit).with(node).and_call_original
33
+
34
+ visited = double('Attribute Operator Visited')
35
+ visitor.stub(:visit).with(attribute_operator).and_return(visited)
36
+
37
+ visit.should == visited
38
+ end
39
+ end
40
+
41
+ context 'with Metasploit::Model::Search::Operator::Attribute' do
42
+ let(:node) do
43
+ Metasploit::Model::Search::Operator::Attribute.new(
44
+ # needs to be a real column so look up on AREL table works
45
+ :attribute => :name,
46
+ # needs to be a real class so Class#arel_table works
47
+ :klass => Mdm::Host
48
+ )
49
+ end
50
+
51
+ it { should be_a Arel::Attributes::Attribute }
52
+
53
+ context 'name' do
54
+ subject(:name) do
55
+ visit.name
56
+ end
57
+
58
+ it 'should be Metasploit::Model::Search::Operator::Attribute#attribute' do
59
+ name.should == node.attribute
60
+ end
61
+ end
62
+
63
+ context 'relation' do
64
+ subject(:relation) do
65
+ visit.relation
66
+ end
67
+
68
+ it 'should be Class#arel_table for Metasploit::Model::Search::Operator::Attribute#klass' do
69
+ relation.should == node.klass.arel_table
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end