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

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 (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