mirror of
https://github.com/inspec/inspec
synced 2024-11-10 07:04:15 +00:00
Merge pull request #5466 from inspec/nm/inputs-regex
New input option `pattern` added for DSL and metadata inputs
This commit is contained in:
commit
79aa03ae14
11 changed files with 164 additions and 6 deletions
|
@ -416,6 +416,12 @@ input values that are used as test results.
|
|||
|
||||
Allowed in: Metadata
|
||||
|
||||
### Pattern
|
||||
|
||||
Optional, `Regexp`. This feature validates the input by matching it with the provided regular expression.
|
||||
|
||||
Allowed in: DSL, Metadata
|
||||
|
||||
## Advanced Topics
|
||||
|
||||
### Debugging Inputs with the Event Log
|
||||
|
|
|
@ -19,12 +19,17 @@ module Inspec
|
|||
attr_accessor :input_name
|
||||
attr_accessor :input_value
|
||||
attr_accessor :input_type
|
||||
attr_accessor :input_pattern
|
||||
end
|
||||
|
||||
class TypeError < Error
|
||||
attr_accessor :input_type
|
||||
end
|
||||
|
||||
class PatternError < Error
|
||||
attr_accessor :input_pattern
|
||||
end
|
||||
|
||||
class RequiredError < Error
|
||||
attr_accessor :input_name
|
||||
end
|
||||
|
@ -56,7 +61,6 @@ module Inspec
|
|||
|
||||
def initialize(properties = {})
|
||||
@value_has_been_set = false
|
||||
|
||||
properties.each do |prop_name, prop_value|
|
||||
if EVENT_PROPERTIES.include? prop_name
|
||||
# OK, save the property
|
||||
|
@ -174,7 +178,7 @@ module Inspec
|
|||
# are free to go higher.
|
||||
DEFAULT_PRIORITY_FOR_VALUE_SET = 60
|
||||
|
||||
attr_reader :description, :events, :identifier, :name, :required, :sensitive, :title, :type
|
||||
attr_reader :description, :events, :identifier, :name, :required, :sensitive, :title, :type, :pattern
|
||||
|
||||
def initialize(name, options = {})
|
||||
@name = name
|
||||
|
@ -192,7 +196,6 @@ module Inspec
|
|||
# debugging record of when and how the value changed.
|
||||
@events = []
|
||||
events.push make_creation_event(options)
|
||||
|
||||
update(options)
|
||||
end
|
||||
|
||||
|
@ -213,6 +216,7 @@ module Inspec
|
|||
def update(options)
|
||||
_update_set_metadata(options)
|
||||
normalize_type_restriction!
|
||||
normalize_pattern_restriction!
|
||||
|
||||
# Values are set by passing events in; but we can also infer an event.
|
||||
if options.key?(:value) || options.key?(:default)
|
||||
|
@ -227,6 +231,7 @@ module Inspec
|
|||
events << options[:event] if options.key? :event
|
||||
|
||||
enforce_type_restriction!
|
||||
enforce_pattern_restriction!
|
||||
end
|
||||
|
||||
# We can determine a value:
|
||||
|
@ -268,6 +273,7 @@ module Inspec
|
|||
@identifier = options[:identifier] if options.key?(:identifier) # TODO: determine if this is ever used
|
||||
@type = options[:type] if options.key?(:type)
|
||||
@sensitive = options[:sensitive] if options.key?(:sensitive)
|
||||
@pattern = options[:pattern] if options.key?(:pattern)
|
||||
end
|
||||
|
||||
def make_creation_event(options)
|
||||
|
@ -310,7 +316,9 @@ module Inspec
|
|||
file: location.path,
|
||||
line: location.lineno
|
||||
)
|
||||
|
||||
enforce_type_restriction!
|
||||
enforce_pattern_restriction!
|
||||
end
|
||||
|
||||
def value
|
||||
|
@ -324,7 +332,7 @@ module Inspec
|
|||
|
||||
def to_hash
|
||||
as_hash = { name: name, options: {} }
|
||||
%i{description title identifier type required value sensitive}.each do |field|
|
||||
%i{description title identifier type required value sensitive pattern}.each do |field|
|
||||
val = send(field)
|
||||
next if val.nil?
|
||||
|
||||
|
@ -407,6 +415,33 @@ module Inspec
|
|||
@type = type_req
|
||||
end
|
||||
|
||||
def enforce_pattern_restriction!
|
||||
return unless pattern
|
||||
return unless has_value?
|
||||
|
||||
string_value = current_value(false).to_s
|
||||
|
||||
valid_pattern = string_value.match?(pattern)
|
||||
unless valid_pattern
|
||||
error = Inspec::Input::ValidationError.new
|
||||
error.input_name = @name
|
||||
error.input_value = string_value
|
||||
error.input_pattern = pattern
|
||||
raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to pattern '#{error.input_pattern}'."
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_pattern_restriction!
|
||||
return unless pattern
|
||||
|
||||
unless valid_regexp?(pattern)
|
||||
error = Inspec::Input::PatternError.new
|
||||
error.input_pattern = pattern
|
||||
raise error, "Pattern '#{error.input_pattern}' is not a valid regex pattern."
|
||||
end
|
||||
@pattern = pattern
|
||||
end
|
||||
|
||||
def valid_numeric?(value)
|
||||
Float(value)
|
||||
true
|
||||
|
|
|
@ -325,6 +325,7 @@ module Inspec
|
|||
type: input_options[:type],
|
||||
required: input_options[:required],
|
||||
sensitive: input_options[:sensitive],
|
||||
pattern: input_options[:pattern],
|
||||
event: evt
|
||||
)
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ module Inspec
|
|||
|
||||
def to_hash
|
||||
as_hash = { name: name, options: {} }
|
||||
%i{description title identifier type required value}.each do |field|
|
||||
%i{description title identifier type required value pattern}.each do |field|
|
||||
val = send(field)
|
||||
next if val.nil?
|
||||
|
||||
|
|
|
@ -180,7 +180,15 @@ module Inspec
|
|||
options[:priority] ||= 20
|
||||
options[:provider] = :inline_control_code
|
||||
evt = Inspec::Input.infer_event(options)
|
||||
Inspec::InputRegistry.find_or_register_input(input_name, __profile_id, event: evt).value
|
||||
Inspec::InputRegistry.find_or_register_input(
|
||||
input_name,
|
||||
__profile_id,
|
||||
type: options[:type],
|
||||
required: options[:required],
|
||||
description: options[:description],
|
||||
pattern: options[:pattern],
|
||||
event: evt
|
||||
).value
|
||||
end
|
||||
end
|
||||
|
||||
|
|
38
test/fixtures/profiles/inputs/dsl/controls/dsl.rb
vendored
Normal file
38
test/fixtures/profiles/inputs/dsl/controls/dsl.rb
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
# copyright: 2021, Chef Software, Inc.
|
||||
title "Testing all option flags on input through DSL"
|
||||
|
||||
control "pattern_flag_success_check" do
|
||||
describe input("input_value_01", value: 5, pattern: "^\d*[13579]$") do
|
||||
it { should eq 5 }
|
||||
end
|
||||
end
|
||||
|
||||
control "pattern_flag_failure_check" do
|
||||
describe input("input_value_02", value: 2, pattern: "^\d*[13579]$") do
|
||||
it { should eq 2 }
|
||||
end
|
||||
end
|
||||
|
||||
control "required_flag_success_check" do
|
||||
describe input("input_value_03", value: 5, required: true) do
|
||||
it { should eq 5 }
|
||||
end
|
||||
end
|
||||
|
||||
control "required_flag_failure_check" do
|
||||
describe input("input_value_04", required: true) do
|
||||
it { should eq 5 }
|
||||
end
|
||||
end
|
||||
|
||||
control "type_flag_success_check" do
|
||||
describe input("input_value_05", value: 5, type: "Numeric") do
|
||||
it { should eq 5 }
|
||||
end
|
||||
end
|
||||
|
||||
control "type_flag_failure_check" do
|
||||
describe input("input_value_06", value: 5, type: "String") do
|
||||
it { should eq 5 }
|
||||
end
|
||||
end
|
9
test/fixtures/profiles/inputs/dsl/inspec.yml
vendored
Normal file
9
test/fixtures/profiles/inputs/dsl/inspec.yml
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
name: dsl
|
||||
title: InSpec Profile to test all option flags on input through dsl
|
||||
maintainer: Chef Software, Inc.
|
||||
copyright: Chef Software, Inc.
|
||||
license: Apache-2.0
|
||||
summary: A profile that tests all option flags on input through dsl
|
||||
version: 0.1.0
|
||||
supports:
|
||||
platform: os
|
8
test/fixtures/profiles/inputs/metadata-pattern/controls/pattern.rb
vendored
Normal file
8
test/fixtures/profiles/inputs/metadata-pattern/controls/pattern.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# copyright: 2021, Chef Software, Inc.
|
||||
title "Testing :pattern flag"
|
||||
|
||||
control "pattern_flag_checking_odd_num" do
|
||||
describe input("input_value_01") do
|
||||
it { should eq 5 }
|
||||
end
|
||||
end
|
14
test/fixtures/profiles/inputs/metadata-pattern/inspec.yml
vendored
Normal file
14
test/fixtures/profiles/inputs/metadata-pattern/inspec.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
name: metadata-pattern
|
||||
title: InSpec Profile to test :pattern flag on inputs using metadata
|
||||
maintainer: Chef Software, Inc.
|
||||
copyright: Chef Software, Inc.
|
||||
license: Apache-2.0
|
||||
summary: A profile that tests the :pattern flag on inputs
|
||||
version: 0.1.0
|
||||
supports:
|
||||
platform: os
|
||||
inputs:
|
||||
- name: input_value_01
|
||||
value: 5
|
||||
pattern: ^\d*[13579]$
|
||||
required: true
|
|
@ -462,4 +462,40 @@ describe "inputs" do
|
|||
assert_json_controls_passing(result)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when a profile is used with input options" do
|
||||
it "should be a success for valid values when pattern flag is passed through metadata file" do
|
||||
result = run_inspec_process("exec #{inputs_profiles_path}/metadata-pattern", json: true)
|
||||
_(result.stderr).must_be_empty
|
||||
assert_json_controls_passing(result)
|
||||
end
|
||||
|
||||
it "should be a success for valid values when required, type and pattern flag is passed through dsl" do
|
||||
result = run_inspec_process("exec #{inputs_profiles_path}/dsl --controls pattern_flag_success_check required_flag_success_check type_flag_success_check", json: true)
|
||||
_(result.stderr).must_be_empty
|
||||
assert_json_controls_passing(result)
|
||||
end
|
||||
|
||||
it "should be a failure for invalid value when required flag is passed through dsl" do
|
||||
result = run_inspec_process("exec #{inputs_profiles_path}/dsl --controls required_flag_failure_check", json: true)
|
||||
_(result.stderr).must_include "Input 'input_value_04' is required and does not have a value.\n"
|
||||
assert_exit_code 1, result
|
||||
end
|
||||
|
||||
it "should be a failure for invalid value when type flag is passed through dsl" do
|
||||
result = run_inspec_process("exec #{inputs_profiles_path}/dsl --controls type_flag_failure_check", json: true)
|
||||
_(result.stderr).must_be_empty
|
||||
output = JSON.parse(result[0])
|
||||
assert_equal "failed", output["profiles"][0]["controls"][0]["results"][0]["status"]
|
||||
assert_exit_code(100, result)
|
||||
end
|
||||
|
||||
it "should be a failure for invalid value when pattern flag is passed through dsl" do
|
||||
result = run_inspec_process("exec #{inputs_profiles_path}/dsl --controls pattern_flag_failure_check", json: true)
|
||||
_(result.stderr).must_be_empty
|
||||
output = JSON.parse(result[0])
|
||||
assert_equal "failed", output["profiles"][0]["controls"][0]["results"][0]["status"]
|
||||
assert_exit_code(100, result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,7 @@ describe Inspec::Input do
|
|||
required: true,
|
||||
title: "how is this different than description",
|
||||
type: "Numeric",
|
||||
pattern: "^[0-9][0-9]$",
|
||||
}.each do |field, value|
|
||||
it "should be able to recall the #{field} field" do
|
||||
opts[field] = value
|
||||
|
@ -32,6 +33,7 @@ describe Inspec::Input do
|
|||
title: "Best input ever",
|
||||
description: "important",
|
||||
type: "Numeric",
|
||||
pattern: "^[0-9][0-9]$",
|
||||
required: true)
|
||||
|
||||
_(input.to_hash).must_equal({
|
||||
|
@ -41,6 +43,7 @@ describe Inspec::Input do
|
|||
title: "Best input ever",
|
||||
description: "important",
|
||||
type: "Numeric",
|
||||
pattern: "^[0-9][0-9]$",
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue