metasploit_data_models 0.18.1-java → 0.19.0-java

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/mdm/host.rb +7 -0
  3. data/app/models/mdm/service.rb +30 -1
  4. data/app/models/mdm/tag.rb +10 -0
  5. data/app/models/metasploit_data_models/ip_address/v4/cidr.rb +14 -0
  6. data/app/models/metasploit_data_models/ip_address/v4/nmap.rb +14 -0
  7. data/app/models/metasploit_data_models/ip_address/v4/range.rb +12 -0
  8. data/app/models/metasploit_data_models/ip_address/v4/segment/nmap/list.rb +126 -0
  9. data/app/models/metasploit_data_models/ip_address/v4/segment/nmap/range.rb +12 -0
  10. data/app/models/metasploit_data_models/ip_address/v4/segment/single.rb +123 -0
  11. data/app/models/metasploit_data_models/ip_address/v4/segmented.rb +200 -0
  12. data/app/models/metasploit_data_models/ip_address/v4/single.rb +53 -0
  13. data/app/models/metasploit_data_models/search/operation/ip_address.rb +60 -0
  14. data/app/models/metasploit_data_models/search/operator/ip_address.rb +33 -0
  15. data/app/models/metasploit_data_models/search/visitor/attribute.rb +1 -0
  16. data/app/models/metasploit_data_models/search/visitor/includes.rb +1 -0
  17. data/app/models/metasploit_data_models/search/visitor/joins.rb +1 -0
  18. data/app/models/metasploit_data_models/search/visitor/where.rb +51 -0
  19. data/config/locales/en.yml +35 -4
  20. data/lib/metasploit_data_models/ip_address.rb +5 -0
  21. data/lib/metasploit_data_models/ip_address/cidr.rb +174 -0
  22. data/lib/metasploit_data_models/ip_address/range.rb +181 -0
  23. data/lib/metasploit_data_models/match/child.rb +48 -0
  24. data/lib/metasploit_data_models/match/parent.rb +103 -0
  25. data/lib/metasploit_data_models/version.rb +4 -4
  26. data/metasploit_data_models.gemspec +2 -1
  27. data/spec/app/models/mdm/cred_spec.rb +164 -31
  28. data/spec/app/models/mdm/service_spec.rb +33 -44
  29. data/spec/app/models/metasploit_data_models/ip_address/v4/cidr_spec.rb +121 -0
  30. data/spec/app/models/metasploit_data_models/ip_address/v4/nmap_spec.rb +151 -0
  31. data/spec/app/models/metasploit_data_models/ip_address/v4/range_spec.rb +300 -0
  32. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/nmap/list_spec.rb +278 -0
  33. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/nmap/range_spec.rb +304 -0
  34. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/segmented_spec.rb +29 -0
  35. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/single_spec.rb +315 -0
  36. data/spec/app/models/metasploit_data_models/ip_address/v4/single_spec.rb +183 -0
  37. data/spec/app/models/metasploit_data_models/search/operation/ip_address_spec.rb +182 -0
  38. data/spec/app/models/metasploit_data_models/search/operator/ip_address_spec.rb +19 -0
  39. data/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb +229 -36
  40. data/spec/dummy/config/application.rb +1 -1
  41. data/spec/dummy/db/structure.sql +3011 -0
  42. data/spec/factories/mdm/services.rb +3 -1
  43. data/spec/lib/metasploit_data_models/ip_address/cidr_spec.rb +350 -0
  44. data/spec/lib/metasploit_data_models/ip_address/range_spec.rb +77 -0
  45. data/spec/lib/metasploit_data_models/match/child_spec.rb +61 -0
  46. data/spec/lib/metasploit_data_models/match/parent_spec.rb +155 -0
  47. data/spec/support/matchers/match_regex_exactly.rb +28 -0
  48. data/spec/support/shared/contexts/rex/text.rb +15 -0
  49. data/spec/support/shared/examples/metasploit_data_models/search/operation/ipaddress/match.rb +109 -0
  50. metadata +58 -9
  51. data/spec/dummy/db/schema.rb +0 -609
@@ -0,0 +1,53 @@
1
+ # A single IPv4 address, in standard, dotted decimal notation.
2
+ #
3
+ # @example Dotted Decimal Notation
4
+ # '1.2.3.4'
5
+ #
6
+ class MetasploitDataModels::IPAddress::V4::Single < MetasploitDataModels::IPAddress::V4::Segmented
7
+ #
8
+ # Segments
9
+ #
10
+
11
+ segment class_name: 'MetasploitDataModels::IPAddress::V4::Segment::Single'
12
+
13
+ #
14
+ # Instance Methods
15
+ #
16
+
17
+ # Adds `other` IPv4 address to this IPv4 address.
18
+ #
19
+ # @return [MetasploitDataModels::IPAddress::V4::Single] a new IPv4 address contain the sum of the two addresses
20
+ # segments with carries from lower to higher segments.
21
+ # @raise [TypeError] if `other` isn't the same class.
22
+ # @raise [ArgmentError] if `self` plus `other` yields an IP address greater than 255.255.255.255.
23
+ # @see succ
24
+ def +(other)
25
+ unless other.is_a? self.class
26
+ raise TypeError, "Cannot add #{other.class} to #{self.class}"
27
+ end
28
+
29
+ carry = 0
30
+ sum_segments = []
31
+ low_to_high_segments = segments.zip(other.segments).reverse
32
+
33
+ low_to_high_segments.each do |self_segment, other_segment|
34
+ segment, carry = self_segment.add_with_carry(other_segment, carry)
35
+ sum_segments.unshift segment
36
+ end
37
+
38
+ unless carry == 0
39
+ raise ArgumentError,
40
+ "#{self} + #{other} is not a valid IP address. It is #{sum_segments.join('.')} with a carry (#{carry})"
41
+ end
42
+
43
+ self.class.new(segments: sum_segments)
44
+ end
45
+
46
+ # The succeeding IPv4 address.
47
+ #
48
+ # @see #+
49
+ # @raise (see #+)
50
+ def succ
51
+ self + self.class.new(value: '0.0.0.1')
52
+ end
53
+ end
@@ -0,0 +1,60 @@
1
+ # Searches an `inet` column in a PostgreSQL database using
2
+ # {MetasploitDataModels::IPAddress::V4::Single a standard IPv4 address},
3
+ # {MetasploitDataModels::IPAddress::V4::CIDR an IPv4 CIDR block}, or
4
+ # {MetasploitDataModels::IPAddress::V4::Range an IPv4 address range}.
5
+ class MetasploitDataModels::Search::Operation::IPAddress < Metasploit::Model::Search::Operation::Base
6
+ include MetasploitDataModels::Match::Parent
7
+
8
+ #
9
+ # Match Children
10
+ #
11
+
12
+ # in order of precedence, so simpler single IPv4 addresses are matched before the more complex ranges which may
13
+ # degenerate to equivalent formatted value
14
+ match_children_named %w{
15
+ MetasploitDataModels::IPAddress::V4::Single
16
+ MetasploitDataModels::IPAddress::V4::CIDR
17
+ MetasploitDataModels::IPAddress::V4::Range
18
+ }
19
+
20
+ #
21
+ #
22
+ # Validations
23
+ #
24
+ #
25
+
26
+ #
27
+ # Validation Methods
28
+ #
29
+
30
+ validate :value_valid
31
+
32
+ #
33
+ # Attribute Validations
34
+ #
35
+
36
+ validates :value,
37
+ presence: true
38
+
39
+ #
40
+ # Instance Method
41
+ #
42
+
43
+ # @param formatted_value [#to_s]
44
+ def value=(formatted_value)
45
+ @value = match_child(formatted_value) || formatted_value
46
+ end
47
+
48
+ private
49
+
50
+ # Validates that `#value` is valid.
51
+ #
52
+ # @return [void]
53
+ def value_valid
54
+ if value.present?
55
+ unless value.respond_to?(:valid?) && value.valid?
56
+ errors.add(:value, :invalid)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,33 @@
1
+ # Operator for `inet` columns in a PostgreSQL database, which operates on formatted values using
2
+ # {MetasploitDataModels::Search::Operation::IPAddress}.
3
+ class MetasploitDataModels::Search::Operator::IPAddress < Metasploit::Model::Search::Operator::Single
4
+ #
5
+ # Attributes
6
+ #
7
+
8
+ # @!attribute [r] attribute
9
+ # The attribute on `Metasploit::Model::Search::Operator::Base#klass` that is searchable.
10
+ #
11
+ # @return [Symbol] the attribute name
12
+ attr_accessor :attribute
13
+
14
+ #
15
+ # Validations
16
+ #
17
+
18
+ validates :attribute,
19
+ presence: true
20
+
21
+ #
22
+ # Instance Methods
23
+ #
24
+
25
+ alias_method :name, :attribute
26
+
27
+ # The class used for `Metasploit::Model::Search::Operator::Single#operate_on`.
28
+ #
29
+ # @return [String] `'MetasploitDataModels::Search::Operation::IPAddress'`
30
+ def operation_class_name
31
+ @operation_class_name ||= 'MetasploitDataModels::Search::Operation::IPAddress'
32
+ end
33
+ end
@@ -7,6 +7,7 @@ class MetasploitDataModels::Search::Visitor::Attribute
7
7
  end
8
8
 
9
9
  visit 'Metasploit::Model::Search::Operator::Attribute',
10
+ 'MetasploitDataModels::Search::Operator::IPAddress',
10
11
  'MetasploitDataModels::Search::Operator::Port::List' do |operator|
11
12
  table = operator.klass.arel_table
12
13
  table[operator.attribute]
@@ -23,6 +23,7 @@ class MetasploitDataModels::Search::Visitor::Includes
23
23
  end
24
24
 
25
25
  visit 'Metasploit::Model::Search::Operator::Attribute',
26
+ 'MetasploitDataModels::Search::Operator::IPAddress',
26
27
  'MetasploitDataModels::Search::Operator::Port::List' do |_operator|
27
28
  []
28
29
  end
@@ -43,6 +43,7 @@ class MetasploitDataModels::Search::Visitor::Joins
43
43
  end
44
44
 
45
45
  visit 'Metasploit::Model::Search::Operator::Attribute',
46
+ 'MetasploitDataModels::Search::Operator::IPAddress',
46
47
  'MetasploitDataModels::Search::Operator::Port::List' do |_|
47
48
  []
48
49
  end
@@ -45,6 +45,45 @@ class MetasploitDataModels::Search::Visitor::Where
45
45
  attribute.matches(match_value)
46
46
  end
47
47
 
48
+ visit 'MetasploitDataModels::IPAddress::CIDR' do |cidr|
49
+ cast_to_inet "#{cidr.address}/#{cidr.prefix_length}"
50
+ end
51
+
52
+ visit 'MetasploitDataModels::IPAddress::Range' do |ip_address_range|
53
+ range = ip_address_range.value
54
+
55
+ begin_node = visit range.begin
56
+ end_node = visit range.end
57
+
58
+ # AND nodes should be created with a list
59
+ Arel::Nodes::And.new([begin_node, end_node])
60
+ end
61
+
62
+ visit 'MetasploitDataModels::IPAddress::V4::Single' do |ip_address|
63
+ cast_to_inet(ip_address.to_s)
64
+ end
65
+
66
+ visit 'MetasploitDataModels::Search::Operation::IPAddress' do |operation|
67
+ attribute = attribute_visitor.visit operation.operator
68
+ value = operation.value
69
+ value_node = visit value
70
+
71
+ case value
72
+ when MetasploitDataModels::IPAddress::CIDR
73
+ Arel::Nodes::InfixOperation.new(
74
+ '<<',
75
+ attribute,
76
+ value_node
77
+ )
78
+ when MetasploitDataModels::IPAddress::Range
79
+ Arel::Nodes::Between.new(attribute, value_node)
80
+ when MetasploitDataModels::IPAddress::V4::Single
81
+ Arel::Nodes::Equality.new(attribute, value_node)
82
+ else
83
+ raise TypeError, "Don't know how to handle #{value.class}"
84
+ end
85
+ end
86
+
48
87
  visit 'MetasploitDataModels::Search::Operation::Port::Range' do |range_operation|
49
88
  attribute = attribute_visitor.visit range_operation.operator
50
89
 
@@ -69,5 +108,17 @@ class MetasploitDataModels::Search::Visitor::Where
69
108
  @method_visitor ||= MetasploitDataModels::Search::Visitor::Method.new
70
109
  end
71
110
 
111
+ private
112
+
113
+ # Casts a literal string to INET in AREL.
114
+ #
115
+ # @return [Arel::Nodes::NamedFunction]
116
+ def cast_to_inet(string)
117
+ cast_argument = Arel::Nodes::As.new(string, Arel::Nodes::SqlLiteral.new('INET'))
118
+ Arel::Nodes::NamedFunction.new('CAST', [cast_argument])
119
+ end
120
+
121
+ public
122
+
72
123
  Metasploit::Concern.run(self)
73
124
  end
@@ -5,12 +5,39 @@ en:
5
5
  messages:
6
6
  # have to duplicate activerecord.model.errors.message.taken because of the different i18n_scope
7
7
  taken: "has already been taken"
8
-
9
8
  models:
10
- metasploit_data_models/search/operator/multitext:
9
+ metasploit_data_models/ip_address/range:
11
10
  attributes:
12
- operator_names:
13
- too_short: "is too short (minimum is %{count} operator names)"
11
+ value:
12
+ order: "beginning of range (%{begin}) cannot be after end of range (%{end})"
13
+ metasploit_data_models/ip_address/v4/segmented:
14
+ attributes:
15
+ segments:
16
+ segment_invalid: "has invalid segment (%{segment}) at index %{index}"
17
+ wrong_length: "is the wrong length (should be %{count} segments)"
18
+ metasploit_data_models/ip_address/v4/segment/nmap/list:
19
+ attributes:
20
+ value:
21
+ array: "is not an Array"
22
+ element: "has invalid element (%{element}) at index %{index}"
23
+ metasploit_data_models/ip_address/v4/segment/nmap/range:
24
+ attributes:
25
+ value:
26
+ order: "beginning of range (%{begin}) cannot be after end of range (%{end})"
27
+ metasploit_data_models/search/operation/ip_address/invalid_range:
28
+ attributes:
29
+ value:
30
+ format: "does not match any known formats (IPv4, IPv4 CIDR, IPv4 NMAP, IPv4 Range, IPv6, IPv6 CIDR, IPv6 Range)"
31
+ metasploit_data_models/search/operation/ip_address/v4/range:
32
+ attributes:
33
+ value:
34
+ extreme: "%{extreme} (%{extreme_value}) is not an IPAddr"
35
+ order: "beginning of range (%{begin}) cannot be after end of range (%{end})"
36
+ type: "is not a range"
37
+ metasploit_data_models/search/operation/ip_address/v4/single:
38
+ attributes:
39
+ value:
40
+ format: "does not match IPv4 dotted decimal format"
14
41
  metasploit_data_models/search/operation/port/range:
15
42
  attributes:
16
43
  value:
@@ -21,3 +48,7 @@ en:
21
48
  value:
22
49
  order: "is not in order: begin (%{begin} is greater than end (%{end})."
23
50
  range: "is not a range"
51
+ metasploit_data_models/search/operator/multitext:
52
+ attributes:
53
+ operator_names:
54
+ too_short: "is too short (minimum is %{count} operator names)"
@@ -0,0 +1,5 @@
1
+ # Namespace for models for validating various IPv4 formats beyond those supported by the Ruby standard library's
2
+ # `IPAddr`.
3
+ module MetasploitDataModels::IPAddress
4
+
5
+ end
@@ -0,0 +1,174 @@
1
+ # Common behavior for Class-InterDomain Routing (`<address>/<prefix-length>`) notation under
2
+ # {MetasploitDataModels::IPAddress},
3
+ module MetasploitDataModels::IPAddress::CIDR
4
+ # so that translations for errors messages can be filed under metasploit_data_models/ip_address/cidr
5
+ extend ActiveModel::Naming
6
+ extend ActiveSupport::Concern
7
+
8
+ #
9
+ # CONSTANTS
10
+ #
11
+
12
+ # Separator between the {#address} and {#prefix_length}
13
+ SEPARATOR = '/'
14
+
15
+ #
16
+ # Attributes
17
+ #
18
+
19
+ # @!attribute address
20
+ # The IP address being masked by {#prefix_length} `1` bits.
21
+ #
22
+ # @return [Object] an instance of {address_class}
23
+ attr_reader :address
24
+
25
+ # @!attribute prefix_length
26
+ # The significant number of bits in {#address}.
27
+ #
28
+ # @return [Integer] number of `1` bits in the netmask of {#address}
29
+ attr_reader :prefix_length
30
+
31
+ included do
32
+ include ActiveModel::Validations
33
+
34
+ #
35
+ #
36
+ # Validations
37
+ #
38
+ #
39
+
40
+ #
41
+ # Validation Methods
42
+ #
43
+
44
+ validate :address_valid
45
+
46
+ #
47
+ # Attribute Validations
48
+ #
49
+
50
+ validates :address,
51
+ presence: true
52
+ end
53
+
54
+ # Class methods added to the including `Class`.
55
+ module ClassMethods
56
+ include MetasploitDataModels::Match::Child
57
+
58
+ #
59
+ # Attributes
60
+ #
61
+
62
+ # @!attribute address_class
63
+ # The Class` whose instance are usd for {MetasploitDataModels::IPAddress::CIDR#address}.
64
+ #
65
+ # @return [Class]
66
+ attr_reader :address_class
67
+
68
+ #
69
+ # Methods
70
+ #
71
+
72
+ # @note `address_class` must respond to `#segment_class` and `#segment_count` so {#maximum_prefix_length} can be
73
+ # calculated.
74
+ #
75
+ # Sets up the address class and allowed {#maximum_prefix_length} for the including `Class`.
76
+ #
77
+ # @param options [Hash{Symbol => Class}]
78
+ # @option options [Class, #segment_class, #segment_count] :address_class The `Class` whose instances will be used
79
+ # for {#address}.
80
+ def cidr(options={})
81
+ options.assert_valid_keys(:address_class)
82
+
83
+ @address_class = options.fetch(:address_class)
84
+
85
+ #
86
+ # Validations
87
+ #
88
+
89
+ validates :prefix_length,
90
+ numericality: {
91
+ only_integer: true,
92
+ greater_than_or_equal_to: 0,
93
+ less_than_or_equal_to: maximum_prefix_length
94
+ }
95
+ end
96
+
97
+ # Regular expression that matches a string that contains only a CIDR IP address.
98
+ #
99
+ # @return [Regexp]
100
+ def match_regexp
101
+ @match_regexp ||= /\A#{regexp}\z/
102
+ end
103
+
104
+ # The maximum number of bits in a prefix for the {#address_class}.
105
+ #
106
+ # @return [Integer] the number of bits across all segments of {#address_class}.
107
+ def maximum_prefix_length
108
+ @maximum_prefix_length ||= address_class.segment_count * address_class.segment_class.bits
109
+ end
110
+
111
+ # Regular expression that matches a portion of string that contains a CIDR IP address.
112
+ #
113
+ # @return [Regexp]
114
+ def regexp
115
+ @regexp ||= /(?<address>#{address_class.regexp})#{Regexp.escape(SEPARATOR)}(?<prefix_length>\d+)/
116
+ end
117
+ end
118
+
119
+ #
120
+ # Instance Methods
121
+ #
122
+
123
+ # Set {#address}.
124
+ #
125
+ # @param formatted_address [#to_s]
126
+ def address=(formatted_address)
127
+ @address = self.class.address_class.new(value: formatted_address)
128
+ end
129
+
130
+ # Set {#prefix_length}.
131
+ #
132
+ # @param formatted_prefix_length [#to_s]
133
+ def prefix_length=(formatted_prefix_length)
134
+ @prefix_length_before_type_cast = formatted_prefix_length
135
+
136
+ begin
137
+ # use Integer() instead of String#to_i as String#to_i will ignore trailing letters (i.e. '1two' -> 1) and turn all
138
+ # string without an integer in it to 0.
139
+ @prefix_length = Integer(formatted_prefix_length.to_s)
140
+ rescue ArgumentError
141
+ @prefix_length = formatted_prefix_length
142
+ end
143
+ end
144
+
145
+ # The formatted_prefix_length passed to {#prefix_length=}
146
+ #
147
+ # @return [#to_s]
148
+ def prefix_length_before_type_cast
149
+ @prefix_length_before_type_cast
150
+ end
151
+
152
+ # Parses the `formatted_value` into an {#address} and {#prefix_length}.
153
+ #
154
+ # @param formatted_value [#to_s]
155
+ def value=(formatted_value)
156
+ formatted_address, formatted_prefix_length = formatted_value.to_s.split(SEPARATOR, 2)
157
+
158
+ self.address = formatted_address
159
+ self.prefix_length = formatted_prefix_length
160
+
161
+ [address, prefix_length]
162
+ end
163
+
164
+ private
165
+
166
+ # Validates that {#address} is valid.
167
+ #
168
+ # @return [void]
169
+ def address_valid
170
+ if address && !address.valid?
171
+ errors.add(:address, :invalid)
172
+ end
173
+ end
174
+ end