metasploit-model 2.0.3 → 3.1.2

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 (38) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -1
  4. data/.travis.yml +8 -3
  5. data/.yardopts +2 -1
  6. data/Gemfile +2 -2
  7. data/app/validators/address_format_validator.rb +26 -0
  8. data/app/validators/ip_format_validator.rb +13 -19
  9. data/app/validators/nil_validator.rb +3 -3
  10. data/app/validators/parameters_validator.rb +18 -10
  11. data/app/validators/password_is_strong_validator.rb +93 -67
  12. data/lib/metasploit/model.rb +1 -0
  13. data/lib/metasploit/model/base.rb +2 -2
  14. data/lib/metasploit/model/engine.rb +2 -2
  15. data/lib/metasploit/model/search/attribute.rb +1 -1
  16. data/lib/metasploit/model/translation.rb +2 -2
  17. data/lib/metasploit/model/version.rb +1 -1
  18. data/metasploit-model.gemspec +9 -9
  19. data/spec/app/models/metasploit/model/association/reflection_spec.rb +2 -2
  20. data/spec/app/models/metasploit/model/search/operator/association_spec.rb +1 -1
  21. data/spec/app/models/metasploit/model/search/operator/attribute_spec.rb +1 -1
  22. data/spec/app/validators/address_format_validator_spec.rb +136 -0
  23. data/spec/app/validators/ip_format_validator_spec.rb +0 -24
  24. data/spec/app/validators/password_is_strong_validator_spec.rb +31 -42
  25. data/spec/dummy/app/models/application_record.rb +3 -0
  26. data/spec/dummy/config/application.rb +1 -5
  27. data/spec/dummy/config/environments/development.rb +0 -10
  28. data/spec/dummy/db/schema.rb +0 -1
  29. data/spec/factories/metasploit/model/association/reflections.rb +1 -1
  30. data/spec/factories/metasploit/model/bases.rb +1 -1
  31. data/spec/factories/metasploit/model/search/operator/associations.rb +1 -1
  32. data/spec/factories/metasploit/model/search/operator/attributes.rb +1 -1
  33. data/spec/factories/metasploit/model/search/operator/bases.rb +1 -1
  34. data/spec/lib/metasploit/model/engine_spec.rb +3 -3
  35. data/spec/lib/metasploit/model/search/attribute_spec.rb +2 -2
  36. metadata +93 -100
  37. metadata.gz.sig +0 -0
  38. data/spec/dummy/config/initializers/assets.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c1de94b0a91746b28e1d2b529bdb1c324ed20e83
4
- data.tar.gz: a05f22f06690c932e565f850b39f1b54341f13c8
2
+ SHA256:
3
+ metadata.gz: 7282653b38429b72974d4f1c6a98c7023aeb85189aa1b929fcc1e1f0751b47b5
4
+ data.tar.gz: 07e615389202c882a355b8ac7f8776d45b99e846512d64b8355a3ae8f1e2e9f8
5
5
  SHA512:
6
- metadata.gz: 91b6baab46178b333f5b5c1d6980472eb3c8240e76e47bad3b5f03dec7fa93afb7441c2623e4f1fc200e305bf97b19bf6aa40e5e23dcd4c2bc4ddd68d17e1ff5
7
- data.tar.gz: cf99373af42856a40d36de1dbc67dfa23fcb316b0de051a0d89691c573b2462c2b0adcb6f2295600f62861db4b86fc2ca2ab21df87f685e0e72c674dce09dbad
6
+ metadata.gz: 1e6634c258bc4ae1d49200b5786e4962e5ceea839311d1cea2e66dd76a9e5eb96f05800122c74be18fd52b291c7ed23ade8e7637b6cff2890046298bc92f9b96
7
+ data.tar.gz: ac18802b7f7a78705f88fbff920c162da5b718394abc0d5e53a167f3d0bfb7e64fb59ddf6594ea39b25ce865ab2b75f26906001e923a8ea6c27c252ad2a3c2c0
Binary file
data.tar.gz.sig CHANGED
@@ -1 +1,2 @@
1
- � v_�#�R1{���� TAe8,��P�>Iӝ?���EXp#Oh!��V����"����^�Ȳ���vPrqU
1
+ �?���}M=����g��1"V��)A�X�yIQb�� T����,<��
2
+ �^y��.
@@ -1,16 +1,21 @@
1
+ dist: trusty
1
2
  group: stable
2
3
  sudo: false
3
4
  cache: bundler
4
5
  language: ruby
5
6
  addons:
6
- postgresql: '9.3'
7
+ postgresql: '9.6'
7
8
  apt:
8
9
  packages:
9
10
  - graphviz
10
11
  rvm:
11
- - 2.3.2
12
+ - 2.6.5
12
13
  before_install:
13
- - gem update bundler
14
+ - gem install bundler
15
+ - cp spec/dummy/config/database.yml.travis spec/dummy/config/database.yml
16
+ - bundle install
17
+ - bundle exec rake --version
18
+ - bundle exec rake db:test:prepare
14
19
  script:
15
20
  - bundle exec rake spec
16
21
  - bundle exec rake yard
data/.yardopts CHANGED
@@ -1,5 +1,6 @@
1
1
  --markup markdown
2
+ --plugin yard-metasploit-erd
2
3
  --private
3
4
  --protected
4
5
  {app,lib}/**/*.rb
5
- db/migrate/*.rb
6
+ db/migrate/*.rb
data/Gemfile CHANGED
@@ -7,9 +7,9 @@ gemspec
7
7
  group :development, :test do
8
8
  # supplies factories for producing model instance for specs
9
9
  # Version 4.1.0 or newer is needed to support generate calls without the 'FactoryGirl.' in factory definitions syntax.
10
- gem 'factory_girl'
10
+ gem 'factory_bot'
11
11
  # auto-load factories from spec/factories
12
- gem 'factory_girl_rails'
12
+ gem 'factory_bot_rails'
13
13
  end
14
14
 
15
15
  group :test do
@@ -0,0 +1,26 @@
1
+ require 'ipaddr'
2
+
3
+ # Validates that attribute is a valid address.
4
+ class AddressFormatValidator < ActiveModel::EachValidator
5
+ # Validates that `attribute`'s `value` on `object` is a valid address.
6
+ #
7
+ # @param record [#errors, ApplicationRecord] ActiveModel or ActiveRecord
8
+ # @param attribute [Symbol] name of address attribute.
9
+ # @param value [String, nil] address.
10
+ # @return [void]
11
+ def validate_each(object, attribute, value)
12
+ error_message_block = lambda{ object.errors.add attribute, "must be a valid (IP or hostname) address" }
13
+ begin
14
+ # Checks for valid IP addresses
15
+ if value.is_a? IPAddr
16
+ potential_ip = value.dup
17
+ else
18
+ potential_ip = IPAddr.new(value)
19
+ end
20
+ error_message_block.call unless potential_ip.ipv4? || potential_ip.ipv6?
21
+ rescue IPAddr::InvalidAddressError, IPAddr::AddressFamilyError, ArgumentError
22
+ # IP address resolution failed, checks for valid hostname
23
+ error_message_block.call unless (value && value.match?(/\A#{URI::PATTERN::HOSTNAME}\z/))
24
+ end
25
+ end
26
+ end
@@ -1,31 +1,25 @@
1
1
  require 'ipaddr'
2
2
 
3
- # Validates that value is an IPv4 or IPv6 address.
3
+ # Validates that value is a valid IPv4 or IPv6 address.
4
4
  class IpFormatValidator < ActiveModel::EachValidator
5
- # Validates that `value` is an IPv4 or IPv4 address. Ranges in CIDR or netmask notation are not allowed.
5
+ # Validates that `attribute`'s `value` on `object` is a valid IPv4 or IPv6 address.
6
6
  #
7
- # @param record [#errors, ActiveRecord::Base] ActiveModel or ActiveRecord
7
+ # @param record [#errors, ApplicationRecord] ActiveModel or ActiveRecord
8
8
  # @param attribute [Symbol] name of IP address attribute.
9
9
  # @param value [String, nil] IP address.
10
10
  # @return [void]
11
- # @see IPAddr#ipv4?
12
- # @see IPAddr#ipv6?
13
- def validate_each(record, attribute, value)
11
+ def validate_each(object, attribute, value)
12
+ error_message_block = lambda{ object.errors.add attribute, "must be a valid IPv4 or IPv6 address" }
14
13
  begin
15
- potential_ip = IPAddr.new(value)
16
- rescue ArgumentError
17
- record.errors[attribute] << 'must be a valid IPv4 or IPv6 address'
18
- else
19
- # if it includes a netmask, then it's not an IP address, but an IP range.
20
- if potential_ip.ipv4?
21
- if potential_ip.instance_variable_get(:@mask_addr) != IPAddr::IN4MASK
22
- record.errors[attribute] << 'must be a valid IPv4 or IPv6 address and not an IPv4 address range in CIDR or netmask notation'
23
- end
24
- elsif potential_ip.ipv6?
25
- if potential_ip.instance_variable_get(:@mask_addr) != IPAddr::IN6MASK
26
- record.errors[attribute] << 'must be a valid IPv4 or IPv6 address and not an IPv6 address range in CIDR or netmask notation'
27
- end
14
+ if value.is_a? IPAddr
15
+ potential_ip = value.dup
16
+ else
17
+ potential_ip = IPAddr.new(value)
28
18
  end
19
+
20
+ error_message_block.call unless potential_ip.ipv4? || potential_ip.ipv6?
21
+ rescue ArgumentError
22
+ error_message_block.call
29
23
  end
30
24
  end
31
25
  end
@@ -4,13 +4,13 @@
4
4
  class NilValidator < ActiveModel::EachValidator
5
5
  # Validates that `value` is `nil`.
6
6
  #
7
- # @param record [#errors, ActiveRecord::Base] an ActiveModel or ActiveRecord
7
+ # @param record [#errors, ApplicationRecord] an ActiveModel or ActiveRecord
8
8
  # @param attribute [Symbol] name of attribute being validated.
9
9
  # @param value [#nil?] value of `attribute` to check with `nil?`
10
10
  # @return [void]
11
11
  def validate_each(record, attribute, value)
12
12
  unless value.nil?
13
- record.errors[attribute] << 'must be nil'
13
+ record.errors.add attribute, 'must be nil'
14
14
  end
15
15
  end
16
- end
16
+ end
@@ -1,13 +1,21 @@
1
1
  # Validates that attribute's value is Array<Array(String, String)> which is the only valid type signature for serialized
2
2
  # parameters.
3
3
  class ParametersValidator < ActiveModel::EachValidator
4
+ #
5
+ # CONSTANTS
6
+ #
7
+
4
8
  # Sentence explaining the valid type signature for parameters.
5
9
  TYPE_SIGNATURE_SENTENCE = 'Valid parameters are an Array<Array(String, String)>.'
6
10
 
7
- # Validates that attribute's value is Array<Array(String, String)> which is the only valid type signature for
8
- # serialized parameters. Errors are specific to the how different `value` is compared to correct format.
9
11
  #
10
- # @param record [#errors, ActiveRecord::Base] ActiveModel or ActiveRecord
12
+ # Instance Methods
13
+ #
14
+
15
+ # Validates that `attribute`'s `value` on `record` is `Array<Array(String, String)>` which is the only valid type
16
+ # signature for serialized parameters.
17
+ #
18
+ # @param record [#errors, ApplicationRecord] ActiveModel or ActiveRecord
11
19
  # @param attribute [Symbol] serialized parameters attribute name.
12
20
  # @param value [Object, nil, Array, Array<Array>, Array<Array(String, String)>] serialized parameters.
13
21
  # @return [void]
@@ -28,7 +36,7 @@ class ParametersValidator < ActiveModel::EachValidator
28
36
  :index => index
29
37
  )
30
38
 
31
- record.errors[attribute] << length_error
39
+ record.errors.add attribute, length_error
32
40
  else
33
41
  parameter_name = element.first
34
42
 
@@ -39,7 +47,7 @@ class ParametersValidator < ActiveModel::EachValidator
39
47
  :index => index,
40
48
  :prefix => "has blank parameter name"
41
49
  )
42
- record.errors[attribute] << error
50
+ record.errors.add attribute, error
43
51
  end
44
52
  else
45
53
  error = error_at(
@@ -47,7 +55,7 @@ class ParametersValidator < ActiveModel::EachValidator
47
55
  :index => index,
48
56
  :prefix => "has non-String parameter name (#{parameter_name.inspect})"
49
57
  )
50
- record.errors[attribute] << error
58
+ record.errors.add attribute, error
51
59
  end
52
60
 
53
61
  parameter_value = element.second
@@ -58,7 +66,7 @@ class ParametersValidator < ActiveModel::EachValidator
58
66
  :index => index,
59
67
  :prefix => "has non-String parameter value (#{parameter_value.inspect})"
60
68
  )
61
- record.errors[attribute] << error
69
+ record.errors.add attribute, error
62
70
  end
63
71
  end
64
72
  else
@@ -67,11 +75,11 @@ class ParametersValidator < ActiveModel::EachValidator
67
75
  :index => index,
68
76
  :prefix => 'has non-Array'
69
77
  )
70
- record.errors[attribute] << error
78
+ record.errors.add attribute, error
71
79
  end
72
80
  end
73
81
  else
74
- record.errors[attribute] << "is not an Array. #{TYPE_SIGNATURE_SENTENCE}"
82
+ record.errors.add attribute, "is not an Array. #{TYPE_SIGNATURE_SENTENCE}"
75
83
  end
76
84
  end
77
85
 
@@ -144,4 +152,4 @@ class ParametersValidator < ActiveModel::EachValidator
144
152
 
145
153
  clause
146
154
  end
147
- end
155
+ end
@@ -4,22 +4,20 @@ class PasswordIsStrongValidator < ActiveModel::EachValidator
4
4
  # CONSTANTS
5
5
  #
6
6
 
7
- # Password that are used too often and will be easily guessed.
7
+ # Known passwords that should NOT be allowed and should be considered weak.
8
8
  COMMON_PASSWORDS = %w{
9
- password pass root admin metasploit
10
- msf 123456 qwerty abc123 letmein monkey link182 demo
11
- changeme test1234 rapid7
12
- }
13
-
14
- # Validates that `value` is a strong password. A password is strong if it meets the following rules:
15
- # * SHOULD contain at least one letter.
16
- # * SHOULD contain at least one digit.
17
- # * SHOULD contain at least one special character.
18
- # * SHOULD NOT contain `record.username` (case-insensitive).
19
- # * SHOULD NOT be in {COMMON_PASSWORDS}.
20
- # * SHOULD NOT repetitions.
9
+ password pass root admin metasploit
10
+ msf 123456 qwerty abc123 letmein monkey link182 demo
11
+ changeme test1234 rapid7
12
+ }
13
+
14
+ # Special characters that are considered to strength passwords and are required once in a strong password.
15
+ SPECIAL_CHARS = %q{!@"#$%&'()*+,-./:;<=>?[\\]^_`{|}~ }
16
+
17
+ # Validates that the `attribute`'s `value` on `record` contains letters, numbers, and at least one special character
18
+ # without containing the `record.username`, any {COMMON_PASSWORDS} or repetition.
21
19
  #
22
- # @param record [#errors, #username, ActiveRecord::Base] ActiveModel or ActiveRecord that supports #username method.
20
+ # @param record [#errors, #username, ApplicationRecord] ActiveModel or ActiveRecord that supports #username method.
23
21
  # @param attribute [Symbol] password attribute name.
24
22
  # @param value [String] a password.
25
23
  # @return [void]
@@ -27,30 +25,101 @@ class PasswordIsStrongValidator < ActiveModel::EachValidator
27
25
  return if value.blank?
28
26
 
29
27
  if is_simple?(value)
30
- record.errors[attribute] << 'must contain letters, numbers, and at least one special character'
28
+ record.errors.add attribute, 'must contain letters, numbers, and at least one special character'
31
29
  end
32
30
 
33
- if contains_username?(record.username, value)
34
- record.errors[attribute] << 'must not contain the username'
31
+ if !record.username.blank? && contains_username?(record.username, value)
32
+ record.errors.add attribute, 'must not contain the username'
35
33
  end
36
34
 
37
35
  if is_common_password?(value)
38
- record.errors[attribute] << 'must not be a common password'
36
+ record.errors.add attribute, 'must not be a common password'
39
37
  end
40
38
 
41
- if contains_repetition?(value)
42
- record.errors[attribute] << 'must not be a predictable sequence of characters'
39
+ if is_only_repetition?(value)
40
+ record.errors.add attribute, 'must not be a predictable sequence of characters'
43
41
  end
44
42
  end
45
43
 
46
44
  private
47
45
 
48
- # Password repetition (quite basic) -- no "aaaaaa" or "ababab" or "abcabc" or
49
- # "abcdabcd" (but note that the user can use "aaaaaab" or something).
46
+ # Returns whether the password is simple.
47
+ #
48
+ # @return [false] if password contains a letter, digit and special character.
49
+ # @return [true] otherwise
50
+ def is_simple?(password)
51
+ not (password =~ /[A-Za-z]/ and password =~ /[0-9]/ and password =~ /[#{Regexp.escape(SPECIAL_CHARS)}]/)
52
+ end
53
+
54
+ # Returns whether username is in password (case-insensitively).
55
+ #
56
+ # @return [true] if `username` is in `password`.
57
+ # @return [false] unless `username` is in `password`.
58
+ def contains_username?(username, password)
59
+ !!(password =~ /#{username}/i)
60
+ end
61
+
62
+ # Returns whether `password` is in {COMMON_PASSWORDS} or a simple variation of a password in {COMMON_PASSWORDS}.
63
+ #
64
+ # @param password [String]
65
+ # @return [Boolean]
66
+ def is_common_password?(password)
67
+ COMMON_PASSWORDS.each do |pw|
68
+ common_pw = [pw] # pw + "!", pw + "1", pw + "12", pw + "123", pw + "1234"]
69
+ common_pw += mutate_pass(pw)
70
+ common_pw.each do |common_pass|
71
+ if password.downcase =~ /#{common_pass}[\d!]*/
72
+ return true
73
+ end
74
+ end
75
+ end
76
+ false
77
+ end
78
+
79
+ # Returns a leet mutated variant of the original password
80
+ #
81
+ # @param password [String]
82
+ # @return [String] containing the password with leet mutations
83
+ def mutate_pass(password)
84
+ mutations = {
85
+ 'a' => '@',
86
+ 'o' => '0',
87
+ 'e' => '3',
88
+ 's' => '$',
89
+ 't' => '7',
90
+ 'l' => '1'
91
+ }
92
+
93
+ iterations = mutations.keys.dup
94
+ results = []
95
+
96
+ # Find PowerSet of all possible mutation combinations
97
+ iterations = iterations.inject([[]]){|c,y|r=[];c.each{|i|r<<i;r<<i+[y]};r}
98
+
99
+ # Iterate through combinations to create each possible mutation
100
+ iterations.each do |iteration|
101
+ next if iteration.flatten.empty?
102
+ first = iteration.shift
103
+ intermediate = password.gsub(/#{first}/i, mutations[first])
104
+ iteration.each do |mutator|
105
+ next unless mutator.kind_of? String
106
+ intermediate.gsub!(/#{mutator}/i, mutations[mutator])
107
+ end
108
+ results << intermediate
109
+ end
110
+
111
+ return results
112
+ end
113
+
114
+
115
+ # Returns whether `password` is only composed of repetitions
50
116
  #
51
117
  # @param password [String]
52
118
  # @return [Boolean]
53
- def contains_repetition?(password)
119
+ def is_only_repetition?(password)
120
+ # Password repetition (quite basic) -- no "aaaaaa" or "ababab" or "abcabc" or
121
+ # "abcdabcd" (but note that the user can use "aaaaaab" or something).
122
+
54
123
  if password.scan(/./).uniq.size < 2
55
124
  return true
56
125
  end
@@ -69,47 +138,4 @@ class PasswordIsStrongValidator < ActiveModel::EachValidator
69
138
 
70
139
  false
71
140
  end
72
-
73
- # Whether username is in password (case-insensitively).
74
- #
75
- # @return [false] if `password` is blank.
76
- # @return [false] if `username` is blank.
77
- # @return [false] unless `username` is in `password`.
78
- # @return [true] if `username` is in `password`.
79
- def contains_username?(username, password)
80
- contains = false
81
-
82
- unless password.blank? or username.blank?
83
- escaped_username = Regexp.escape(username)
84
- username_regexp = Regexp.new(escaped_username, Regexp::IGNORECASE)
85
-
86
- if username_regexp.match(password)
87
- contains = true
88
- end
89
- end
90
-
91
- contains
92
- end
93
-
94
- # Whether `password` is in {COMMON_PASSWORDS} or a simple variation of a password in {COMMON_PASSWORDS}.
95
- #
96
- # @param password [String]
97
- # @return [Boolean]
98
- def is_common_password?(password)
99
- COMMON_PASSWORDS.each do |pw|
100
- common_pw = [pw, pw + "!", pw + "1", pw + "12", pw + "123", pw + "1234"]
101
- if common_pw.include?(password.downcase)
102
- return true
103
- end
104
- end
105
- false
106
- end
107
-
108
- # Returns whether the password is simple.
109
- #
110
- # @return [false] if password contains a letter, digit and special character.
111
- # @return [true] otherwise
112
- def is_simple?(password)
113
- not (password =~ /[A-Za-z]/ and password =~ /[0-9]/ and password =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/)
114
- end
115
- end
141
+ end
@@ -9,6 +9,7 @@
9
9
  require 'active_model'
10
10
  require 'active_support'
11
11
 
12
+ autoload :AddressFormatValidator, 'address_format_validator'
12
13
  autoload :IpFormatValidator, 'ip_format_validator'
13
14
  autoload :NilValidator, 'nil_validator'
14
15
  autoload :ParametersValidator, 'parameters_validator'
@@ -1,5 +1,5 @@
1
1
  # Superclass for all Metasploit::Models. Just adds a default {#initialize} to make models mimic behavior of
2
- # ActiveRecord::Base subclasses.
2
+ # ApplicationRecord subclasses.
3
3
  class Metasploit::Model::Base
4
4
  include ActiveModel::Validations
5
5
 
@@ -22,4 +22,4 @@ class Metasploit::Model::Base
22
22
  raise Metasploit::Model::Invalid.new(self)
23
23
  end
24
24
  end
25
- end
25
+ end