metasploit-model 2.0.2 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) 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/bin/rails +14 -0
  13. data/lib/metasploit/model.rb +1 -0
  14. data/lib/metasploit/model/base.rb +2 -2
  15. data/lib/metasploit/model/engine.rb +2 -2
  16. data/lib/metasploit/model/search/attribute.rb +1 -1
  17. data/lib/metasploit/model/translation.rb +2 -2
  18. data/lib/metasploit/model/version.rb +1 -1
  19. data/metasploit-model.gemspec +4 -6
  20. data/spec/app/models/metasploit/model/association/reflection_spec.rb +2 -2
  21. data/spec/app/models/metasploit/model/search/operator/association_spec.rb +1 -1
  22. data/spec/app/models/metasploit/model/search/operator/attribute_spec.rb +1 -1
  23. data/spec/app/validators/address_format_validator_spec.rb +136 -0
  24. data/spec/app/validators/ip_format_validator_spec.rb +0 -24
  25. data/spec/app/validators/password_is_strong_validator_spec.rb +31 -42
  26. data/spec/dummy/app/models/application_record.rb +3 -0
  27. data/spec/dummy/config/application.rb +1 -5
  28. data/spec/dummy/config/environments/development.rb +0 -10
  29. data/spec/dummy/db/schema.rb +0 -1
  30. data/spec/factories/metasploit/model/association/reflections.rb +1 -1
  31. data/spec/factories/metasploit/model/bases.rb +1 -1
  32. data/spec/factories/metasploit/model/search/operator/associations.rb +1 -1
  33. data/spec/factories/metasploit/model/search/operator/attributes.rb +1 -1
  34. data/spec/factories/metasploit/model/search/operator/bases.rb +1 -1
  35. data/spec/lib/metasploit/model/engine_spec.rb +3 -3
  36. data/spec/lib/metasploit/model/search/attribute_spec.rb +2 -2
  37. metadata +92 -98
  38. metadata.gz.sig +0 -0
  39. data/spec/dummy/config/initializers/assets.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ca1ce40d12dc010c6d8e50e5065bfb01f9f98010
4
- data.tar.gz: 10a9d9ce3ef8522d7837730b2ec26cb0fa3926e3
2
+ SHA256:
3
+ metadata.gz: 1d9c60bd7ee4b602e6ed7b8a9c3033284bc0b3521855f6f535462e95deae424f
4
+ data.tar.gz: 1e2200bf21b4e5cca22cfad82bcaf64d1937cfc730463798893d37f8f14cc3d4
5
5
  SHA512:
6
- metadata.gz: 96507f49db689fa50766d0a97ec301863816b62804f674a9b70c50e80bf71ef049bf07486eca06d4a6088e9a544b78dcdb6315fc980f9aeb4b317c5c431d152f
7
- data.tar.gz: b16ca56afd1c8ae309fa309f872880b427c9f8567ec2fcd16813fc66aff7b05ece08454beacd12b1de973eb08950836541d0d9d30e5e93db151f559f112aa40b
6
+ metadata.gz: 12f64d37690116152f6b281fdb4ba056a1546bf4647000c46fe925b39cbbb6ec1e1131d4422a51412681348bccaa4f4c235badb699ab768b57f4350b35253a98
7
+ data.tar.gz: 94c577557b024cc6a0d5aef112bc85689170b67fb7fab6ecb95abc9cd17f636c379f8d1ff1ad77a32129ea331b1fd3678a7691febb649d504a5c54b167dfe5ee
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.1
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
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/metasploit/model/engine', __dir__)
7
+ APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
12
+
13
+ require 'rails/all'
14
+ require 'rails/engine/commands'
@@ -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'