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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -1
- data/.travis.yml +8 -3
- data/.yardopts +2 -1
- data/Gemfile +2 -2
- data/app/validators/address_format_validator.rb +26 -0
- data/app/validators/ip_format_validator.rb +13 -19
- data/app/validators/nil_validator.rb +3 -3
- data/app/validators/parameters_validator.rb +18 -10
- data/app/validators/password_is_strong_validator.rb +93 -67
- data/lib/metasploit/model.rb +1 -0
- data/lib/metasploit/model/base.rb +2 -2
- data/lib/metasploit/model/engine.rb +2 -2
- data/lib/metasploit/model/search/attribute.rb +1 -1
- data/lib/metasploit/model/translation.rb +2 -2
- data/lib/metasploit/model/version.rb +1 -1
- data/metasploit-model.gemspec +9 -9
- data/spec/app/models/metasploit/model/association/reflection_spec.rb +2 -2
- data/spec/app/models/metasploit/model/search/operator/association_spec.rb +1 -1
- data/spec/app/models/metasploit/model/search/operator/attribute_spec.rb +1 -1
- data/spec/app/validators/address_format_validator_spec.rb +136 -0
- data/spec/app/validators/ip_format_validator_spec.rb +0 -24
- data/spec/app/validators/password_is_strong_validator_spec.rb +31 -42
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/config/application.rb +1 -5
- data/spec/dummy/config/environments/development.rb +0 -10
- data/spec/dummy/db/schema.rb +0 -1
- data/spec/factories/metasploit/model/association/reflections.rb +1 -1
- data/spec/factories/metasploit/model/bases.rb +1 -1
- data/spec/factories/metasploit/model/search/operator/associations.rb +1 -1
- data/spec/factories/metasploit/model/search/operator/attributes.rb +1 -1
- data/spec/factories/metasploit/model/search/operator/bases.rb +1 -1
- data/spec/lib/metasploit/model/engine_spec.rb +3 -3
- data/spec/lib/metasploit/model/search/attribute_spec.rb +2 -2
- metadata +93 -100
- metadata.gz.sig +0 -0
- data/spec/dummy/config/initializers/assets.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7282653b38429b72974d4f1c6a98c7023aeb85189aa1b929fcc1e1f0751b47b5
|
4
|
+
data.tar.gz: 07e615389202c882a355b8ac7f8776d45b99e846512d64b8355a3ae8f1e2e9f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e6634c258bc4ae1d49200b5786e4962e5ceea839311d1cea2e66dd76a9e5eb96f05800122c74be18fd52b291c7ed23ade8e7637b6cff2890046298bc92f9b96
|
7
|
+
data.tar.gz: ac18802b7f7a78705f88fbff920c162da5b718394abc0d5e53a167f3d0bfb7e64fb59ddf6594ea39b25ce865ab2b75f26906001e923a8ea6c27c252ad2a3c2c0
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
�?���}�M=����g��1"V��)�A�X�yIQb�� T����,<��
|
2
|
+
�^y��.
|
data/.travis.yml
CHANGED
@@ -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.
|
7
|
+
postgresql: '9.6'
|
7
8
|
apt:
|
8
9
|
packages:
|
9
10
|
- graphviz
|
10
11
|
rvm:
|
11
|
-
- 2.
|
12
|
+
- 2.6.5
|
12
13
|
before_install:
|
13
|
-
- gem
|
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
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 '
|
10
|
+
gem 'factory_bot'
|
11
11
|
# auto-load factories from spec/factories
|
12
|
-
gem '
|
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
|
3
|
+
# Validates that value is a valid IPv4 or IPv6 address.
|
4
4
|
class IpFormatValidator < ActiveModel::EachValidator
|
5
|
-
# Validates that `value` is
|
5
|
+
# Validates that `attribute`'s `value` on `object` is a valid IPv4 or IPv6 address.
|
6
6
|
#
|
7
|
-
# @param record [#errors,
|
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
|
-
|
12
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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,
|
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
|
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
|
-
#
|
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
|
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
|
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
|
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
|
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
|
78
|
+
record.errors.add attribute, error
|
71
79
|
end
|
72
80
|
end
|
73
81
|
else
|
74
|
-
record.errors
|
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
|
-
#
|
7
|
+
# Known passwords that should NOT be allowed and should be considered weak.
|
8
8
|
COMMON_PASSWORDS = %w{
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
#
|
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,
|
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
|
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
|
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
|
36
|
+
record.errors.add attribute, 'must not be a common password'
|
39
37
|
end
|
40
38
|
|
41
|
-
if
|
42
|
-
record.errors
|
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
|
-
#
|
49
|
-
#
|
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
|
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
|
data/lib/metasploit/model.rb
CHANGED
@@ -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
|
-
#
|
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
|