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.
- checksums.yaml +8 -8
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/app/models/mdm/host.rb +24 -0
- data/app/models/mdm/service.rb +9 -0
- data/app/models/metasploit_data_models/search/visitor/attribute.rb +13 -0
- data/app/models/metasploit_data_models/search/visitor/includes.rb +28 -0
- data/app/models/metasploit_data_models/search/visitor/joins.rb +47 -0
- data/app/models/metasploit_data_models/search/visitor/method.rb +13 -0
- data/app/models/metasploit_data_models/search/visitor/relation.rb +87 -0
- data/app/models/metasploit_data_models/search/visitor/where.rb +65 -0
- data/config/locales/en.yml +7 -0
- data/lib/metasploit_data_models.rb +1 -0
- data/lib/metasploit_data_models/search.rb +6 -0
- data/lib/metasploit_data_models/search/visitor.rb +8 -0
- data/lib/metasploit_data_models/version.rb +1 -1
- data/metasploit_data_models.gemspec +1 -0
- data/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb +74 -0
- data/spec/app/models/metasploit_data_models/search/visitor/includes_spec.rb +124 -0
- data/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb +292 -0
- data/spec/app/models/metasploit_data_models/search/visitor/method_spec.rb +33 -0
- data/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb +253 -0
- data/spec/app/models/metasploit_data_models/search/visitor/where_spec.rb +147 -0
- data/spec/dummy/config/initializers/active_record_migrations.rb +4 -0
- data/spec/factories/mdm/services.rb +5 -0
- data/spec/support/shared/examples/metasploit_data_models/search/visitor/includes/visit/with_children.rb +38 -0
- data/spec/support/shared/examples/metasploit_data_models/search/visitor/includes/visit/with_metasploit_model_search_operation_base.rb +26 -0
- data/spec/support/shared/examples/metasploit_data_models/search/visitor/relation/visit/matching_record.rb +37 -0
- data/spec/support/shared/examples/metasploit_data_models/search/visitor/relation/visit/matching_record/with_metasploit_model_search_opeator_deprecated_platform.rb +31 -0
- data/spec/support/shared/examples/metasploit_data_models/search/visitor/relation/visit/matching_record/with_metasploit_model_search_operator_deprecated_authority.rb +78 -0
- data/spec/support/shared/examples/metasploit_data_models/search/visitor/where/visit/with_equality.rb +34 -0
- data/spec/support/shared/examples/metasploit_data_models/search/visitor/where/visit/with_metasploit_model_search_group_base.rb +50 -0
- metadata +61 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZTk1ODlmNzUyMDg2MjZlMjZkNzUyMWVmZWQ4NTVlMzhlZmU2OWM2OA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MDUzNTY1ZjRjNzU5NWYzOTMyODE1NGIxNzI0MzAwMjRlOWNjY2I2NQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YTlhMTU5NTRiYTI5Y2YwZjYwOTMzNWIxN2NjOTY4YWVjNmE4Y2Q2OWFiNzc2
|
10
|
+
YzQ4NTJhNDRlOGYzODdkNjQ4MDljNDJlZTZlNTIxZmFjMTk5YTdjODU4NGVj
|
11
|
+
NjUzZTQyZTU1MmIzNGQ2NDg5MjdlZTk0MDJhM2Q4Mjc4MzY3NWI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTFiMTFkODU2ZTZmZjE5ZGY1NjQwYjZkMDJkNjY2NjBlMGU4YzhmODQzNzJi
|
14
|
+
NWRjY2M5MjI4NDhhM2ZiM2ZmZGRhNWNhZjRlNDBhNzc0ODY3OTRkNjI0NzE3
|
15
|
+
MWIyZDUyZjg3NWU2NzEwMWVkMjc0MTYwNzMwZWMyYjQ0ZGFkNzQ=
|
data/.travis.yml
CHANGED
@@ -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'
|
data/app/models/mdm/host.rb
CHANGED
@@ -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`.
|
data/app/models/mdm/service.rb
CHANGED
@@ -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
|
@@ -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.
|
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
|