metasploit-model 2.0.4 → 3.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  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/file.rb +1 -0
  16. data/lib/metasploit/model/search/attribute.rb +1 -1
  17. data/lib/metasploit/model/search/operator/help.rb +1 -1
  18. data/lib/metasploit/model/translation.rb +2 -2
  19. data/lib/metasploit/model/version.rb +1 -1
  20. data/metasploit-model.gemspec +11 -13
  21. data/spec/app/models/metasploit/model/association/reflection_spec.rb +2 -2
  22. data/spec/app/models/metasploit/model/search/operator/association_spec.rb +1 -1
  23. data/spec/app/models/metasploit/model/search/operator/attribute_spec.rb +1 -1
  24. data/spec/app/validators/address_format_validator_spec.rb +136 -0
  25. data/spec/app/validators/ip_format_validator_spec.rb +0 -24
  26. data/spec/app/validators/password_is_strong_validator_spec.rb +31 -42
  27. data/spec/dummy/app/models/application_record.rb +3 -0
  28. data/spec/dummy/config/application.rb +1 -5
  29. data/spec/dummy/config/environments/development.rb +0 -10
  30. data/spec/dummy/db/schema.rb +0 -1
  31. data/spec/factories/metasploit/model/association/reflections.rb +1 -1
  32. data/spec/factories/metasploit/model/bases.rb +1 -1
  33. data/spec/factories/metasploit/model/search/operator/associations.rb +1 -1
  34. data/spec/factories/metasploit/model/search/operator/attributes.rb +1 -1
  35. data/spec/factories/metasploit/model/search/operator/bases.rb +1 -1
  36. data/spec/lib/metasploit/model/engine_spec.rb +3 -3
  37. data/spec/lib/metasploit/model/search/attribute_spec.rb +2 -2
  38. metadata +106 -99
  39. metadata.gz.sig +0 -0
  40. data/spec/dummy/config/initializers/assets.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f5931fcef9a47b50a9829e81ad51125d66e855a8
4
- data.tar.gz: 92a0b8a04a711bfd83b3ec395e5558d1867142ba
2
+ SHA256:
3
+ metadata.gz: 5dfe45d8a1e68673abc35d971c84c0008953a48ea3d02ea1474038737f26174f
4
+ data.tar.gz: cb013f8c4e3c54e36c5ab14cf4f3a00b3737b3914dd3b8f36ba89745080abae7
5
5
  SHA512:
6
- metadata.gz: d5508cca540e03ba5581227747d831f7378529392dff9b96938ad9ced88c91bb8f10c62f6c279786b2039cea36ce8fd72a852daf0575d9f933781cc8db5600b7
7
- data.tar.gz: 94bc2fd53546b919075f06f3b620510c98d95488d16cd8655208cab3c1e11a3521dab84875d6f1d4de3431914a62618cc4c3e37d1f027551d51530294ad74faa
6
+ metadata.gz: dd93de36bc27cee9b4f189bf3e3b50d500280eac1ec695340bad22e68ce0862e88c326c69d093945681515cbac44f57c9cef255f40a42daa00f1db2b94733aed
7
+ data.tar.gz: 34c2e286192dc1d7c92cd981a0b7e47f7523053a283930b3dafc9a2bf452f0dd5198b08b54f11723d8a03ee5959f93b0e1768494aeefef84aba810fdcde6c07f
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -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.7.2
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