metasploit-model 0.25.7 → 0.26.1
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/app/models/metasploit/model/search/operation/association.rb +57 -0
- data/app/models/metasploit/model/search/operator/association.rb +24 -14
- data/app/models/metasploit/model/search/operator/base.rb +19 -3
- data/app/models/metasploit/model/search/operator/single.rb +21 -1
- data/app/models/metasploit/model/search/query.rb +20 -1
- data/lib/metasploit/model/association/tree.rb +130 -0
- data/lib/metasploit/model/search.rb +61 -27
- data/lib/metasploit/model/search/association.rb +152 -8
- data/lib/metasploit/model/search/attribute.rb +112 -22
- data/lib/metasploit/model/search/operator.rb +53 -1
- data/lib/metasploit/model/search/operator/help.rb +39 -1
- data/lib/metasploit/model/search/with.rb +44 -1
- data/lib/metasploit/model/version.rb +2 -2
- data/spec/app/models/metasploit/model/search/operation/association_spec.rb +67 -0
- data/spec/app/models/metasploit/model/search/operator/association_spec.rb +76 -76
- data/spec/app/models/metasploit/model/search/operator/deprecated/author_spec.rb +54 -18
- data/spec/app/models/metasploit/model/search/operator/deprecated/authority_spec.rb +20 -8
- data/spec/app/models/metasploit/model/search/operator/deprecated/platform_spec.rb +20 -8
- data/spec/app/models/metasploit/model/search/operator/deprecated/ref_spec.rb +86 -26
- data/spec/app/models/metasploit/model/search/operator/deprecated/text_spec.rb +63 -21
- data/spec/lib/metasploit/model/search/association/tree_spec.rb +385 -0
- data/spec/lib/metasploit/model/search/association_spec.rb +99 -10
- data/spec/lib/metasploit/model/search_spec.rb +48 -107
- data/spec/support/shared/examples/search/query/metasploit/model/search/operator/deprecated/authority.rb +19 -7
- metadata +7 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzE5ZmE5MjgxN2RlZjhlMDk1NTM1OTcwYjZiZWRjMmRiOWI5ZDQ1Ng==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NmJjYTg0NWIwZGIxNzUyZGMwYmUyOTVlMzdlMGNmNGU5MDNkNjJiMQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MTNhYzY1MWNiNTNkNWY0ODBkZGMzNDMxN2JiYjA4YTlkNzg4Mzg3ZTRiNWI2
|
10
|
+
YTVmOGY1YWI1ZTVkZDdkMDJkN2M1NzU0ZGUzMmVkNDBiMThlZGVhMTg3MmRh
|
11
|
+
ZmQwZDI0NGJlYWRhMTQ3YTg5MDFiNmVlNDNiYjZjMzAwZWI4MjM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OWJmNTc3ZGM2MmJmZjUwNjJlZTUzZTI0NTY1ZjRjZWVhNWNkYWFiODQ4YjAz
|
14
|
+
OGJlZjM4ZDI5NjJkODI5MjdlOTEzZDVjYTI2YmRkMGJmMjNjMDUzOTM3NTVh
|
15
|
+
YTUyODRhMDViOWJiODg0Zjc5ZDI0NmQxYTg3M2VlNmEwNzdhNzA=
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# An operation with a {Metasploit::Model::Search::Operator::Association} for
|
2
|
+
# {Metasploit::Model::Search::Operation::Base#operator} that wraps a {#source_operation} produced by the
|
3
|
+
# {Metasploit::Model::Search::Operator::Association#source_operator}. This allows an arbitrary number of associations
|
4
|
+
# to be changed together until a non-association operation is found that actually validates the value.
|
5
|
+
class Metasploit::Model::Search::Operation::Association < Metasploit::Model::Search::Operation::Base
|
6
|
+
#
|
7
|
+
# Attributes
|
8
|
+
#
|
9
|
+
|
10
|
+
# @!attribute source_operation
|
11
|
+
# The operation from the {Metasploit::Model::Search::Operator::Association#source_operator}.
|
12
|
+
#
|
13
|
+
# @return [Metasploit::Model::Search::Operation::Base]
|
14
|
+
attr_accessor :source_operation
|
15
|
+
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# Validations
|
19
|
+
#
|
20
|
+
#
|
21
|
+
|
22
|
+
#
|
23
|
+
# Validation Methods
|
24
|
+
#
|
25
|
+
|
26
|
+
validate :source_operation_valid
|
27
|
+
|
28
|
+
#
|
29
|
+
# Attribute Validations
|
30
|
+
#
|
31
|
+
|
32
|
+
validates :source_operation,
|
33
|
+
presence: true
|
34
|
+
|
35
|
+
#
|
36
|
+
# Instance Methods
|
37
|
+
#
|
38
|
+
|
39
|
+
# Explicitly remove value attribute so code that depends on the old behavior will break so downstream developers know
|
40
|
+
# to update their code to use source_operation.
|
41
|
+
undef_method :value
|
42
|
+
undef_method :value=
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Validates that {#source_operation} is valid.
|
47
|
+
#
|
48
|
+
# @return [void]
|
49
|
+
def source_operation_valid
|
50
|
+
# presence validation handles errors when nil
|
51
|
+
if source_operation
|
52
|
+
unless source_operation.valid?
|
53
|
+
errors.add(:source_operation, :invalid)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,43 +1,53 @@
|
|
1
1
|
# A search operator declared with
|
2
2
|
# {Metasploit::Model::Search::Association::ClassMethods#search_association search_association}.
|
3
|
-
class Metasploit::Model::Search::Operator::Association < Metasploit::Model::Search::Operator::
|
3
|
+
class Metasploit::Model::Search::Operator::Association < Metasploit::Model::Search::Operator::Base
|
4
4
|
#
|
5
5
|
# Attributes
|
6
6
|
#
|
7
7
|
|
8
8
|
# @!attribute [rw] association
|
9
|
-
# The association on which {
|
9
|
+
# The association on which {#source_operator} was declared.
|
10
10
|
#
|
11
11
|
# @return [Symbol] association on {Metasploit::Model::Search::Operator::Base#klass klass}.
|
12
12
|
attr_accessor :association
|
13
13
|
|
14
|
-
# @!attribute [rw]
|
15
|
-
# The {Metasploit::Model::Search::Operator::
|
14
|
+
# @!attribute [rw] source_operator
|
15
|
+
# The {Metasploit::Model::Search::Operator::Base operator} as declared on the {#association} class.
|
16
16
|
#
|
17
|
-
# @return [Metasploit::Model::Search::Operator::
|
18
|
-
attr_accessor :
|
17
|
+
# @return [Metasploit::Model::Search::Operator::Base]
|
18
|
+
attr_accessor :source_operator
|
19
19
|
|
20
20
|
#
|
21
21
|
# Validations
|
22
22
|
#
|
23
23
|
|
24
24
|
validates :association, :presence => true
|
25
|
-
validates :
|
25
|
+
validates :source_operator, :presence => true
|
26
26
|
|
27
27
|
#
|
28
28
|
# Methods
|
29
29
|
#
|
30
30
|
|
31
|
-
delegate :
|
32
|
-
:
|
33
|
-
:help,
|
34
|
-
:type,
|
35
|
-
:to => :attribute_operator
|
31
|
+
delegate :help,
|
32
|
+
to: :source_operator
|
36
33
|
|
37
34
|
# The name of this operator.
|
38
35
|
#
|
39
|
-
# @return [String] <association>.<
|
36
|
+
# @return [String] <association>.<source_operator.name>
|
40
37
|
def name
|
41
|
-
@name ||= "#{association}.#{
|
38
|
+
@name ||= "#{association}.#{source_operator.name}".to_sym
|
39
|
+
end
|
40
|
+
|
41
|
+
# Creates a {Metasploit::Model::Search::Operation::Association} to wrap the original operation returned by
|
42
|
+
# {#source_operator}'s `#operate_on`.
|
43
|
+
#
|
44
|
+
# @param formatted_value [#to_s] Formatted value to pass to {#source_operator}.
|
45
|
+
# @return [Metasploit::Model::Search::Operation::Association] Association operation with the original operation from
|
46
|
+
# {#source_operator} operating on `formatted_value`.
|
47
|
+
def operate_on(formatted_value)
|
48
|
+
Metasploit::Model::Search::Operation::Association.new(
|
49
|
+
operator: self,
|
50
|
+
source_operation: source_operator.operate_on(formatted_value)
|
51
|
+
)
|
42
52
|
end
|
43
53
|
end
|
@@ -1,7 +1,23 @@
|
|
1
|
-
#
|
2
|
-
#
|
1
|
+
# Instead of writing an operator completely from scratch, you can subclass
|
2
|
+
# {Metasploit::Model::Search::Operator::Base}.
|
3
|
+
#
|
4
|
+
# class MyOperator < Metasploit::Model::Search::Operator::Base
|
5
|
+
# # Name of this operator. The name of the operator is matched to the string before the ':' in a formatted
|
6
|
+
# # operation.
|
7
|
+
# #
|
8
|
+
# # @return [Symbol]
|
9
|
+
# def name
|
10
|
+
# # ...
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# # Creates a one or more operations based on `formatted_value`.
|
14
|
+
# #
|
15
|
+
# # @return [#operator, Array<#operator>] Operation with this operator as the operation's `operator`.
|
16
|
+
# def operate_on(formatted_value)
|
17
|
+
# # ...
|
18
|
+
# end
|
19
|
+
# end
|
3
20
|
#
|
4
|
-
# A search operator.
|
5
21
|
class Metasploit::Model::Search::Operator::Base < Metasploit::Model::Base
|
6
22
|
include ActiveModel::Validations
|
7
23
|
include Metasploit::Model::Search::Operator::Help
|
@@ -1,4 +1,24 @@
|
|
1
|
-
#
|
1
|
+
# If all you want do is customize the name and operation `Class` that your custom operator class returns from
|
2
|
+
# `#operate_on`, then you can subclass {Metasploit::Model::Search::Operator::Single} instead of
|
3
|
+
# {Metasploit::Model::Search::Operator::Base}.
|
4
|
+
#
|
5
|
+
# class MyOperator < Metasploit::Model::Search::Operator::Single
|
6
|
+
# # Name of this operator. The name of the operator is matched to the string before the ':' in a formatted
|
7
|
+
# # operation.
|
8
|
+
# #
|
9
|
+
# # @return [Symbol]
|
10
|
+
# def name
|
11
|
+
# # ...
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # `Class.name` of `Class` returned from {Metasploit::Model::Search::Operator::Single#operate_on}.
|
15
|
+
# #
|
16
|
+
# # @return [String] a `Class.name`
|
17
|
+
# def operation_class_name
|
18
|
+
# # ...
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
2
22
|
class Metasploit::Model::Search::Operator::Single < Metasploit::Model::Search::Operator::Base
|
3
23
|
#
|
4
24
|
# CONSTANTS
|
@@ -1,4 +1,23 @@
|
|
1
|
-
#
|
1
|
+
# Once {Metasploit::Model::Search::Operator search operators} are {Metasploit::Model::Search defined}, a formatted
|
2
|
+
# query, composed of space separated formatted operation, `<operator.name>:<formatted_value>`, can be parsed to
|
3
|
+
# produce a validatable query.
|
4
|
+
#
|
5
|
+
# query = Metasploit::Model::Search::Query.new(
|
6
|
+
# formatted: 'parts.number:1 parts.number:2 parts.uuid:EX,QR'
|
7
|
+
# )
|
8
|
+
#
|
9
|
+
# Operations using the same operator are unioned together, while operations with different operator are intersected
|
10
|
+
# together, so the above formatted query can be thought of as
|
11
|
+
# `(parts.number:1 || parts.number:2) && parts.uuid:EX,QR`.
|
12
|
+
#
|
13
|
+
# # Results
|
14
|
+
#
|
15
|
+
# Once a {Metasploit::Model::Search::Query} is defined, it needs to be converted to a data store specific visitor.
|
16
|
+
#
|
17
|
+
# Visitors for `ActiveRecord` are defined in
|
18
|
+
# {http://rubydoc.info/gems/metasploit_data_models/MetasploitDataModels/Search/Visitor MetasploitDataModels::Search::Visitor}.
|
19
|
+
#
|
20
|
+
# If you want to define your own visitors, you can subclass {Metasploit::Model::Visitation::Visitor}.
|
2
21
|
class Metasploit::Model::Search::Query < Metasploit::Model::Base
|
3
22
|
#
|
4
23
|
# Attributes
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Metasploit
|
2
|
+
module Model
|
3
|
+
module Association
|
4
|
+
# Functions for turning a compact tree of compact as passed to
|
5
|
+
# {Metasploit::Model::Search::Association::ClassMethods#search_associations} into an expanded
|
6
|
+
# {Metasploit::Model::Search::Association::ClassMethods#search_association_tree}.
|
7
|
+
module Tree
|
8
|
+
# Expands a `compact` association into an expanded association tree.
|
9
|
+
#
|
10
|
+
# @param compact [Array, Hash{Symbol => Array,Hash,Symbol}, Symbol] a compact association as passed to
|
11
|
+
# {Metasploit::Model::Search::Association::ClassMethods#search_associations}.
|
12
|
+
# @return [Hash{Symbol => Hash,nil}]
|
13
|
+
def self.expand(compact)
|
14
|
+
case compact
|
15
|
+
when Array
|
16
|
+
compact.reduce({}) { |hash, association|
|
17
|
+
hash.merge(expand(association))
|
18
|
+
}
|
19
|
+
when Hash
|
20
|
+
child_by_parent = compact
|
21
|
+
|
22
|
+
child_by_parent.each_with_object({}) { |(parent, child), hash|
|
23
|
+
hash[parent] = expand(child)
|
24
|
+
}
|
25
|
+
when Symbol
|
26
|
+
association = compact
|
27
|
+
|
28
|
+
{association => nil}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @note Unlike `Hash#deep_merge`, `second_expanded`'s values aren't favored over `first`'s values. Instead whichever
|
33
|
+
# side is present is used and if both `first` and `second_expanded` are present, then their `Hash#key`s' values are
|
34
|
+
# recursively merged.
|
35
|
+
#
|
36
|
+
# Merges two expanded association trees.
|
37
|
+
#
|
38
|
+
# @param first_expanded [nil, Hash{Symbol => nil,Hash}] An expanded association tree as from {expand}
|
39
|
+
# @param second_expanded [nil, Hash{Symbol => nil,Hash}] An expanded association tree as from {expand}
|
40
|
+
# @return [nil, Hash{Symbol => nil,Hash}] a new expanded association tree.
|
41
|
+
def self.merge(first_expanded, second_expanded)
|
42
|
+
if first_expanded.nil? && second_expanded.nil?
|
43
|
+
nil
|
44
|
+
elsif !first_expanded.nil? && second_expanded.nil?
|
45
|
+
first_expanded
|
46
|
+
elsif first_expanded.nil? && !second_expanded.nil?
|
47
|
+
second_expanded
|
48
|
+
else
|
49
|
+
first_keys = first_expanded.keys
|
50
|
+
key_set = Set.new(first_keys)
|
51
|
+
|
52
|
+
second_keys = second_expanded.keys
|
53
|
+
key_set.merge(second_keys)
|
54
|
+
|
55
|
+
key_set.each_with_object({}) do |key, merged|
|
56
|
+
first_child = first_expanded[key]
|
57
|
+
second_child = second_expanded[key]
|
58
|
+
|
59
|
+
merged[key] = merge(first_child, second_child)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Calculates association operators for the `expanded` association tree.
|
65
|
+
#
|
66
|
+
# @param expanded [Hash{Symbol => Hash,nil}, nil] An expanded association tree.
|
67
|
+
# @param options [Hash{Symbol => Class}]
|
68
|
+
# @option options [Class, #reflect_on_association] :class The `Class` on which the top-level key associations in
|
69
|
+
# `expanded` are declared.
|
70
|
+
# @return [Array<Metasploit::Model::Search::Operator::Association>]
|
71
|
+
def self.operators(expanded, options={})
|
72
|
+
expanded ||= {}
|
73
|
+
|
74
|
+
options.assert_valid_keys(:class)
|
75
|
+
klass = options.fetch(:class)
|
76
|
+
|
77
|
+
expanded.flat_map { |parent_association, child_tree|
|
78
|
+
reflection = reflect_on_association_on_class(parent_association, klass)
|
79
|
+
association_class = reflection.klass
|
80
|
+
|
81
|
+
association_search_with_operators = association_class.search_with_operator_by_name.each_value
|
82
|
+
|
83
|
+
child_tree_operators = operators(
|
84
|
+
child_tree,
|
85
|
+
class: reflection.klass
|
86
|
+
)
|
87
|
+
|
88
|
+
[association_search_with_operators, child_tree_operators].flat_map { |enumerator|
|
89
|
+
enumerator.map { |source_operator|
|
90
|
+
Metasploit::Model::Search::Operator::Association.new(
|
91
|
+
association: parent_association,
|
92
|
+
klass: klass,
|
93
|
+
source_operator: source_operator
|
94
|
+
)
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Return the association reflection for `association` on `klass`.
|
103
|
+
#
|
104
|
+
# @param association [Symbol] name of an association on `klass`.
|
105
|
+
# @param klass [#reflect_on_association] `Class` on which `association` is declared.
|
106
|
+
# @return [#klass] Association reflection that can give the `#klass` pointed to by the association.
|
107
|
+
# @raise [Metasploit::Model::Association::Error] if `association` is not declared on `klass`.
|
108
|
+
# @raise [NameError] if `klass` does not respond to `reflect_on_association`.
|
109
|
+
def self.reflect_on_association_on_class(association, klass)
|
110
|
+
begin
|
111
|
+
reflection = klass.reflect_on_association(association)
|
112
|
+
rescue NameError
|
113
|
+
raise NameError,
|
114
|
+
"#{self} does not respond to reflect_on_association. " \
|
115
|
+
"It can be added to ActiveModels by including Metasploit::Model::Association into the class."
|
116
|
+
end
|
117
|
+
|
118
|
+
unless reflection
|
119
|
+
raise Metasploit::Model::Association::Error.new(
|
120
|
+
model: klass,
|
121
|
+
name: association
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
reflection
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -2,6 +2,65 @@ module Metasploit
|
|
2
2
|
module Model
|
3
3
|
# DSL to define associations and attributes that can be searched. Making an association searchable, will expose
|
4
4
|
# the attributes that association's class defined as searchable.
|
5
|
+
#
|
6
|
+
# # Operators
|
7
|
+
#
|
8
|
+
# Search operators define how to search against a given `Class`.
|
9
|
+
#
|
10
|
+
# ## Attributes
|
11
|
+
#
|
12
|
+
# Boolean, `Date`, `Integer`, and `String` attributes can be searched with
|
13
|
+
# {Metasploit::Model::Search::Attribute::ClassMethods#search_attribute search_attribute}. `Integer` and `String`
|
14
|
+
# attributes can be further restricted to a defined `Set` of values.
|
15
|
+
#
|
16
|
+
# class Part
|
17
|
+
# include Metasploit::Model::Search
|
18
|
+
#
|
19
|
+
# search_attribute :part,
|
20
|
+
# :integer
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# The above defines the `:part` operator on `Part`.
|
24
|
+
#
|
25
|
+
# ## Custom search operators
|
26
|
+
#
|
27
|
+
# If a search operator does not directly correspond to an attribute or a the attribute needs custom validation, then
|
28
|
+
# a custom {Metasploit::Model::Search::Operator operator class} can be setup as the search operator
|
29
|
+
#
|
30
|
+
# class Search::Operator::UUID
|
31
|
+
# def name
|
32
|
+
# :uuid
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# class Part
|
37
|
+
# include Metasploit::Model::Search
|
38
|
+
#
|
39
|
+
# search_with Search::Operator::UUID
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# The above defines the `:uuid` operator on `Part`.
|
43
|
+
#
|
44
|
+
# ## Associations
|
45
|
+
#
|
46
|
+
# Search operators registered with
|
47
|
+
# {Metasploit::Model::Search::Attribute::ClassMethods#search_attribute search_attribute} or
|
48
|
+
# {Metasploit::Model::Search::With::ClassMethods#search_with search_with} on an associated `Class` can be searched
|
49
|
+
# with {Metasploit::Model::Search::Association::ClassMethods#search_association}:
|
50
|
+
#
|
51
|
+
# class Widget
|
52
|
+
# include Metasploit::Model::Search
|
53
|
+
#
|
54
|
+
# # declare parts association
|
55
|
+
#
|
56
|
+
# search_association :parts
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# The above will define the `:'parts.number'` and `:'parts.uuid'` operator on `Widget`.
|
60
|
+
#
|
61
|
+
# # Queries
|
62
|
+
#
|
63
|
+
# {include:Metasploit::Model::Search::Query}
|
5
64
|
module Search
|
6
65
|
extend ActiveSupport::Concern
|
7
66
|
|
@@ -25,33 +84,8 @@ module Metasploit
|
|
25
84
|
@search_operator_by_name[operator.name] = operator
|
26
85
|
end
|
27
86
|
|
28
|
-
|
29
|
-
|
30
|
-
reflection = reflect_on_association(association)
|
31
|
-
rescue NameError
|
32
|
-
raise NameError,
|
33
|
-
"#{self} does not respond to reflect_on_association. " \
|
34
|
-
"It can be added to ActiveModels by including Metasploit::Model::Association into the class."
|
35
|
-
end
|
36
|
-
|
37
|
-
unless reflection
|
38
|
-
raise Metasploit::Model::Association::Error.new(:model => self, :name => association)
|
39
|
-
end
|
40
|
-
|
41
|
-
association_class = reflection.klass
|
42
|
-
|
43
|
-
# don't use search_operator_by_name as association operators on operators won't work
|
44
|
-
association_class.search_with_operator_by_name.each_value do |with_operator|
|
45
|
-
# non-attribute operators on association are assumed not to work
|
46
|
-
if with_operator.respond_to? :attribute
|
47
|
-
association_operator = Metasploit::Model::Search::Operator::Association.new(
|
48
|
-
:association => association,
|
49
|
-
:attribute_operator => with_operator,
|
50
|
-
:klass => self
|
51
|
-
)
|
52
|
-
@search_operator_by_name[association_operator.name] = association_operator
|
53
|
-
end
|
54
|
-
end
|
87
|
+
search_association_operators.each do |operator|
|
88
|
+
@search_operator_by_name[operator.name] = operator
|
55
89
|
end
|
56
90
|
end
|
57
91
|
|