metasploit-model 3.0.0 → 3.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a5f988847549534fae7fffc226d076ba4bd61664ec29df83d88bd063841d7a4
4
- data.tar.gz: c9e43a7f1669acf013d92202396df2e37a2225a9ddebd0d868d448891015d0d3
3
+ metadata.gz: 9735bf99e2c7ca26ac1b2e41075a5d8895f4ea952a0641e1cb3189468086cf66
4
+ data.tar.gz: c4659546c247eb29d7c06a4af9ef765eda2606df5876f4405f6e45430ce143ff
5
5
  SHA512:
6
- metadata.gz: b3382aedd7f694cb1d1087d524763f00d0c01f505cd530cb5acf708681a78c6e94ef8725bbd07e1174d107eba468a5c4ec905c6dc30c22825afbfba74ba6fdec
7
- data.tar.gz: b86c773e1eb4789c7b32660f24bceb4a7746f0d148ef57a668ab1fd6e55eb1337a12d498414dc9e17255328e3d6efba2ab5acf4b627ca595f8eccf30805d4769
6
+ metadata.gz: 7909c402f1aca1f347e6f20a4d8062607c59c871178a1b20aacc45af5e781426363527cf77b200a4a24d252d5bbd0d477c16b8cea0e185edcb765095377c7d9c
7
+ data.tar.gz: a81b3d657573da78157a54bfa78bea6083e6490ea1f164ce7be83332816c95cc49796bc4a715fcec5b08603cd1e221231439795fb1037171e76a840dc54b64cf
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -0,0 +1,83 @@
1
+ name: Verify
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '*'
7
+ pull_request:
8
+ branches:
9
+ - '*'
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-16.04
14
+ timeout-minutes: 40
15
+
16
+ services:
17
+ postgres:
18
+ image: postgres:9.6
19
+ ports: [ "5432:5432" ]
20
+ env:
21
+ POSTGRES_USER: postgres
22
+ POSTGRES_PASSWORD: postgres
23
+ options: >-
24
+ --health-cmd pg_isready
25
+ --health-interval 10s
26
+ --health-timeout 5s
27
+ --health-retries 5
28
+
29
+ strategy:
30
+ fail-fast: true
31
+ matrix:
32
+ ruby:
33
+ - 2.5
34
+ - 2.6
35
+ - 2.7
36
+
37
+ env:
38
+ RAILS_ENV: test
39
+
40
+ name: Ruby ${{ matrix.ruby }}
41
+ steps:
42
+ - name: Install system dependencies
43
+ run: sudo apt-get install graphviz
44
+
45
+ - name: Checkout code
46
+ uses: actions/checkout@v2
47
+
48
+ - uses: actions/setup-ruby@v1
49
+ with:
50
+ ruby-version: ${{ matrix.ruby }}
51
+
52
+ - name: Setup bundler
53
+ run: |
54
+ gem install bundler
55
+ - uses: actions/cache@v2
56
+ with:
57
+ path: vendor/bundle
58
+ key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
59
+ restore-keys: |
60
+ ${{ runner.os }}-gems-
61
+ - name: Bundle install
62
+ run: |
63
+ bundle config path vendor/bundle
64
+ bundle install --jobs 4 --retry 3
65
+
66
+ - name: Test
67
+ run: |
68
+ gem install bundler
69
+ cp spec/dummy/config/database.yml.github_actions spec/dummy/config/database.yml
70
+ bundle install
71
+ bundle exec rake --version
72
+ bundle exec rake db:test:prepare
73
+
74
+ bundle exec rake spec
75
+ bundle exec rake yard
76
+
77
+ - name: Upload coverage report
78
+ uses: actions/upload-artifact@v2
79
+ with:
80
+ name: coverage-${{ matrix.ruby }}
81
+ path: |
82
+ coverage/
83
+ retention-days: 1
data/Gemfile CHANGED
@@ -18,8 +18,6 @@ group :test do
18
18
 
19
19
  # Dummy app uses actionpack for ActionController, but not rails since it doesn't use activerecord.
20
20
  gem 'actionpack'
21
- # Uploads simplecov reports to coveralls.io
22
- gem 'coveralls', require: false
23
21
  # Engine tasks are loaded using railtie
24
22
  gem 'railties'
25
23
  gem 'rspec-rails'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Metasploit::Model [![Build Status](https://travis-ci.org/rapid7/metasploit-model.png)](https://travis-ci.org/rapid7/metasploit-model)[![Code Climate](https://codeclimate.com/github/rapid7/metasploit-model.png)](https://codeclimate.com/github/rapid7/metasploit-model)[![Coverage Status](https://coveralls.io/repos/rapid7/metasploit-model/badge.png?branch=feature%2Fexploit)](https://coveralls.io/r/rapid7/metasploit-model)[![Dependency Status](https://gemnasium.com/rapid7/metasploit-model.svg)](https://gemnasium.com/rapid7/metasploit-model)[![Gem Version](https://badge.fury.io/rb/metasploit-model.svg)](http://badge.fury.io/rb/metasploit-model)[![Inline docs](http://inch-ci.org/github/rapid7/metasploit-model.svg?branch=master)](http://inch-ci.org/github/rapid7/metasploit-model)[![PullReview stats](https://www.pullreview.com/github/rapid7/metasploit-model/badges/master.svg)](https://www.pullreview.com/github/rapid7/metasploit-model/reviews/master)
1
+ # Metasploit::Model [![Build Status](https://github.com/rapid7/metasploit-model/actions/workflows/verify.yml/badge.svg)](https://github.com/rapid7/metasploit-model/actions/workflows/verify.yml)[![Code Climate](https://codeclimate.com/github/rapid7/metasploit-model.png)](https://codeclimate.com/github/rapid7/metasploit-model)[![Dependency Status](https://gemnasium.com/rapid7/metasploit-model.svg)](https://gemnasium.com/rapid7/metasploit-model)[![Gem Version](https://badge.fury.io/rb/metasploit-model.svg)](http://badge.fury.io/rb/metasploit-model)[![Inline docs](http://inch-ci.org/github/rapid7/metasploit-model.svg?branch=master)](http://inch-ci.org/github/rapid7/metasploit-model)[![PullReview stats](https://www.pullreview.com/github/rapid7/metasploit-model/badges/master.svg)](https://www.pullreview.com/github/rapid7/metasploit-model/reviews/master)
2
2
 
3
3
  ## Versioning
4
4
 
@@ -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
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
@@ -10,7 +10,7 @@ class NilValidator < ActiveModel::EachValidator
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
16
  end
@@ -1,11 +1,19 @@
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.
11
+ #
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.
9
17
  #
10
18
  # @param record [#errors, ApplicationRecord] ActiveModel or ActiveRecord
11
19
  # @param attribute [Symbol] serialized parameters attribute name.
@@ -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
 
@@ -4,20 +4,18 @@ 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
20
  # @param record [#errors, #username, ApplicationRecord] ActiveModel or ActiveRecord that supports #username method.
23
21
  # @param attribute [Symbol] password attribute name.
@@ -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
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'
@@ -41,5 +41,6 @@ if RUBY_PLATFORM =~ /java/ && Gem::Version.new(JRUBY_VERSION) < Gem::Version.new
41
41
  end
42
42
  end
43
43
  else
44
+ # Alias for ::File when not in jruby.
44
45
  Metasploit::Model::File = ::File
45
46
  end
@@ -72,6 +72,6 @@ module Metasploit::Model::Search::Operator::Help
72
72
  name: name
73
73
  }
74
74
 
75
- ::I18n.translate(key, options)
75
+ ::I18n.translate(key, **options)
76
76
  end
77
77
  end
@@ -1,7 +1,7 @@
1
1
  module Metasploit
2
2
  module Model
3
3
  # VERSION is managed by GemRelease
4
- VERSION = '3.0.0'
4
+ VERSION = '3.1.4'
5
5
 
6
6
  # @return [String]
7
7
  #
@@ -6,24 +6,24 @@ require 'metasploit/model/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'metasploit-model'
8
8
  spec.version = Metasploit::Model::VERSION
9
- spec.authors = ['Luke Imhoff']
10
- spec.email = ['luke_imhoff@rapid7.com']
9
+ spec.authors = ['Metasploit Hackers']
10
+ spec.email = ['msfdev@metasploit.com']
11
11
  spec.description = %q{Common code, such as validators and mixins, that are shared between ActiveModels in metasploit-framework and ActiveRecords in metasploit_data_models.}
12
12
  spec.summary = %q{Metasploit Model Mixins and Validators}
13
13
 
14
- spec.files = `git ls-files`.split($/)
14
+ spec.files = `git ls-files`.split($/).reject { |file|
15
+ file =~ /^bin/
16
+ }
15
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
18
  spec.require_paths = %w{app/models app/validators lib}
17
19
 
18
- spec.required_ruby_version = '>= 2.2.0'
20
+ spec.required_ruby_version = '>= 2.4.0'
19
21
 
20
22
  spec.add_development_dependency 'metasploit-yard'
21
23
  spec.add_development_dependency 'metasploit-erd'
22
24
  spec.add_development_dependency 'rake'
23
-
24
- # documentation
25
- # 0.8.7.4 has a bug where attribute setters show up as undocumented
26
- spec.add_development_dependency 'yard', '< 0.8.7.4'
25
+ spec.add_development_dependency 'yard'
26
+ spec.add_development_dependency 'e2mmap'
27
27
 
28
28
  # Dependency loading
29
29
 
@@ -0,0 +1,136 @@
1
+ RSpec.describe AddressFormatValidator do
2
+ subject(:address_format_validator) do
3
+ described_class.new(
4
+ :attributes => attributes
5
+ )
6
+ end
7
+
8
+ let(:attribute) do
9
+ :address
10
+ end
11
+
12
+ let(:attributes) do
13
+ [
14
+ attribute
15
+ ]
16
+ end
17
+
18
+ context '#validate_each' do
19
+ subject(:validate_each) do
20
+ address_format_validator.validate_each(record, attribute, value)
21
+ end
22
+
23
+ let(:error) do
24
+ 'must be a valid (IP or hostname) address'
25
+ end
26
+
27
+ let(:record) do
28
+ record_class.new
29
+ end
30
+
31
+ let(:record_class) do
32
+ # capture for Class.new scope
33
+ attribute = self.attribute
34
+
35
+ Class.new do
36
+ include ActiveModel::Validations
37
+
38
+ #
39
+ # Validations
40
+ #
41
+
42
+ validates attribute,
43
+ :address_format => true
44
+ end
45
+ end
46
+
47
+ context 'address' do
48
+ context 'in IPv4 format' do
49
+ let(:value) do
50
+ '192.168.0.1'
51
+ end
52
+
53
+ it 'should not record any errors' do
54
+ validate_each
55
+
56
+ expect(record.errors).to be_empty
57
+ end
58
+ end
59
+
60
+ context 'in IPv6 format' do
61
+ let(:value) do
62
+ '::1'
63
+ end
64
+
65
+ it 'should not record any errors' do
66
+ validate_each
67
+
68
+ expect(record.errors).to be_empty
69
+ end
70
+ end
71
+
72
+ context 'with a valid hostname' do
73
+ let(:value) do
74
+ 'testvalue.test.com'
75
+ end
76
+
77
+ it 'should not record any errors' do
78
+ validate_each
79
+
80
+ expect(record.errors).to be_empty
81
+ end
82
+ end
83
+
84
+ context 'with localhost' do
85
+ let(:value) do
86
+ 'localhost'
87
+ end
88
+
89
+ it 'should not record any errors' do
90
+ validate_each
91
+
92
+ expect(record.errors).to be_empty
93
+ end
94
+ end
95
+
96
+ context 'with blank address' do
97
+ let(:value) do
98
+ ''
99
+ end
100
+
101
+ it 'should record error' do
102
+ validate_each
103
+
104
+ expect(record.errors[attribute]).to include(error)
105
+ end
106
+ end
107
+
108
+ context 'with nil value' do
109
+ let(:value) do
110
+ nil
111
+ end
112
+
113
+ it 'should record error on attribute' do
114
+ validate_each
115
+
116
+ expect(record.errors[attribute]).to include(error)
117
+ end
118
+ end
119
+
120
+ context 'with a malformed hostname should record an error' do
121
+ invalid_hostnames = ['testvalue.test.com:', 'testvalue-.test.com', '[testvalue.test.com]']
122
+ invalid_hostnames.each do | entry |
123
+ let(:value) do
124
+ entry
125
+ end
126
+
127
+ it 'should record an error on attribute' do
128
+ validate_each
129
+ expect(record.errors[attribute]).to include(error)
130
+ end
131
+ end
132
+
133
+ end
134
+ end
135
+ end
136
+ end
@@ -69,30 +69,6 @@ RSpec.describe IpFormatValidator do
69
69
  end
70
70
  end
71
71
 
72
- context 'with IPv4 range' do
73
- let(:value) do
74
- '127.0.0.1/8'
75
- end
76
-
77
- it 'should record error' do
78
- validate_each
79
-
80
- expect(record.errors[attribute]).to include("#{error} and not an IPv4 address range in CIDR or netmask notation")
81
- end
82
- end
83
-
84
- context 'with IPv6 range' do
85
- let(:value) do
86
- '3ffe:505:2::1/48'
87
- end
88
-
89
- it 'should record error' do
90
- validate_each
91
-
92
- expect(record.errors[attribute]).to include("#{error} and not an IPv6 address range in CIDR or netmask notation")
93
- end
94
- end
95
-
96
72
  context 'without IPv4 or IPv6 address' do
97
73
  let(:value) do
98
74
  'localhost'
@@ -15,9 +15,9 @@ RSpec.describe PasswordIsStrongValidator do
15
15
  ]
16
16
  end
17
17
 
18
- context '#contains_repetition' do
19
- subject(:contains_repetition?) do
20
- password_is_strong_validator.send(:contains_repetition?, password)
18
+ context '#is_only_repetition' do
19
+ subject(:is_only_repetition?) do
20
+ password_is_strong_validator.send(:is_only_repetition?, password)
21
21
  end
22
22
 
23
23
  context 'with all the same character' do
@@ -56,8 +56,8 @@ RSpec.describe PasswordIsStrongValidator do
56
56
  let(:password) do
57
57
  'abcdefgh'
58
58
  end
59
-
60
59
  it { is_expected.to eq(false) }
60
+
61
61
  end
62
62
  end
63
63
 
@@ -66,59 +66,48 @@ RSpec.describe PasswordIsStrongValidator do
66
66
  password_is_strong_validator.send(:contains_username?, username, password)
67
67
  end
68
68
 
69
- let(:username) do
70
- ''
71
- end
72
-
73
- context 'with blank password' do
74
- let(:password) do
75
- ''
76
- end
77
-
78
- it { is_expected.to eq(false) }
79
- end
80
-
81
- context 'without blank password' do
82
- let(:password) do
83
- 'password'
69
+ context 'without blank username' do
70
+ let(:username) do
71
+ 'username'
84
72
  end
85
73
 
86
- context 'with blank username' do
87
- let(:username) do
74
+ context 'with blank password' do
75
+ let(:password) do
88
76
  ''
89
77
  end
90
-
91
78
  it { is_expected.to eq(false) }
92
79
  end
93
80
 
94
- context 'without blank username' do
81
+ context 'with reserved regex characters in username' do
95
82
  let(:username) do
96
- 'username'
83
+ "user(with)regex"
97
84
  end
98
85
 
99
- it 'should escape username' do
100
- expect(Regexp).to receive(:escape).with(username).and_call_original
101
-
102
- contains_username?
86
+ context 'with username in password' do
87
+ let(:password) do
88
+ "myPassworduser(with)regexValue"
89
+ end
90
+ it { is_expected.to eq(false)}
103
91
  end
92
+ end
104
93
 
105
- context 'with matching password' do
106
- context 'of different case' do
107
- let(:password) do
108
- username.titleize
109
- end
110
-
111
- it { is_expected.to eq(true) }
94
+ context 'with matching password' do
95
+ context 'of different case' do
96
+ let(:password) do
97
+ username.titleize
112
98
  end
113
99
 
114
- context 'of same case' do
115
- let(:password) do
116
- username
117
- end
100
+ it { is_expected.to eq(true) }
101
+ end
118
102
 
119
- it { is_expected.to eq(true) }
103
+ context 'of same case' do
104
+ let(:password) do
105
+ username
120
106
  end
107
+
108
+ it { is_expected.to eq(true) }
121
109
  end
110
+
122
111
  end
123
112
  end
124
113
  end
@@ -231,7 +220,7 @@ RSpec.describe PasswordIsStrongValidator do
231
220
 
232
221
  context 'without repetition' do
233
222
  let(:value) do
234
- 'A$uperg00dp@ssw0rd'
223
+ 'A$uperg00dp@sw0rd'
235
224
  end
236
225
 
237
226
  it 'should not record any errors' do
@@ -245,4 +234,4 @@ RSpec.describe PasswordIsStrongValidator do
245
234
  end
246
235
  end
247
236
  end
248
- end
237
+ end
@@ -1,17 +1,16 @@
1
- # @note This file is only for use in travis-ci. If you need to make a `spec/dummy/config/database.yml` for running
1
+ # @note This file is only for use in github actions. If you need to make a `spec/dummy/config/database.yml` for running
2
2
  # rake, rake spec, or rspec locally, please customize `spec/dummy/config/database.yml.example`.
3
3
  #
4
4
  # @example Customizing config/database.yml.example
5
5
  # cp spec/dummy/config/database.yml.example spec/dummy/config/database.yml
6
6
  # # update password fields for each environment's user
7
7
 
8
- # Using the postgres user locally without a host and port is the supported configuration from Travis-CI
9
- #
10
- # @see http://about.travis-ci.org/docs/user/database-setup/#PostgreSQL
11
8
  development: &pgsql
12
9
  adapter: postgresql
13
10
  database: metasploit_model_development
11
+ host: localhost
14
12
  username: postgres
13
+ password: postgres
15
14
  pool: 5
16
15
  timeout: 5
17
16
 
@@ -19,4 +18,4 @@ development: &pgsql
19
18
  # `rake`. Do not set this db to the same as development or production.
20
19
  test:
21
20
  <<: *pgsql
22
- database: metasploit_model_test
21
+ database: metasploit_model_test
data/spec/spec_helper.rb CHANGED
@@ -8,9 +8,8 @@ Bundler.setup(:default, :test)
8
8
  # Require simplecov before loading ..dummy/config/environment.rb because it will cause metasploit_data_models/lib to
9
9
  # be loaded, which would result in Coverage not recording hits for any of the files.
10
10
  require 'simplecov'
11
- require 'coveralls'
12
11
 
13
- SimpleCov.formatter = Coveralls::SimpleCov::Formatter
12
+ SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
14
13
 
15
14
  require File.expand_path('../dummy/config/environment.rb', __FILE__)
16
15
  require 'rspec/rails'
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metasploit-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.4
5
5
  platform: ruby
6
6
  authors:
7
- - Luke Imhoff
7
+ - Metasploit Hackers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain:
@@ -64,20 +64,20 @@ cert_chain:
64
64
  -----END CERTIFICATE-----
65
65
  - |
66
66
  -----BEGIN CERTIFICATE-----
67
- MIIFIzCCBAugAwIBAgIQDX9ZkVJ2eNVTlibR5ALyJTANBgkqhkiG9w0BAQsFADBy
67
+ MIIFIzCCBAugAwIBAgIQCMePMbkSxvnPeJhYXIfaxzANBgkqhkiG9w0BAQsFADBy
68
68
  MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
69
69
  d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQg
70
- SUQgQ29kZSBTaWduaW5nIENBMB4XDTE5MTAxNjAwMDAwMFoXDTIwMTAxOTEyMDAw
70
+ SUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMTAwNzAwMDAwMFoXDTIzMTEwNjEyMDAw
71
71
  MFowYDELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxDzANBgNV
72
72
  BAcTBkJvc3RvbjETMBEGA1UEChMKUmFwaWQ3IExMQzETMBEGA1UEAxMKUmFwaWQ3
73
- IExMQzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHnKegPAghKuZk4
74
- Gy1jKaZEXbWc4fxioTemv/F1yIYzAjCWP65qjKtyeeFDe4/kJzG9nseF9oa93YBf
75
- 1nyEqxNSZMw/sCAZ87lOl713dRi73uxOoszy2PT5xEB+Q5R6cbzExkWG2zrLdXDr
76
- so0Bd6VHw+IsAoBBkAq5FrZOJQYGn5VY20xw/2DqtCeoW4QDWyqTnbJmwO9tZrfr
77
- 3Le2crfk2eOgafaPNhLon5uuIKCZsk2YkUSNURSS3M7gosMwU9Gg4JTBi7X5+oww
78
- rY43dJT28YklxmNVu8o5kJxW4dqLKJLOIgSXZ63nceT/EaCSg7DcofHNcUzejFwb
79
- M7Zbb2kCAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZl
80
- dQ5YMB0GA1UdDgQWBBR18CAeMsIEU+0pXal/XXw9LCtMADAOBgNVHQ8BAf8EBAMC
73
+ IExMQzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALNTz4zvAy7h/vQp
74
+ 4dr1txXHlABAagkwYYwTMCtHs5PXsJITx/5SAjx5swuaLfze5kPBNF2YImvFlOXY
75
+ WaB+0PsOnXnaARsDZU683xFlj8izU6IN6VrAHzDLKFBzruJENrOJD/ikbEtbjO/q
76
+ gFbmS9J9v5ohG/pcRSS0t4ZPAwymf8eCp6QsvOKK/Aymp1RhlRaP8N6N5CIpkhz1
77
+ 9p968iCE+DjOXVYxcWE+jE/7uB1dbgrXykNBujMSS3GULOvVEY28n6NCmrPlo23g
78
+ yRjYVJ2Vy14nBqnxDZ/yRIfWRVjWoT9TsAEbe9gY29oDpSCSs4wSmLQd5zGCpZ9h
79
+ r0HDFB8CAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZl
80
+ dQ5YMB0GA1UdDgQWBBTLBL7DTwumVEKtdCdpHVYMXOFeDzAOBgNVHQ8BAf8EBAMC
81
81
  B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDov
82
82
  L2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGG
83
83
  L2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3Js
@@ -86,14 +86,14 @@ cert_chain:
86
86
  JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcw
87
87
  AoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3Vy
88
88
  ZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL
89
- BQADggEBAFpzR9s7lcYKDzSJucOHztEPj+iSIeCzxEw34NTE9M2AfkYIu82c4r2a
90
- bzIGmzZWiCGufjOp0gF5xW6sSSJ9n0TqH0nhHhvjtZQkmkGtOBbN1zeYDFS2ozAp
91
- sljF/g68Y1eYs3NaFf7kQUa6vb6RdjW3J8M9AQ8gthBt7gr/guVxd/gJUYbdDdBX
92
- cWfJJi/X7GVBOBmmvA43qoKideuhOBrVGBHvIF/yO9p23dIiUrGmW9kxXCSxgute
93
- JI/W23RbIRksG2pioMhd4dCXq3FLLlkOV1YfCwWixNB+iIhQPPZVaPNfgPhCn4Dt
94
- DeGjje/qA4fkLtRmOtb9PUBq3ToRDE4=
89
+ BQADggEBAN+GL5/myPWg7oH4mVrG7/OhXF1MoYQF0ddaNiqaweEHMuKJBQCVZRbL
90
+ 37HojoKXXv2yyRJBCeTB+ojrxX+5PdLVZa0ss7toWzJ2A1poPXZ1eZvm5xeFD32z
91
+ YQaTmmNWNI3PCDTyJ2PXUc+bDiNNwcZ7yc5o78UNRvp9Jxghya17Q76c9Ov9wvnv
92
+ dxxQKWGOQy0m4fBrkyjAyH9Djjn81RbQrqYgPuhd5nD0HjN3VUQLhQbIJrk9TVs0
93
+ EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
94
+ 9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
95
95
  -----END CERTIFICATE-----
96
- date: 2020-02-01 00:00:00.000000000 Z
96
+ date: 2021-04-28 00:00:00.000000000 Z
97
97
  dependencies:
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: metasploit-yard
@@ -141,16 +141,30 @@ dependencies:
141
141
  name: yard
142
142
  requirement: !ruby/object:Gem::Requirement
143
143
  requirements:
144
- - - "<"
144
+ - - ">="
145
145
  - !ruby/object:Gem::Version
146
- version: 0.8.7.4
146
+ version: '0'
147
147
  type: :development
148
148
  prerelease: false
149
149
  version_requirements: !ruby/object:Gem::Requirement
150
150
  requirements:
151
- - - "<"
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ - !ruby/object:Gem::Dependency
155
+ name: e2mmap
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
152
159
  - !ruby/object:Gem::Version
153
- version: 0.8.7.4
160
+ version: '0'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
154
168
  - !ruby/object:Gem::Dependency
155
169
  name: activemodel
156
170
  requirement: !ruby/object:Gem::Requirement
@@ -210,16 +224,15 @@ dependencies:
210
224
  description: Common code, such as validators and mixins, that are shared between ActiveModels
211
225
  in metasploit-framework and ActiveRecords in metasploit_data_models.
212
226
  email:
213
- - luke_imhoff@rapid7.com
227
+ - msfdev@metasploit.com
214
228
  executables: []
215
229
  extensions: []
216
230
  extra_rdoc_files: []
217
231
  files:
218
- - ".coveralls.yml"
232
+ - ".github/workflows/verify.yml"
219
233
  - ".gitignore"
220
234
  - ".rspec"
221
235
  - ".simplecov"
222
- - ".travis.yml"
223
236
  - ".yardopts"
224
237
  - CHANGELOG.md
225
238
  - CONTRIBUTING.md
@@ -263,11 +276,11 @@ files:
263
276
  - app/models/metasploit/model/search/operator/single.rb
264
277
  - app/models/metasploit/model/search/query.rb
265
278
  - app/models/metasploit/model/visitation/visitor.rb
279
+ - app/validators/address_format_validator.rb
266
280
  - app/validators/ip_format_validator.rb
267
281
  - app/validators/nil_validator.rb
268
282
  - app/validators/parameters_validator.rb
269
283
  - app/validators/password_is_strong_validator.rb
270
- - bin/rails
271
284
  - config/locales/en.yml
272
285
  - lib/metasploit/model.rb
273
286
  - lib/metasploit/model/association.rb
@@ -337,6 +350,7 @@ files:
337
350
  - spec/app/models/metasploit/model/search/operator/single_spec.rb
338
351
  - spec/app/models/metasploit/model/search/query_spec.rb
339
352
  - spec/app/models/metasploit/model/visitation/visitor_spec.rb
353
+ - spec/app/validators/address_format_validator_spec.rb
340
354
  - spec/app/validators/ip_format_validator_spec.rb
341
355
  - spec/app/validators/nil_validator_spec.rb
342
356
  - spec/app/validators/parameters_validator_spec.rb
@@ -356,7 +370,7 @@ files:
356
370
  - spec/dummy/config/application.rb
357
371
  - spec/dummy/config/boot.rb
358
372
  - spec/dummy/config/database.yml.example
359
- - spec/dummy/config/database.yml.travis
373
+ - spec/dummy/config/database.yml.github_actions
360
374
  - spec/dummy/config/environment.rb
361
375
  - spec/dummy/config/environments/development.rb
362
376
  - spec/dummy/config/environments/production.rb
@@ -435,7 +449,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
435
449
  requirements:
436
450
  - - ">="
437
451
  - !ruby/object:Gem::Version
438
- version: 2.2.0
452
+ version: 2.4.0
439
453
  required_rubygems_version: !ruby/object:Gem::Requirement
440
454
  requirements:
441
455
  - - ">="
@@ -476,6 +490,7 @@ test_files:
476
490
  - spec/app/models/metasploit/model/search/operator/single_spec.rb
477
491
  - spec/app/models/metasploit/model/search/query_spec.rb
478
492
  - spec/app/models/metasploit/model/visitation/visitor_spec.rb
493
+ - spec/app/validators/address_format_validator_spec.rb
479
494
  - spec/app/validators/ip_format_validator_spec.rb
480
495
  - spec/app/validators/nil_validator_spec.rb
481
496
  - spec/app/validators/parameters_validator_spec.rb
@@ -495,7 +510,7 @@ test_files:
495
510
  - spec/dummy/config/application.rb
496
511
  - spec/dummy/config/boot.rb
497
512
  - spec/dummy/config/database.yml.example
498
- - spec/dummy/config/database.yml.travis
513
+ - spec/dummy/config/database.yml.github_actions
499
514
  - spec/dummy/config/environment.rb
500
515
  - spec/dummy/config/environments/development.rb
501
516
  - spec/dummy/config/environments/production.rb
metadata.gz.sig CHANGED
Binary file
data/.coveralls.yml DELETED
@@ -1 +0,0 @@
1
- service_name: travis-ci
data/.travis.yml DELETED
@@ -1,21 +0,0 @@
1
- dist: trusty
2
- group: stable
3
- sudo: false
4
- cache: bundler
5
- language: ruby
6
- addons:
7
- postgresql: '9.6'
8
- apt:
9
- packages:
10
- - graphviz
11
- rvm:
12
- - 2.6.5
13
- before_install:
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
19
- script:
20
- - bundle exec rake spec
21
- - bundle exec rake yard
data/bin/rails DELETED
@@ -1,14 +0,0 @@
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'