metasploit_data_models 4.0.2 → 4.1.0

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: 7160a9465ddf37341f8823e9523185b042f1fb99db31e42a79aa25b583ddb676
4
- data.tar.gz: dfff423e1f177a6bdc6bc85b84225c3a243e2528283e49185d188c76d04808c1
3
+ metadata.gz: 60db39ea159287e80b6c950ee0c32905fce78fbbca7e8c117ed50c7d3f656ca8
4
+ data.tar.gz: f441e55e9110eb68f33a169ba5beb55977985fe6497164c45a3221643146c8dc
5
5
  SHA512:
6
- metadata.gz: 7c60f29e7ebff8b3c2f1aa0e7a10a9c06a07f186f57c3539deb24419ba65b5d004897ecada92e313b19c38f37c634dcf2dc55765aea3e0e1ae2b126f6748e597
7
- data.tar.gz: 501f97755d40903d2e25b3476538e498f650209df40c9522e98ddf7eb6ab1bd716e28454eb0ceb289991e22a400b10feb0665e776f0f4d4ee55822888247466f
6
+ metadata.gz: 80823abc0bc2632e626559ab9defb80c04096518079932b95afbab71158180687df6e6f00ea1db0c554f1e01940ed68ab6047be4afa983ff6e97803a2dbd1bb0
7
+ data.tar.gz: 490d14e31544ad0c9f9e6d8a59bd0b24a5b8da1d7b5fc8f87eb730796fca2791e8abb319d7921e5bc0ef930ddcbb34a8c1ab1f29207b1b3250ba39497cf91bfc
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,6 +1,5 @@
1
1
  # A connection to Nexpose from Metasploit.
2
2
  class Mdm::NexposeConsole < ApplicationRecord
3
-
4
3
  #
5
4
  # Associations
6
5
  #
@@ -80,7 +79,7 @@ class Mdm::NexposeConsole < ApplicationRecord
80
79
  # Callbacks
81
80
  #
82
81
 
83
- before_save :strip_protocol
82
+ before_validation :strip_protocol
84
83
 
85
84
  #
86
85
  # Serializations
@@ -96,10 +95,14 @@ class Mdm::NexposeConsole < ApplicationRecord
96
95
  # Validations
97
96
  #
98
97
 
99
- validates :address, :presence => true
98
+ validates :address, :presence => true, :address_format => true
99
+
100
100
  validates :name, :presence => true
101
+
101
102
  validates :password, :presence => true
103
+
102
104
  validates :port, :numericality => { :only_integer => true }, :inclusion => {:in => 1..65535}
105
+
103
106
  validates :username, :presence => true
104
107
 
105
108
  #
@@ -110,7 +113,7 @@ class Mdm::NexposeConsole < ApplicationRecord
110
113
  #
111
114
  # @return [void]
112
115
  def strip_protocol
113
- self.address.gsub!(/^http(s)*:\/\//i,'')
116
+ self.address.gsub!(/^http(s)*:\/\//i,'') unless self.address.nil?
114
117
  end
115
118
 
116
119
  Metasploit::Concern.run(self)
@@ -21,11 +21,6 @@ class Mdm::Workspace < ApplicationRecord
21
21
  class_name: 'MetasploitDataModels::AutomaticExploitation:MatchSet',
22
22
  inverse_of: :workspace
23
23
 
24
- # @deprecated Use `Mdm::Workspace#core_credentials` defined by `Metasploit::Credential::Engine` to get
25
- # `Metasploit::Credential::Core`s gathered from this workspace's {#hosts} and {#services}.
26
- #
27
- # Creds gathered from this workspace's {#hosts} and {#services}.
28
- has_many :creds, :through => :services, :class_name => 'Mdm::Cred'
29
24
 
30
25
  # Events that occurred in this workspace.
31
26
  has_many :events, dependent: :delete_all, :class_name => 'Mdm::Event'
@@ -80,6 +75,12 @@ class Mdm::Workspace < ApplicationRecord
80
75
 
81
76
  # Sessions opened on {#hosts} in this workspace.
82
77
  has_many :sessions, :through => :hosts, :class_name => 'Mdm::Session'
78
+
79
+ # @deprecated Use `Mdm::Workspace#core_credentials` defined by `Metasploit::Credential::Engine` to get
80
+ # `Metasploit::Credential::Core`s gathered from this workspace's {#hosts} and {#services}.
81
+ #
82
+ # Creds gathered from this workspace's {#hosts} and {#services}.
83
+ has_many :creds, :through => :services, :class_name => 'Mdm::Cred'
83
84
 
84
85
  #
85
86
  # Attributes
@@ -1,6 +1,6 @@
1
1
  module MetasploitDataModels
2
2
  # VERSION is managed by GemRelease
3
- VERSION = '4.0.2'
3
+ VERSION = '4.1.0'
4
4
 
5
5
  # @return [String]
6
6
  #
@@ -5,23 +5,15 @@ require 'metasploit_data_models/version'
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'metasploit_data_models'
7
7
  s.version = MetasploitDataModels::VERSION
8
- s.authors = [
9
- 'Samuel Huckins',
10
- 'Luke Imhoff',
11
- "David 'thelightcosine' Maloney",
12
- "Trevor 'burlyscudd' Rosen"
13
- ]
14
- s.email = [
15
- 'shuckins@rapid7.com',
16
- 'luke_imhoff@rapid7.com',
17
- 'dmaloney@rapid7.com',
18
- 'trevor_rosen@rapid7.com'
19
- ]
8
+ s.authors = ['Metasploit Hackers']
9
+ s.email = ['msfdev@metasploit.com']
20
10
  s.homepage = ""
21
11
  s.summary = %q{Database code for MSF and Metasploit Pro}
22
12
  s.description = %q{Implements minimal ActiveRecord models and database helper code used in both the Metasploit Framework (MSF) and Metasploit commercial editions.}
23
13
 
24
- s.files = `git ls-files`.split("\n")
14
+ s.files = `git ls-files`.split("\n").reject { |file|
15
+ file =~ /^bin/
16
+ }
25
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
18
  s.require_paths = %w{app/models app/validators lib}
27
19
 
@@ -46,7 +38,7 @@ Gem::Specification.new do |s|
46
38
  s.add_runtime_dependency 'activerecord', '~>5.2.2'
47
39
  s.add_runtime_dependency 'activesupport', '~>5.2.2'
48
40
  s.add_runtime_dependency 'metasploit-concern'
49
- s.add_runtime_dependency 'metasploit-model'
41
+ s.add_runtime_dependency 'metasploit-model', '>=3.1'
50
42
  s.add_runtime_dependency 'railties', '~>5.2.2'
51
43
 
52
44
  # os fingerprinting
@@ -32,7 +32,7 @@ RSpec.describe Mdm::NexposeConsole, type: :model do
32
32
 
33
33
  context '#destroy' do
34
34
  it 'should successfully destroy the object' do
35
- nexpose_console = FactoryBot.create(:mdm_nexpose_console)
35
+ nexpose_console = FactoryBot.create(:mdm_nexpose_console, :address => 'localhost')
36
36
  expect {
37
37
  nexpose_console.destroy
38
38
  }.to_not raise_error
@@ -50,6 +50,20 @@ RSpec.describe Mdm::NexposeConsole, type: :model do
50
50
  expect(addressless_nexpose_console.errors[:address]).to include("can't be blank")
51
51
  end
52
52
 
53
+ it 'should be valid for a valid hostname' do
54
+ host_nexpose_console = FactoryBot.build(:mdm_nexpose_console, :address => 'testvalue.test.com')
55
+ expect(host_nexpose_console).to be_valid
56
+ end
57
+
58
+ it 'should be invalid for a malformed hostname' do
59
+ host_nexpose_consoles = ['testvalue.test.com:', 'testvalue-.test.com', '[testvalue.test.com]']
60
+ host_nexpose_consoles.each do | entry |
61
+ host_nexpose_console = FactoryBot.build(:mdm_nexpose_console, :address => entry)
62
+ expect(host_nexpose_console).not_to be_valid
63
+ expect(host_nexpose_console.errors[:address]).to include("must be a valid (IP or hostname) address")
64
+ end
65
+ end
66
+
53
67
  it 'should be valid for IPv4 format' do
54
68
  ipv4_nexpose_console = FactoryBot.build(:mdm_nexpose_console, :address => '192.168.1.120')
55
69
  expect(ipv4_nexpose_console).to be_valid
metadata CHANGED
@@ -1,13 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metasploit_data_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.2
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Samuel Huckins
8
- - Luke Imhoff
9
- - David 'thelightcosine' Maloney
10
- - Trevor 'burlyscudd' Rosen
7
+ - Metasploit Hackers
11
8
  autorequire:
12
9
  bindir: bin
13
10
  cert_chain:
@@ -96,7 +93,7 @@ cert_chain:
96
93
  JI/W23RbIRksG2pioMhd4dCXq3FLLlkOV1YfCwWixNB+iIhQPPZVaPNfgPhCn4Dt
97
94
  DeGjje/qA4fkLtRmOtb9PUBq3ToRDE4=
98
95
  -----END CERTIFICATE-----
99
- date: 2020-07-07 00:00:00.000000000 Z
96
+ date: 2020-10-05 00:00:00.000000000 Z
100
97
  dependencies:
101
98
  - !ruby/object:Gem::Dependency
102
99
  name: metasploit-yard
@@ -230,14 +227,14 @@ dependencies:
230
227
  requirements:
231
228
  - - ">="
232
229
  - !ruby/object:Gem::Version
233
- version: '0'
230
+ version: '3.1'
234
231
  type: :runtime
235
232
  prerelease: false
236
233
  version_requirements: !ruby/object:Gem::Requirement
237
234
  requirements:
238
235
  - - ">="
239
236
  - !ruby/object:Gem::Version
240
- version: '0'
237
+ version: '3.1'
241
238
  - !ruby/object:Gem::Dependency
242
239
  name: railties
243
240
  requirement: !ruby/object:Gem::Requirement
@@ -311,10 +308,7 @@ dependencies:
311
308
  description: Implements minimal ActiveRecord models and database helper code used
312
309
  in both the Metasploit Framework (MSF) and Metasploit commercial editions.
313
310
  email:
314
- - shuckins@rapid7.com
315
- - luke_imhoff@rapid7.com
316
- - dmaloney@rapid7.com
317
- - trevor_rosen@rapid7.com
311
+ - msfdev@metasploit.com
318
312
  executables: []
319
313
  extensions: []
320
314
  extra_rdoc_files: []
@@ -408,11 +402,6 @@ files:
408
402
  - app/models/metasploit_data_models/search/visitor/method.rb
409
403
  - app/models/metasploit_data_models/search/visitor/relation.rb
410
404
  - app/models/metasploit_data_models/search/visitor/where.rb
411
- - app/validators/ip_format_validator.rb
412
- - app/validators/parameters_validator.rb
413
- - app/validators/password_is_strong_validator.rb
414
- - bin/mdm_console
415
- - bin/rails
416
405
  - config/initializers/arel_helper.rb
417
406
  - config/initializers/ipaddr.rb
418
407
  - config/locales/en.yml
@@ -649,8 +638,6 @@ files:
649
638
  - spec/app/models/metasploit_data_models/search/visitor/method_spec.rb
650
639
  - spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb
651
640
  - spec/app/models/metasploit_data_models/search/visitor/where_spec.rb
652
- - spec/app/validators/parameters_validator_spec.rb
653
- - spec/app/validators/password_is_strong_validator_spec.rb
654
641
  - spec/dummy/Rakefile
655
642
  - spec/dummy/app/assets/config/manifest.js
656
643
  - spec/dummy/app/assets/javascripts/application.js
metadata.gz.sig CHANGED
Binary file
@@ -1,22 +0,0 @@
1
- require "ipaddr"
2
-
3
- # Validates that attribute is a valid IPv4 or IPv6 address.
4
- class IpFormatValidator < ActiveModel::EachValidator
5
- # Validates that `attribute`'s `value` on `object` is a valid IPv4 or IPv6 address.
6
- #
7
- # @return [void]
8
- def validate_each(object, attribute, value)
9
- error_message_block = lambda{ object.errors.add attribute, " must be a valid IPv4 or IPv6 address" }
10
- begin
11
- if value.is_a? IPAddr
12
- potential_ip = value.dup
13
- else
14
- potential_ip = IPAddr.new(value)
15
- end
16
-
17
- error_message_block.call unless potential_ip.ipv4? || potential_ip.ipv6?
18
- rescue ArgumentError
19
- error_message_block.call
20
- end
21
- end
22
- end
@@ -1,129 +0,0 @@
1
- # Validates that attribute's value is Array<Array(String, String)> which is the only valid type signature for serialized
2
- # parameters.
3
- class ParametersValidator < ActiveModel::EachValidator
4
- #
5
- # CONSTANTS
6
- #
7
-
8
- # Sentence explaining the valid type signature for parameters.
9
- TYPE_SIGNATURE_SENTENCE = 'Valid parameters are an Array<Array(String, String)>.'
10
-
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.
17
- #
18
- # @return [void]
19
- def validate_each(record, attribute, value)
20
- if value.is_a? Array
21
- value.each_with_index do |element, index|
22
- if element.is_a? Array
23
- if element.length != 2
24
- extreme = :few
25
-
26
- if element.length > 2
27
- extreme = :many
28
- end
29
-
30
- length_error = length_error_at(
31
- :extreme => extreme,
32
- :element => element,
33
- :index => index
34
- )
35
-
36
- record.errors.add attribute, length_error
37
- else
38
- parameter_name = element.first
39
-
40
- if parameter_name.is_a? String
41
- unless parameter_name.present?
42
- error = error_at(
43
- :element => element,
44
- :index => index,
45
- :prefix => "has blank parameter name"
46
- )
47
- record.errors.add attribute, error
48
- end
49
- else
50
- error = error_at(
51
- :element => element,
52
- :index => index,
53
- :prefix => "has non-String parameter name (#{parameter_name.inspect})"
54
- )
55
- record.errors.add attribute, error
56
- end
57
-
58
- parameter_value = element.second
59
-
60
- unless parameter_value.is_a? String
61
- error = error_at(
62
- :element => element,
63
- :index => index,
64
- :prefix => "has non-String parameter value (#{parameter_value.inspect})"
65
- )
66
- record.errors.add attribute, error
67
- end
68
- end
69
- else
70
- error = error_at(
71
- :element => element,
72
- :index => index,
73
- :prefix => 'has non-Array'
74
- )
75
- record.errors.add attribute, error
76
- end
77
- end
78
- else
79
- record.errors.add attribute, "is not an Array. #{TYPE_SIGNATURE_SENTENCE}"
80
- end
81
- end
82
-
83
- private
84
-
85
- def error_at(options={})
86
- options.assert_valid_keys(:element, :index, :prefix)
87
- prefix = options.fetch(:prefix)
88
-
89
- clause = location_clause(
90
- :element => options[:element],
91
- :index => options[:index]
92
- )
93
- sentence = "#{prefix} #{clause}."
94
-
95
- sentences = [
96
- sentence,
97
- TYPE_SIGNATURE_SENTENCE
98
- ]
99
-
100
- error = sentences.join(" ")
101
-
102
- error
103
- end
104
-
105
- def length_error_at(options={})
106
- options.assert_valid_keys(:element, :extreme, :index)
107
- extreme = options.fetch(:extreme)
108
-
109
- prefix = "has too #{extreme} elements"
110
- error = error_at(
111
- :element => options[:element],
112
- :index => options[:index],
113
- :prefix => prefix
114
- )
115
-
116
- error
117
- end
118
-
119
- def location_clause(options={})
120
- options.assert_valid_keys(:element, :index)
121
-
122
- element = options.fetch(:element)
123
- index = options.fetch(:index)
124
-
125
- clause = "at index #{index} (#{element.inspect})"
126
-
127
- clause
128
- end
129
- end
@@ -1,117 +0,0 @@
1
- # Validates that
2
- class PasswordIsStrongValidator < ActiveModel::EachValidator
3
- #
4
- # CONSTANTS
5
- #
6
-
7
- # Known passwords that should NOT be allowed and should be considered weak.
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
- # 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.
19
- def validate_each(record, attribute, value)
20
- return if value.blank?
21
-
22
- if is_simple?(value)
23
- record.errors[attribute] << "must contain letters, numbers, and at least one special character"
24
- end
25
-
26
- if contains_username?(record.username, value)
27
- record.errors[attribute] << "must not contain the username"
28
- end
29
-
30
- if is_common_password?(value)
31
- record.errors[attribute] << "must not be a common password"
32
- end
33
-
34
- if contains_repetition?(value)
35
- record.errors[attribute] << "must not be a predictable sequence of characters"
36
- end
37
- end
38
-
39
- private
40
-
41
- def is_simple?(password)
42
- not (password =~ /[A-Za-z]/ and password =~ /[0-9]/ and password =~ /[#{Regexp.escape(SPECIAL_CHARS)}]/)
43
- end
44
-
45
- def contains_username?(username, password)
46
- !!(password =~ /#{username}/i)
47
- end
48
-
49
- def is_common_password?(password)
50
- COMMON_PASSWORDS.each do |pw|
51
- common_pw = [pw] # pw + "!", pw + "1", pw + "12", pw + "123", pw + "1234"]
52
- common_pw += mutate_pass(pw)
53
- common_pw.each do |common_pass|
54
- if password.downcase =~ /#{common_pass}[\d!]*/
55
- return true
56
- end
57
- end
58
- end
59
- false
60
- end
61
-
62
- def mutate_pass(password)
63
- mutations = {
64
- 'a' => '@',
65
- 'o' => '0',
66
- 'e' => '3',
67
- 's' => '$',
68
- 't' => '7',
69
- 'l' => '1'
70
- }
71
-
72
- iterations = mutations.keys.dup
73
- results = []
74
-
75
- # Find PowerSet of all possible mutation combinations
76
- iterations = iterations.inject([[]]){|c,y|r=[];c.each{|i|r<<i;r<<i+[y]};r}
77
-
78
- # Iterate through combinations to create each possible mutation
79
- iterations.each do |iteration|
80
- next if iteration.flatten.empty?
81
- first = iteration.shift
82
- intermediate = password.gsub(/#{first}/i, mutations[first])
83
- iteration.each do |mutator|
84
- next unless mutator.kind_of? String
85
- intermediate.gsub!(/#{mutator}/i, mutations[mutator])
86
- end
87
- results << intermediate
88
- end
89
-
90
- return results
91
- end
92
-
93
-
94
-
95
- def contains_repetition?(password)
96
- # Password repetition (quite basic) -- no "aaaaaa" or "ababab" or "abcabc" or
97
- # "abcdabcd" (but note that the user can use "aaaaaab" or something).
98
-
99
- if password.scan(/./).uniq.size < 2
100
- return true
101
- end
102
-
103
- if (password.size % 2 == 0) and (password.scan(/../).uniq.size < 2)
104
- return true
105
- end
106
-
107
- if (password.size % 3 == 0) and (password.scan(/.../).uniq.size < 2)
108
- return true
109
- end
110
-
111
- if (password.size % 4 == 0) and (password.scan(/..../).uniq.size < 2)
112
- return true
113
- end
114
-
115
- false
116
- end
117
- end
@@ -1,68 +0,0 @@
1
- #! /usr/bin/env ruby
2
-
3
- def mdm_banner
4
- banner = {}
5
- banner[:color] = "\e[34m"
6
- banner[:text] = <<-BANNER
7
- _______ _______________________ _______ _______ _ _______ __________________
8
- ( ) ____ \\__ __/ ___ ) ____ \\ ____ ) \\ ( ___ )\\__ __/\\__ __/
9
- | () () | ( \\/ ) ( | ( ) | ( \\/ ( )| ( | ( ) | ) ( ) (
10
- | || || | (__ | | | (___) | (_____| (____)| | | | | | | | | |
11
- | |(_)| | __) | | | ___ |_____ ) _____) | | | | | | | | |
12
- | | | | ( | | | ( ) | ) | ( | | | | | | | | | |
13
- | ) ( | (____/\\ | | | ) ( |\\____) | ) | (____/\\ (___) |___) (___ | |
14
- |/ \\|_______/ )_( |/ \\|_______)/ (_______/_______)\\_______/ )_(
15
-
16
-
17
- ______ _______________________ _______ _______ ______ _______ _ _______
18
- ( __ \\( ___ )__ __/ ___ ) ) ___ ) __ \\( ____ \\ \\ ( ____ \\
19
- | ( \\ ) ( ) | ) ( | ( ) | () () | ( ) | ( \\ ) ( \\/ ( | ( \\/
20
- | | ) | (___) | | | | (___) | || || | | | | | ) | (__ | | | (_____
21
- | | | | ___ | | | | ___ | |(_)| | | | | | | | __) | | (_____ )
22
- | | ) | ( ) | | | | ( ) | | | | | | | | ) | ( | | ) |
23
- | (__/ ) ) ( | | | | ) ( | ) ( | (___) | (__/ ) (____/\\ (____/Y\\____) |
24
- (______/|/ \\| )_( |/ \\|/ \\|_______)______/(_______/_______|_______)
25
- BANNER
26
- banner
27
- end
28
-
29
- def db_info_file
30
- hidden_file = "#{Dir.home}/.mdm.yml"
31
- if File.readable?(hidden_file)
32
- pro_path = YAML.load_file(hidden_file)['pro_path']
33
- return "#{pro_path}/ui/config/database.yml"
34
- elsif !ARGV[0].blank?
35
- return ARGV[0]
36
- else
37
- warn "No YAML file of DB info available"
38
- exit
39
- end
40
- end
41
-
42
- begin
43
- require 'pry'
44
- require "metasploit_data_models"
45
-
46
- # Set up a DB connection, preferring one from Pro if it's in the normal place
47
- # Otherwise get from ARGV[0]
48
-
49
- if File.readable?(db_info_file)
50
- connection_info = YAML.load_file(db_info_file)
51
- ActiveRecord::Base.establish_connection(connection_info['development'])
52
- else
53
- warn "Can't access DB -- check file path."
54
- exit
55
- end
56
-
57
- MetasploitDataModels.require_models
58
-
59
- puts "\n\n\n#{mdm_banner[:color]}#{mdm_banner[:text]}\e[0m\n\n\n"
60
-
61
- Pry.config.prompt = proc { |obj, nest_level, _| "mdm:#{nest_level}> " }
62
-
63
- Pry.start
64
- exit
65
- rescue LoadError
66
- warn "Unable to load Pry"
67
- end
68
-
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_data_models/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'
@@ -1,342 +0,0 @@
1
- RSpec.describe ParametersValidator do
2
- subject(:parameters_validator) do
3
- described_class.new(
4
- :attributes => attributes
5
- )
6
- end
7
-
8
- let(:attribute) do
9
- :params
10
- end
11
-
12
- let(:attributes) do
13
- attribute
14
- end
15
-
16
- let(:element) do
17
- []
18
- end
19
-
20
- let(:index) do
21
- rand(100)
22
- end
23
-
24
- let(:type_signature_sentence) do
25
- 'Valid parameters are an Array<Array(String, String)>.'
26
- end
27
-
28
- context 'CONSTANTS' do
29
- it 'should define TYPE_SIGNATURE_SENTENCE' do
30
- expect(described_class::TYPE_SIGNATURE_SENTENCE).to eq(type_signature_sentence)
31
- end
32
- end
33
-
34
- context '#error_at' do
35
- subject(:error_at) do
36
- parameters_validator.send(
37
- :error_at,
38
- :element => element,
39
- :index => index,
40
- :prefix => prefix
41
- )
42
- end
43
-
44
- let(:prefix) do
45
- 'has a prefix'
46
- end
47
-
48
- it 'should include prefix' do
49
- expect(error_at).to include(prefix)
50
- end
51
-
52
- it 'should include location_clause in same sentence as prefix' do
53
- location_clause = parameters_validator.send(
54
- :location_clause,
55
- :element => element,
56
- :index => index
57
- )
58
-
59
- expect(error_at).to include("#{prefix} #{location_clause}.")
60
- end
61
-
62
- it 'should include TYPE_SIGNATURE_SENTENCE' do
63
- expect(error_at).to include(type_signature_sentence)
64
- end
65
- end
66
-
67
- context '#length_error_at' do
68
- subject(:length_error_at) do
69
- parameters_validator.send(
70
- :length_error_at,
71
- :element => element,
72
- :extreme => extreme,
73
- :index => index
74
- )
75
- end
76
-
77
- let(:extreme) do
78
- [:few, :many].sample
79
- end
80
-
81
- it 'should include extreme in prefix' do
82
- expect(parameters_validator).to receive(:error_at) do |*args|
83
- options = args.first
84
- expect(options[:prefix]).to include(extreme.to_s)
85
- end
86
-
87
- length_error_at
88
- end
89
- end
90
-
91
- context '#location_clause' do
92
- subject(:location_clause) do
93
- parameters_validator.send(
94
- :location_clause,
95
- :element => element,
96
- :index => index
97
- )
98
- end
99
-
100
- it 'should include numerical index' do
101
- expect(location_clause).to include("at index #{index}")
102
- end
103
-
104
- it 'should include inspect of element' do
105
- expect(location_clause).to include(element.inspect)
106
- end
107
- end
108
-
109
- context '#validate_each' do
110
- subject(:errors) do
111
- record.errors[attribute]
112
- end
113
-
114
- def validate_each
115
- parameters_validator.validate_each(record, attribute, value)
116
- end
117
-
118
- let(:record) do
119
- Object.new.tap { |object|
120
- object.extend ActiveModel::Validations
121
- }
122
- end
123
-
124
- context 'with Array' do
125
- let(:value) do
126
- []
127
- end
128
-
129
- context 'element' do
130
- let(:value) do
131
- [element]
132
- end
133
-
134
- context 'with Array' do
135
- let(:element) do
136
- []
137
- end
138
-
139
- context 'with length < 2' do
140
- let(:element) do
141
- []
142
- end
143
-
144
- it 'should call #length_error_at with :extreme => :few' do
145
- expect(parameters_validator).to receive(:length_error_at).with(
146
- hash_including(
147
- :extreme => :few
148
- )
149
- )
150
-
151
- validate_each
152
- end
153
-
154
- it 'should record error' do
155
- validate_each
156
-
157
- expect(errors).not_to be_empty
158
- end
159
- end
160
-
161
- context 'with length > 2' do
162
- let(:element) do
163
- ['', '', '']
164
- end
165
-
166
- it 'should call #length_error_at with :extreme => :many' do
167
- expect(parameters_validator).to receive(:length_error_at).with(
168
- hash_including(
169
- :extreme => :many
170
- )
171
- )
172
-
173
- validate_each
174
- end
175
-
176
- it 'should record error' do
177
- validate_each
178
-
179
- expect(errors).not_to be_empty
180
- end
181
- end
182
-
183
- context 'with length == 2' do
184
- let(:element) do
185
- [parameter_name, parameter_value]
186
- end
187
-
188
- let(:parameter_name) do
189
- 'parameter_name'
190
- end
191
-
192
- let(:parameter_value) do
193
- 'parameter_value'
194
- end
195
-
196
- context 'parameter name' do
197
- context 'with String' do
198
- context 'with blank' do
199
- let(:parameter_name) do
200
- ''
201
- end
202
-
203
- it 'should call error_at with blank parameter name prefix' do
204
- expect(parameters_validator).to receive(:error_at).with(
205
- hash_including(
206
- :prefix => 'has blank parameter name'
207
- )
208
- )
209
-
210
- validate_each
211
- end
212
-
213
- it 'should record error' do
214
- validate_each
215
-
216
- expect(errors).not_to be_empty
217
- end
218
- end
219
-
220
- context 'without blank' do
221
- let(:parameter_name) do
222
- 'parameter_name'
223
- end
224
-
225
- it 'should not record error' do
226
- validate_each
227
-
228
- expect(errors).to be_blank
229
- end
230
- end
231
- end
232
-
233
- context 'without String' do
234
- let(:parameter_name) do
235
- :parameter_name
236
- end
237
-
238
- it 'should call error_at with non-String prefix' do
239
- expect(parameters_validator).to receive(:error_at).with(
240
- hash_including(
241
- :prefix => "has non-String parameter name (#{parameter_name.inspect})"
242
- )
243
- )
244
-
245
- validate_each
246
- end
247
-
248
- it 'should record error' do
249
- validate_each
250
-
251
- expect(errors).not_to be_empty
252
- end
253
- end
254
- end
255
-
256
- context 'parameter value' do
257
- context 'with String' do
258
- let(:parameter_value) do
259
- 'parameter_value'
260
- end
261
-
262
- it 'should not record error' do
263
- validate_each
264
-
265
- expect(errors).to be_blank
266
- end
267
- end
268
-
269
- context 'without String' do
270
- let(:parameter_value) do
271
- 0
272
- end
273
-
274
- it 'should call error_at with non-String prefix' do
275
- expect(parameters_validator).to receive(:error_at).with(
276
- hash_including(
277
- :prefix => "has non-String parameter value (#{parameter_value.inspect})"
278
- )
279
- )
280
-
281
- validate_each
282
- end
283
-
284
- it 'should record error' do
285
- validate_each
286
-
287
- expect(errors).not_to be_empty
288
- end
289
- end
290
- end
291
- end
292
- end
293
-
294
- context 'without Array' do
295
- let(:element) do
296
- {}
297
- end
298
-
299
- it 'should use #error_at with has non-Array for prefix' do
300
- expect(parameters_validator).to receive(:error_at).with(
301
- hash_including(
302
- :prefix => 'has non-Array'
303
- )
304
- )
305
-
306
- validate_each
307
- end
308
-
309
- it 'should record error' do
310
- validate_each
311
-
312
- expect(errors).not_to be_empty
313
- end
314
- end
315
- end
316
- end
317
-
318
- context 'without Array' do
319
- let(:value) do
320
- ''
321
- end
322
-
323
- before(:example) do
324
- validate_each
325
- end
326
-
327
- it 'should error that attribute is not an array' do
328
- expect(
329
- errors.any? { |error|
330
- error.include? 'is not an Array.'
331
- }
332
- ).to eq(true)
333
- end
334
-
335
- it 'should include TYPE_SIGNATURE_SENTENCE' do
336
- errors.each do |error|
337
- expect(error).to include(type_signature_sentence)
338
- end
339
- end
340
- end
341
- end
342
- end
@@ -1,332 +0,0 @@
1
- RSpec.describe PasswordIsStrongValidator do
2
-
3
- subject(:password_validator) do
4
- described_class.new(
5
- :attributes => attributes
6
- )
7
- end
8
-
9
- let(:attribute) do
10
- :params
11
- end
12
-
13
- let(:attributes) do
14
- attribute
15
- end
16
-
17
-
18
- context '#contains_repetition?' do
19
-
20
- it 'should return true for aaaa' do
21
- expect(password_validator.send(:contains_repetition?, 'aaaa')).to eq(true)
22
- end
23
-
24
- it 'should return true for ababab' do
25
- expect(password_validator.send(:contains_repetition?, 'ababab')).to eq(true)
26
- end
27
-
28
- it 'should return true for abcabcabc' do
29
- expect(password_validator.send(:contains_repetition?, 'abcabcabc')).to eq(true)
30
- end
31
-
32
- it 'should return true for abcdabcd' do
33
- expect(password_validator.send(:contains_repetition?, 'abcdabcd')).to eq(true)
34
- end
35
-
36
- it 'should return false for abcd1234abcd' do
37
- expect(password_validator.send(:contains_repetition?, 'abcd1234abcd')).to eq(false)
38
- end
39
-
40
- end
41
-
42
-
43
-
44
- context '#mutate_pass' do
45
-
46
- variants = [
47
- "metasp1oit",
48
- "me7asploi7",
49
- "me7asp1oi7",
50
- "meta$ploit",
51
- "meta$p1oit",
52
- "me7a$ploi7",
53
- "me7a$p1oi7",
54
- "m3tasploit",
55
- "m3tasp1oit",
56
- "m37asploi7",
57
- "m37asp1oi7",
58
- "m3ta$ploit",
59
- "m3ta$p1oit",
60
- "m37a$ploi7",
61
- "m37a$p1oi7",
62
- "metaspl0it",
63
- "metasp10it",
64
- "me7aspl0i7",
65
- "me7asp10i7",
66
- "meta$pl0it",
67
- "meta$p10it",
68
- "me7a$pl0i7",
69
- "me7a$p10i7",
70
- "m3taspl0it",
71
- "m3tasp10it",
72
- "m37aspl0i7",
73
- "m37asp10i7",
74
- "m3ta$pl0it",
75
- "m3ta$p10it",
76
- "m37a$pl0i7",
77
- "m37a$p10i7",
78
- "met@sploit",
79
- "met@sp1oit",
80
- "me7@sploi7",
81
- "me7@sp1oi7",
82
- "met@$ploit",
83
- "met@$p1oit",
84
- "me7@$ploi7",
85
- "me7@$p1oi7",
86
- "m3t@sploit",
87
- "m3t@sp1oit",
88
- "m37@sploi7",
89
- "m37@sp1oi7",
90
- "m3t@$ploit",
91
- "m3t@$p1oit",
92
- "m37@$ploi7",
93
- "m37@$p1oi7",
94
- "met@spl0it",
95
- "met@sp10it",
96
- "me7@spl0i7",
97
- "me7@sp10i7",
98
- "met@$pl0it",
99
- "met@$p10it",
100
- "me7@$pl0i7",
101
- "me7@$p10i7",
102
- "m3t@spl0it",
103
- "m3t@sp10it",
104
- "m37@spl0i7",
105
- "m37@sp10i7",
106
- "m3t@$pl0it",
107
- "m3t@$p10it",
108
- "m37@$pl0i7",
109
- "m37@$p10i7"
110
- ]
111
-
112
- it 'should return all the expected mutations of a password' do
113
- expect(password_validator.send(:mutate_pass, 'metasploit')).to eq(variants)
114
- end
115
-
116
- end
117
-
118
-
119
- context '#is_common_password?' do
120
-
121
- PasswordIsStrongValidator::COMMON_PASSWORDS.each do |password|
122
-
123
- it "should return true for #{password}" do
124
- expect(password_validator.send(:is_common_password?, password)).to eq(true)
125
- end
126
-
127
- it "should return true for #{password}!" do
128
- expect(password_validator.send(:is_common_password?, "#{password}!")).to eq(true)
129
- end
130
-
131
- it "should return true for #{password}1" do
132
- expect(password_validator.send(:is_common_password?, "#{password}1")).to eq(true)
133
- end
134
-
135
- it "should return true for #{password}9" do
136
- expect(password_validator.send(:is_common_password?, "#{password}1")).to eq(true)
137
- end
138
-
139
- it "should return true for #{password}99" do
140
- expect(password_validator.send(:is_common_password?, "#{password}12")).to eq(true)
141
- end
142
-
143
- it "should return true for #{password}123" do
144
- expect(password_validator.send(:is_common_password?, "#{password}123")).to eq(true)
145
- end
146
-
147
- it "should return true for #{password}123!" do
148
- expect(password_validator.send(:is_common_password?, "#{password}123!")).to eq(true)
149
- end
150
-
151
- end
152
-
153
- it "should return true for r00t" do
154
- expect(password_validator.send(:is_common_password?, "r00t")).to eq(true)
155
- end
156
-
157
- it "should return true for m3t@spl0it" do
158
- expect(password_validator.send(:is_common_password?, "m3t@spl0it")).to eq(true)
159
- end
160
-
161
- it "should return true for m3t@spl0it123!" do
162
- expect(password_validator.send(:is_common_password?, "m3t@spl0it123!")).to eq(true)
163
- end
164
- end
165
-
166
- context '#contains_username' do
167
-
168
- it 'should return true if username and password are the same' do
169
- expect(password_validator.send(:contains_username?, 'admin', 'admin')).to eq(true)
170
- end
171
-
172
- it 'should return true if the password contains the username as part of it' do
173
- expect(password_validator.send(:contains_username?, 'admin', '123admin123')).to eq(true)
174
- end
175
-
176
- it 'should return false otherwise' do
177
- expect(password_validator.send(:contains_username?, 'admin', 'foobar')).to eq(false)
178
- end
179
- end
180
-
181
- context '#is_simple?' do
182
-
183
- it "should return true if no number" do
184
- expect(password_validator.send(:is_simple?, "b@carat")).to eq(true)
185
- end
186
-
187
- it "should return true if no special char" do
188
- expect(password_validator.send(:is_simple?, "bacarat4")).to eq(true)
189
- end
190
-
191
- it "should return true if no letters" do
192
- expect(password_validator.send(:is_simple?, "1337")).to eq(true)
193
- end
194
-
195
- PasswordIsStrongValidator::SPECIAL_CHARS.each_char do |char|
196
-
197
- it "should return false with a #{char}" do
198
- expect(password_validator.send(:is_simple?, "bacarat4#{char}")).to eq(false)
199
- end
200
- end
201
- end
202
-
203
- context '#validate_each' do
204
-
205
- subject(:errors) do
206
- record.errors[attribute]
207
- end
208
-
209
- def validate_each
210
- password_validator.validate_each(record, attribute, value)
211
- end
212
-
213
- let(:record) {
214
- record_class.new.tap { |instance|
215
- instance.username = 'admin'
216
- }
217
- }
218
-
219
- let(:record_class) {
220
- Class.new do
221
- include ActiveModel::Validations
222
-
223
- #
224
- # Attributes
225
- #
226
-
227
- attr_accessor :username
228
- end
229
- }
230
-
231
-
232
- context 'with a password with no special char' do
233
- let(:value) { "bacarat4" }
234
-
235
- it 'should record an error' do
236
- validate_each
237
- expect(errors).not_to be_empty
238
- end
239
-
240
- it 'should have an error of "must contain letters, numbers, and at least one special character"' do
241
- validate_each
242
- expect(errors.include?("must contain letters, numbers, and at least one special character")).to eq(true)
243
- end
244
- end
245
-
246
- context 'with a password with no numbers' do
247
- let(:value) { "b@carat" }
248
-
249
- it 'should record an error' do
250
- validate_each
251
- expect(errors).not_to be_empty
252
- end
253
-
254
- it 'should have an error of "must contain letters, numbers, and at least one special character"' do
255
- validate_each
256
- expect(errors.include?("must contain letters, numbers, and at least one special character")).to eq(true)
257
- end
258
- end
259
-
260
- context 'with a password with no letters' do
261
- let(:value) { "1337@" }
262
-
263
- it 'should record an error' do
264
- validate_each
265
- expect(errors).not_to be_empty
266
- end
267
-
268
- it 'should have an error of "must contain letters, numbers, and at least one special character"' do
269
- validate_each
270
- expect(errors.include?("must contain letters, numbers, and at least one special character")).to eq(true)
271
- end
272
- end
273
-
274
- context 'with a password containing the username' do
275
- let(:value) { "admin1" }
276
-
277
- it 'should record an error' do
278
- validate_each
279
- expect(errors).not_to be_empty
280
- end
281
-
282
- it 'should have an error of "must not contain the username"' do
283
- validate_each
284
- expect(errors.include?("must not contain the username")).to eq(true)
285
- end
286
- end
287
-
288
- context 'with a common password' do
289
- let(:value) { "password" }
290
-
291
- it 'should record an error' do
292
- validate_each
293
- expect(errors).not_to be_empty
294
- end
295
-
296
- it 'should have an error of "must not be a common password"' do
297
- validate_each
298
- expect(errors.include?("must not be a common password")).to eq(true)
299
- end
300
- end
301
-
302
- context 'with a mutated common password' do
303
- let(:value) { "P@ssw0rd1!" }
304
-
305
- it 'should record an error' do
306
- validate_each
307
- expect(errors).not_to be_empty
308
- end
309
-
310
- it 'should have an error of "must not be a common password"' do
311
- validate_each
312
- expect(errors.include?("must not be a common password")).to eq(true)
313
- end
314
- end
315
-
316
- context 'with a repeated pattern' do
317
- let(:value) { "abcdabcd" }
318
-
319
- it 'should record an error' do
320
- validate_each
321
- expect(errors).not_to be_empty
322
- end
323
-
324
- it 'should have an error of "must not be a predictable sequence of characters"' do
325
- validate_each
326
- expect(errors.include?("must not be a predictable sequence of characters")).to eq(true)
327
- end
328
- end
329
-
330
- end
331
-
332
- end