inspec-core 2.2.78 → 2.2.101
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -14
- data/docs/profiles.md +106 -8
- data/examples/inheritance/inspec.yml +2 -1
- data/examples/profile/controls/gordon.rb +1 -1
- data/examples/profile/controls/meta.rb +2 -0
- data/examples/profile/inspec.yml +2 -1
- data/inspec-core.gemspec +2 -1
- data/lib/bundles/inspec-compliance/cli.rb +13 -1
- data/lib/bundles/inspec-compliance/http.rb +9 -18
- data/lib/bundles/inspec-compliance/target.rb +3 -3
- data/lib/fetchers/local.rb +60 -17
- data/lib/inspec.rb +4 -0
- data/lib/inspec/attribute_registry.rb +83 -0
- data/lib/inspec/base_cli.rb +10 -1
- data/lib/inspec/cli.rb +12 -1
- data/lib/inspec/control_eval_context.rb +13 -4
- data/lib/inspec/dependencies/cache.rb +1 -1
- data/lib/inspec/dependencies/dependency_set.rb +1 -1
- data/lib/inspec/dependencies/requirement.rb +2 -1
- data/lib/inspec/errors.rb +27 -0
- data/lib/inspec/file_provider.rb +38 -1
- data/lib/inspec/globals.rb +5 -0
- data/lib/inspec/impact.rb +34 -0
- data/lib/inspec/objects/attribute.rb +92 -7
- data/lib/inspec/profile.rb +33 -4
- data/lib/inspec/profile_context.rb +7 -7
- data/lib/inspec/profile_vendor.rb +21 -1
- data/lib/inspec/reporters/automate.rb +7 -2
- data/lib/inspec/reporters/cli.rb +12 -4
- data/lib/inspec/reporters/json.rb +3 -1
- data/lib/inspec/rspec_extensions.rb +12 -0
- data/lib/inspec/rule.rb +6 -1
- data/lib/inspec/runner.rb +2 -2
- data/lib/inspec/schema.rb +16 -2
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/mysql_session.rb +1 -0
- metadata +22 -4
data/lib/inspec.rb
CHANGED
@@ -15,6 +15,10 @@ require 'inspec/runner'
|
|
15
15
|
require 'inspec/shell'
|
16
16
|
require 'inspec/formatters'
|
17
17
|
require 'inspec/reporters'
|
18
|
+
require 'inspec/attribute_registry'
|
19
|
+
require 'inspec/rspec_extensions'
|
20
|
+
require 'inspec/globals'
|
21
|
+
require 'inspec/impact'
|
18
22
|
|
19
23
|
require 'inspec/plugin/v2'
|
20
24
|
require 'inspec/plugin/v1'
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'singleton'
|
3
|
+
require 'inspec/objects/attribute'
|
4
|
+
|
5
|
+
module Inspec
|
6
|
+
class AttributeRegistry
|
7
|
+
include Singleton
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
attr_reader :list
|
11
|
+
def_delegator :list, :each
|
12
|
+
def_delegator :list, :[]
|
13
|
+
def_delegator :list, :key?, :profile_exist?
|
14
|
+
def_delegator :list, :select
|
15
|
+
|
16
|
+
# These self methods are convenience methods so you dont always
|
17
|
+
# have to specify instance when calling the registry
|
18
|
+
def self.find_attribute(name, profile)
|
19
|
+
instance.find_attribute(name, profile)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.register_attribute(name, profile, options = {})
|
23
|
+
instance.register_attribute(name, profile, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.register_profile_alias(name, alias_name)
|
27
|
+
instance.register_profile_alias(name, alias_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.list_attributes_for_profile(profile)
|
31
|
+
instance.list_attributes_for_profile(profile)
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
# this is a collection of profiles which have a value of attribute objects
|
36
|
+
@list = {}
|
37
|
+
|
38
|
+
# this is a list of optional profile name overrides set in the inspec.yml
|
39
|
+
@profile_aliases = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_attribute(name, profile)
|
43
|
+
profile = @profile_aliases[profile] if !profile_exist?(profile) && @profile_aliases[profile]
|
44
|
+
unless profile_exist?(profile)
|
45
|
+
error = Inspec::AttributeRegistry::ProfileError.new
|
46
|
+
error.profile_name = profile
|
47
|
+
raise error, "Profile '#{error.profile_name}' does not have any attributes"
|
48
|
+
end
|
49
|
+
|
50
|
+
unless list[profile].key?(name)
|
51
|
+
error = Inspec::AttributeRegistry::AttributeError.new
|
52
|
+
error.attribute_name = name
|
53
|
+
error.profile_name = profile
|
54
|
+
raise error, "Profile '#{error.profile_name}' does not have a attribute with name '#{error.attribute_name}'"
|
55
|
+
end
|
56
|
+
list[profile][name]
|
57
|
+
end
|
58
|
+
|
59
|
+
def register_attribute(name, profile, options = {})
|
60
|
+
# check for a profile override name
|
61
|
+
if profile_exist?(profile) && list[profile][name] && options.empty?
|
62
|
+
list[profile][name]
|
63
|
+
else
|
64
|
+
list[profile] = {} unless profile_exist?(profile)
|
65
|
+
list[profile][name] = Inspec::Attribute.new(name, options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def register_profile_alias(name, alias_name)
|
70
|
+
@profile_aliases[name] = alias_name
|
71
|
+
end
|
72
|
+
|
73
|
+
def list_attributes_for_profile(profile)
|
74
|
+
list[profile] = {} unless profile_exist?(profile)
|
75
|
+
list[profile]
|
76
|
+
end
|
77
|
+
|
78
|
+
def __reset
|
79
|
+
@list = {}
|
80
|
+
@profile_aliases = {}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -8,6 +8,10 @@ require 'inspec/profile_vendor'
|
|
8
8
|
|
9
9
|
module Inspec
|
10
10
|
class BaseCLI < Thor
|
11
|
+
class << self
|
12
|
+
attr_accessor :inspec_cli_command
|
13
|
+
end
|
14
|
+
|
11
15
|
# https://github.com/erikhuda/thor/issues/244
|
12
16
|
def self.exit_on_failure?
|
13
17
|
true
|
@@ -62,6 +66,8 @@ module Inspec
|
|
62
66
|
desc: 'Specifies the bastion port if applicable'
|
63
67
|
option :insecure, type: :boolean, default: false,
|
64
68
|
desc: 'Disable SSL verification on select targets'
|
69
|
+
option :target_id, type: :string,
|
70
|
+
desc: 'Provide a ID which will be included on reports'
|
65
71
|
end
|
66
72
|
|
67
73
|
def self.profile_options
|
@@ -134,7 +140,7 @@ module Inspec
|
|
134
140
|
if opts['reporter'].is_a?(Array)
|
135
141
|
reports = {}
|
136
142
|
opts['reporter'].each do |report|
|
137
|
-
reporter_name, target = report.split(':')
|
143
|
+
reporter_name, target = report.split(':', 2)
|
138
144
|
if target.nil? || target.strip == '-'
|
139
145
|
reports[reporter_name] = { 'stdout' => true }
|
140
146
|
else
|
@@ -142,6 +148,7 @@ module Inspec
|
|
142
148
|
'file' => target,
|
143
149
|
'stdout' => false,
|
144
150
|
}
|
151
|
+
reports[reporter_name]['target_id'] = opts['target_id'] if opts['target_id']
|
145
152
|
end
|
146
153
|
end
|
147
154
|
opts['reporter'] = reports
|
@@ -152,6 +159,7 @@ module Inspec
|
|
152
159
|
opts['reporter'].each do |reporter_name, config|
|
153
160
|
opts['reporter'][reporter_name] = {} if config.nil?
|
154
161
|
opts['reporter'][reporter_name]['stdout'] = true if opts['reporter'][reporter_name].empty?
|
162
|
+
opts['reporter'][reporter_name]['target_id'] = opts['target_id'] if opts['target_id']
|
155
163
|
end
|
156
164
|
end
|
157
165
|
|
@@ -295,6 +303,7 @@ module Inspec
|
|
295
303
|
# start with default options if we have any
|
296
304
|
opts = BaseCLI.default_options[type] unless type.nil? || BaseCLI.default_options[type].nil?
|
297
305
|
opts['type'] = type unless type.nil?
|
306
|
+
Inspec::BaseCLI.inspec_cli_command = type
|
298
307
|
|
299
308
|
# merge in any options from json-config
|
300
309
|
json_config = options_json
|
data/lib/inspec/cli.rb
CHANGED
@@ -35,6 +35,9 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
35
35
|
def json(target)
|
36
36
|
o = opts.dup
|
37
37
|
diagnose(o)
|
38
|
+
o['log_location'] = STDERR
|
39
|
+
configure_logger(o)
|
40
|
+
|
38
41
|
o[:backend] = Inspec::Backend.create(target: 'mock://')
|
39
42
|
o[:check_mode] = true
|
40
43
|
o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
|
@@ -119,6 +122,10 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
119
122
|
desc: 'Overwrite existing vendored dependencies and lockfile.'
|
120
123
|
def vendor(path = nil)
|
121
124
|
o = opts.dup
|
125
|
+
configure_logger(o)
|
126
|
+
o[:logger] = Logger.new(STDOUT)
|
127
|
+
o[:logger].level = get_log_level(o.log_level)
|
128
|
+
|
122
129
|
vendor_deps(path, o)
|
123
130
|
end
|
124
131
|
|
@@ -141,7 +148,11 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
141
148
|
o[:logger] = Logger.new(STDOUT)
|
142
149
|
o[:logger].level = get_log_level(o.log_level)
|
143
150
|
o[:backend] = Inspec::Backend.create(target: 'mock://')
|
144
|
-
|
151
|
+
|
152
|
+
# Force vendoring with overwrite when archiving
|
153
|
+
vendor_options = o.dup
|
154
|
+
vendor_options[:overwrite] = true
|
155
|
+
vendor_deps(path, vendor_options)
|
145
156
|
|
146
157
|
profile = Inspec::Profile.for_target(path, o)
|
147
158
|
result = profile.check
|
@@ -19,11 +19,16 @@ module Inspec
|
|
19
19
|
#
|
20
20
|
# @param [ResourcesDSL] resources_dsl which has all resources to attach
|
21
21
|
# @return [RuleContext] the inner context of rules
|
22
|
-
def self.rule_context(resources_dsl)
|
22
|
+
def self.rule_context(resources_dsl, profile_id)
|
23
23
|
require 'rspec/core/dsl'
|
24
24
|
Class.new(Inspec::Rule) do
|
25
25
|
include RSpec::Core::DSL
|
26
26
|
with_resource_dsl resources_dsl
|
27
|
+
|
28
|
+
# allow attributes to be accessed within control blocks
|
29
|
+
define_method :attribute do |name|
|
30
|
+
Inspec::AttributeRegistry.find_attribute(name, profile_id).value
|
31
|
+
end
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
@@ -36,9 +41,9 @@ module Inspec
|
|
36
41
|
# @param outer_dsl [OuterDSLClass]
|
37
42
|
# @return [ProfileContextClass]
|
38
43
|
def self.create(profile_context, resources_dsl) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
39
|
-
rule_class = rule_context(resources_dsl)
|
40
44
|
profile_context_owner = profile_context
|
41
45
|
profile_id = profile_context.profile_id
|
46
|
+
rule_class = rule_context(resources_dsl, profile_id)
|
42
47
|
|
43
48
|
Class.new do # rubocop:disable Metrics/BlockLength
|
44
49
|
include Inspec::DSL
|
@@ -137,8 +142,12 @@ module Inspec
|
|
137
142
|
end
|
138
143
|
|
139
144
|
# method for attributes; import attribute handling
|
140
|
-
define_method :attribute do |name, options|
|
141
|
-
|
145
|
+
define_method :attribute do |name, options = {}|
|
146
|
+
if options.empty?
|
147
|
+
Inspec::AttributeRegistry.find_attribute(name, profile_id).value
|
148
|
+
else
|
149
|
+
profile_context_owner.register_attribute(name, options)
|
150
|
+
end
|
142
151
|
end
|
143
152
|
|
144
153
|
define_method :skip_control do |id|
|
@@ -18,7 +18,7 @@ module Inspec
|
|
18
18
|
class Cache
|
19
19
|
attr_reader :path
|
20
20
|
def initialize(path = nil)
|
21
|
-
@path = path || File.join(
|
21
|
+
@path = path || File.join(Inspec.config_dir, 'cache')
|
22
22
|
FileUtils.mkdir_p(@path) unless File.directory?(@path)
|
23
23
|
end
|
24
24
|
|
@@ -121,8 +121,9 @@ module Inspec
|
|
121
121
|
if !@dependencies.nil? && !@dependencies.empty?
|
122
122
|
opts[:dependencies] = Inspec::DependencySet.from_array(@dependencies, @cwd, @cache, @backend)
|
123
123
|
end
|
124
|
+
opts[:profile_name] = @name
|
125
|
+
opts[:parent_profile] = @parent_profile
|
124
126
|
@profile = Inspec::Profile.for_fetcher(fetcher, opts)
|
125
|
-
@profile.parent_profile = @parent_profile
|
126
127
|
@profile
|
127
128
|
end
|
128
129
|
end
|
data/lib/inspec/errors.rb
CHANGED
@@ -11,4 +11,31 @@ module Inspec
|
|
11
11
|
class DuplicateDep < Error; end
|
12
12
|
class FetcherFailure < Error; end
|
13
13
|
class ReporterError < Error; end
|
14
|
+
class ImpactError < Error; end
|
15
|
+
|
16
|
+
class Attribute
|
17
|
+
class Error < Inspec::Error; end
|
18
|
+
class ValidationError < Error
|
19
|
+
attr_accessor :attribute_name
|
20
|
+
attr_accessor :attribute_value
|
21
|
+
attr_accessor :attribute_type
|
22
|
+
end
|
23
|
+
class TypeError < Error
|
24
|
+
attr_accessor :attribute_type
|
25
|
+
end
|
26
|
+
class RequiredError < Error
|
27
|
+
attr_accessor :attribute_name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class AttributeRegistry
|
32
|
+
class Error < Inspec::Error; end
|
33
|
+
class ProfileError < Error
|
34
|
+
attr_accessor :profile_name
|
35
|
+
end
|
36
|
+
class AttributeError < Error
|
37
|
+
attr_accessor :profile_name
|
38
|
+
attr_accessor :attribute_name
|
39
|
+
end
|
40
|
+
end
|
14
41
|
end
|
data/lib/inspec/file_provider.rb
CHANGED
@@ -105,6 +105,22 @@ module Inspec
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
+
def extract(destination_path = '.')
|
109
|
+
FileUtils.mkdir_p(destination_path)
|
110
|
+
|
111
|
+
Zip::File.open(@path) do |archive|
|
112
|
+
archive.each do |file|
|
113
|
+
final_path = File.join(destination_path, file.name)
|
114
|
+
|
115
|
+
# This removes the top level directory (and any other files) to ensure
|
116
|
+
# extracted files do not conflict.
|
117
|
+
FileUtils.remove_entry(final_path) if File.exist?(final_path)
|
118
|
+
|
119
|
+
archive.extract(file, final_path)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
108
124
|
def read(file)
|
109
125
|
@contents[file] ||= read_from_zip(file)
|
110
126
|
end
|
@@ -150,6 +166,24 @@ module Inspec
|
|
150
166
|
end
|
151
167
|
end
|
152
168
|
|
169
|
+
def extract(destination_path = '.')
|
170
|
+
FileUtils.mkdir_p(destination_path)
|
171
|
+
|
172
|
+
walk_tar(@path) do |files|
|
173
|
+
files.each do |file|
|
174
|
+
next unless @files.include?(file.full_name)
|
175
|
+
final_path = File.join(destination_path, file.full_name)
|
176
|
+
|
177
|
+
# This removes the top level directory (and any other files) to ensure
|
178
|
+
# extracted files do not conflict.
|
179
|
+
FileUtils.remove_entry(final_path) if File.exist?(final_path)
|
180
|
+
|
181
|
+
FileUtils.mkdir_p(File.dirname(final_path))
|
182
|
+
File.open(final_path, 'wb') { |f| f.write(file.read) }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
153
187
|
def read(file)
|
154
188
|
@contents[file] ||= read_from_tar(file)
|
155
189
|
end
|
@@ -157,7 +191,10 @@ module Inspec
|
|
157
191
|
private
|
158
192
|
|
159
193
|
def walk_tar(path, &callback)
|
160
|
-
|
194
|
+
tar_file = Zlib::GzipReader.open(path)
|
195
|
+
Gem::Package::TarReader.new(tar_file, &callback)
|
196
|
+
ensure
|
197
|
+
tar_file.close
|
161
198
|
end
|
162
199
|
|
163
200
|
def read_from_tar(file)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Impact scores based off CVSS 3.0
|
4
|
+
module Inspec::Impact
|
5
|
+
IMPACT_SCORES = {
|
6
|
+
'none' => 0.0,
|
7
|
+
'low' => 0.01,
|
8
|
+
'medium' => 0.4,
|
9
|
+
'high' => 0.7,
|
10
|
+
'critical' => 0.9,
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def self.impact_from_string(value)
|
14
|
+
# return if its a number
|
15
|
+
return value if is_number?(value)
|
16
|
+
raise Inspec::ImpactError, "'#{value}' is not a valid impact name. Valid impact names: none, low, medium, high, critical." unless IMPACT_SCORES.key?(value.downcase)
|
17
|
+
IMPACT_SCORES[value]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.is_number?(value)
|
21
|
+
Float(value)
|
22
|
+
true
|
23
|
+
rescue
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.string_from_impact(value)
|
28
|
+
value = value.to_f
|
29
|
+
raise Inspec::ImpactError, "'#{value}' is not a valid impact score. Valid impact scores: [0.0 - 1.0]." if value < 0 || value > 1
|
30
|
+
IMPACT_SCORES.reverse_each do |name, impact|
|
31
|
+
return name if value >= impact
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -3,7 +3,16 @@
|
|
3
3
|
module Inspec
|
4
4
|
class Attribute
|
5
5
|
attr_accessor :name
|
6
|
-
|
6
|
+
|
7
|
+
VALID_TYPES = %w{
|
8
|
+
String
|
9
|
+
Numeric
|
10
|
+
Regexp
|
11
|
+
Array
|
12
|
+
Hash
|
13
|
+
Boolean
|
14
|
+
Any
|
15
|
+
}.freeze
|
7
16
|
|
8
17
|
DEFAULT_ATTRIBUTE = Class.new do
|
9
18
|
def initialize(name)
|
@@ -16,7 +25,6 @@ module Inspec
|
|
16
25
|
"Use --attrs to provide a value for '#{@name}' or specify a default "\
|
17
26
|
"value with `attribute('#{@name}', default: 'somedefault', ...)`.",
|
18
27
|
)
|
19
|
-
|
20
28
|
self
|
21
29
|
end
|
22
30
|
|
@@ -28,16 +36,22 @@ module Inspec
|
|
28
36
|
def initialize(name, options = {})
|
29
37
|
@name = name
|
30
38
|
@opts = options
|
39
|
+
validate_value_type(default) if @opts.key?(:type) && @opts.key?(:default)
|
31
40
|
@value = nil
|
32
41
|
end
|
33
42
|
|
34
|
-
|
35
|
-
|
36
|
-
@value
|
43
|
+
def value=(new_value)
|
44
|
+
validate_value_type(new_value) if @opts.key?(:type)
|
45
|
+
@value = new_value
|
37
46
|
end
|
38
47
|
|
39
|
-
def
|
40
|
-
@
|
48
|
+
def value
|
49
|
+
if @value.nil?
|
50
|
+
validate_required(@value) if @opts[:required] == true
|
51
|
+
@value = default
|
52
|
+
else
|
53
|
+
@value
|
54
|
+
end
|
41
55
|
end
|
42
56
|
|
43
57
|
def title
|
@@ -71,5 +85,76 @@ module Inspec
|
|
71
85
|
def to_s
|
72
86
|
"Attribute #{@name} with #{@value}"
|
73
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def validate_required(value)
|
92
|
+
# value will be set already if a secrets file was passed in
|
93
|
+
if (!@opts.key?(:default) && value.nil?) || (@opts[:default].nil? && value.nil?)
|
94
|
+
error = Inspec::Attribute::RequiredError.new
|
95
|
+
error.attribute_name = @name
|
96
|
+
raise error, "Attribute '#{error.attribute_name}' is required and does not have a value."
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def validate_type(type)
|
101
|
+
type = type.capitalize
|
102
|
+
abbreviations = {
|
103
|
+
'Num' => 'Numeric',
|
104
|
+
'Regex' => 'Regexp',
|
105
|
+
}
|
106
|
+
type = abbreviations[type] if abbreviations.key?(type)
|
107
|
+
if !VALID_TYPES.include?(type)
|
108
|
+
error = Inspec::Attribute::TypeError.new
|
109
|
+
error.attribute_type = type
|
110
|
+
raise error, "Type '#{error.attribute_type}' is not a valid attribute type."
|
111
|
+
end
|
112
|
+
type
|
113
|
+
end
|
114
|
+
|
115
|
+
def valid_numeric?(value)
|
116
|
+
Float(value)
|
117
|
+
true
|
118
|
+
rescue
|
119
|
+
false
|
120
|
+
end
|
121
|
+
|
122
|
+
def valid_regexp?(value)
|
123
|
+
# check for invalid regex syntex
|
124
|
+
Regexp.new(value)
|
125
|
+
true
|
126
|
+
rescue
|
127
|
+
false
|
128
|
+
end
|
129
|
+
|
130
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
131
|
+
def validate_value_type(value)
|
132
|
+
type = validate_type(@opts[:type])
|
133
|
+
return if type == 'Any'
|
134
|
+
|
135
|
+
invalid_type = false
|
136
|
+
if type == 'Regexp'
|
137
|
+
invalid_type = true if !value.is_a?(String) || !valid_regexp?(value)
|
138
|
+
elsif type == 'Numeric'
|
139
|
+
invalid_type = true if !valid_numeric?(value)
|
140
|
+
elsif type == 'Boolean'
|
141
|
+
invalid_type = true if ![true, false].include?(value)
|
142
|
+
elsif value.is_a?(Module.const_get(type)) == false
|
143
|
+
invalid_type = true
|
144
|
+
end
|
145
|
+
|
146
|
+
if invalid_type == true
|
147
|
+
error = Inspec::Attribute::ValidationError.new
|
148
|
+
error.attribute_name = @name
|
149
|
+
error.attribute_value = value
|
150
|
+
error.attribute_type = type
|
151
|
+
raise error, "Attribute '#{error.attribute_name}' with value '#{error.attribute_value}' does not validate to type '#{error.attribute_type}'."
|
152
|
+
end
|
153
|
+
end
|
154
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
155
|
+
|
156
|
+
def default
|
157
|
+
@opts.key?(:default) ? @opts[:default] : DEFAULT_ATTRIBUTE.new(@name)
|
158
|
+
end
|
74
159
|
end
|
75
160
|
end
|