inspec 3.2.6 → 3.3.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41bedb4528fa302afca59c5834684789d195e7addb8247cf03bad101be3c26db
4
- data.tar.gz: e0d91369ea72888be97ad4eaeab9515f4a38a87609de938495b1c2d317ad67f0
3
+ metadata.gz: 57b4ef6616ae9f2ee1a0d715fd3849d91f759242afe31da2e044fd243424c88d
4
+ data.tar.gz: c2a5e2e09134bd5919cdcf057110ad96de7e35bdf8316d440e0d8809d11ea603
5
5
  SHA512:
6
- metadata.gz: 2beb30ea2652ed85d070783c9f846b47d8624809419286b0cb78ead32902b92af0578e6ea281c50637697b38e20fd165445be9baa5946e04b1aab2503d5fcb6e
7
- data.tar.gz: f741b613b7472f4a2be1fd7d155398af3ad9aa54ac4f191ac812e89cf5ab2710fd976b4750a8bcd79dad094facdd50f5c2296f5c4845003975875f1f251ce1ca
6
+ metadata.gz: b51dad58342d2a68ccec838623278fedc4573576f43618f90a36ba9443415ebb922220b656d0b1a2185f6e699e3d0532bd45ef7cb15ff6996225ed9d7c71d286
7
+ data.tar.gz: d59f22a869aeabf92638eef956e8b9068a71f636b951b3ddeee8ad1c691d31b264dbafd5384449f0fe7ef6d59742e9814e3b134090a5fb62ec791de449d3742d
data/Gemfile CHANGED
@@ -6,7 +6,6 @@ gem 'ffi', '>= 1.9.14'
6
6
  gem 'aws-sdk', '~> 2'
7
7
 
8
8
  group :test do
9
- gem 'bundler', '~> 1.5'
10
9
  gem 'minitest', '~> 5.5'
11
10
  gem 'rake', '>= 10'
12
11
  gem 'rubocop', '= 0.49.1'
@@ -23,7 +22,7 @@ end
23
22
 
24
23
  group :integration do
25
24
  gem 'berkshelf', '~> 5.2'
26
- gem 'test-kitchen', '~> 1.6'
25
+ gem 'test-kitchen', '>= 1.24'
27
26
  gem 'kitchen-vagrant'
28
27
  # we need winrm v2 support >= 0.15.1
29
28
  gem 'kitchen-inspec', '>= 0.15.1'
@@ -0,0 +1,6 @@
1
+ {
2
+ "file_version": "1.0.0",
3
+ "unknown_group_action": "ignore",
4
+ "groups": {
5
+ }
6
+ }
data/inspec.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.required_ruby_version = '>= 2.3'
26
26
 
27
- spec.add_dependency 'train', '~> 1.5', '>= 1.6.3'
27
+ spec.add_dependency 'train', '~> 1.5', '>= 1.7.0'
28
28
  spec.add_dependency 'thor', '~> 0.20'
29
29
  spec.add_dependency 'json', '>= 1.8', '< 3.0'
30
30
  spec.add_dependency 'method_source', '~> 0.8'
data/lib/inspec.rb CHANGED
@@ -8,6 +8,7 @@ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
8
8
 
9
9
  require 'inspec/version'
10
10
  require 'inspec/exceptions'
11
+ require 'utils/deprecation'
11
12
  require 'inspec/profile'
12
13
  require 'inspec/rule'
13
14
  require 'matchers/matchers'
data/lib/inspec/cli.rb CHANGED
@@ -175,14 +175,21 @@ class Inspec::InspecCLI < Inspec::BaseCLI
175
175
  end
176
176
 
177
177
  desc 'exec LOCATIONS', 'run all test files at the specified LOCATIONS.'
178
+ # TODO: find a way for Thor not to butcher the formatting of this
178
179
  long_desc <<~EOT
179
180
  Loads the given profile(s) and fetches their dependencies if needed. Then
180
181
  connects to the target and executes any controls contained in the profiles.
181
- One or more reporters are used to generate output. If all tests passed
182
- (no fails, no skips) exit code 0 is returned. If some tests skipped but
183
- none failed, exit code 101 is returned. If at least one test failed, exit
184
- code 100 is returned. If inspec failed for any other reason, exit code 1
185
- is returned.
182
+ One or more reporters are used to generate output.
183
+
184
+ ```
185
+ Exit codes:
186
+ 0 Normal exit, all tests passed
187
+ 1 Usage or general error
188
+ 2 Error in plugin system
189
+ 3 Fatal deprecation encountered
190
+ 100 Normal exit, at least one test failed
191
+ 101 Normal exit, at least one test skipped but none failed
192
+ ```
186
193
 
187
194
  Below are some examples of using `exec` with different test LOCATIONS:
188
195
 
@@ -91,9 +91,9 @@ module Inspec
91
91
  warnings.push("Missing profile #{field} in #{ref}")
92
92
  end
93
93
 
94
- # if version is set, ensure it is in SPDX format
95
- if !params[:license].nil? && !Spdx.valid_license?(params[:license])
96
- warnings.push("License '#{params[:license]}' needs to be in SPDX format. See https://spdx.org/licenses/.")
94
+ # if license is set, ensure it is in SPDX format or marked as proprietary
95
+ if !params[:license].nil? && !valid_license?(params[:license])
96
+ warnings.push("License '#{params[:license]}' needs to be in SPDX format or marked as 'Proprietary'. See https://spdx.org/licenses/.")
97
97
  end
98
98
 
99
99
  [errors, warnings]
@@ -112,6 +112,10 @@ module Inspec
112
112
  false
113
113
  end
114
114
 
115
+ def valid_license?(value)
116
+ value =~ /^Proprietary[,;]?\b/ || Spdx.valid_license?(value)
117
+ end
118
+
115
119
  def method_missing(sth, *args)
116
120
  @logger.warn "#{ref} doesn't support: #{sth} #{args}"
117
121
  @missing_methods.push(sth)
@@ -8,6 +8,7 @@ require 'fileutils'
8
8
  require 'rubygems/package'
9
9
  require 'rubygems/name_tuple'
10
10
  require 'rubygems/uninstaller'
11
+ require 'rubygems/remote_fetcher'
11
12
 
12
13
  require 'inspec/plugin/v2/filter'
13
14
 
data/lib/inspec/ui.rb CHANGED
@@ -32,6 +32,7 @@ module Inspec
32
32
  EXIT_NORMAL = 0
33
33
  EXIT_USAGE_ERROR = 1
34
34
  EXIT_PLUGIN_ERROR = 2
35
+ EXIT_FATAL_DEPRECATION = 3
35
36
  EXIT_FAILED_TESTS = 100
36
37
  EXIT_SKIPPED_TESTS = 101
37
38
 
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '3.2.6'
7
+ VERSION = '3.3.14'
8
8
  end
@@ -12,6 +12,8 @@ require 'resource_support/aws/aws_backend_base'
12
12
  # Load all AWS resources
13
13
  # TODO: loop over and load entire directory
14
14
  # for f in ls lib/resources/aws/*; do t=$(echo $f | cut -c 5- | cut -f1 -d. ); echo "require '${t}'"; done
15
+ require 'resources/aws/aws_billing_report'
16
+ require 'resources/aws/aws_billing_reports'
15
17
  require 'resources/aws/aws_cloudtrail_trail'
16
18
  require 'resources/aws/aws_cloudtrail_trails'
17
19
  require 'resources/aws/aws_cloudwatch_alarm'
@@ -0,0 +1,99 @@
1
+ class AwsBillingReport < Inspec.resource(1)
2
+ name 'aws_billing_report'
3
+ supports platform: 'aws'
4
+ desc 'Verifies settings for AWS Cost and Billing Reports.'
5
+ example "
6
+ describe aws_billing_report('inspec1') do
7
+ its('report_name') { should cmp 'inspec1' }
8
+ its('time_unit') { should cmp 'hourly' }
9
+ end
10
+
11
+ describe aws_billing_report(report: 'inspec1') do
12
+ it { should exist }
13
+ end"
14
+
15
+ include AwsSingularResourceMixin
16
+
17
+ attr_reader :report_name, :time_unit, :format, :compression, :s3_bucket,
18
+ :s3_prefix, :s3_region
19
+
20
+ def to_s
21
+ "AWS Billing Report #{report_name}"
22
+ end
23
+
24
+ def hourly?
25
+ exists? ? time_unit.eql?('hourly') : nil
26
+ end
27
+
28
+ def daily?
29
+ exists? ? time_unit.eql?('daily') : nil
30
+ end
31
+
32
+ def zip?
33
+ exists? ? compression.eql?('zip') : nil
34
+ end
35
+
36
+ def gzip?
37
+ exists? ? compression.eql?('gzip') : nil
38
+ end
39
+
40
+ private
41
+
42
+ def validate_params(raw_params)
43
+ validated_params = check_resource_param_names(
44
+ raw_params: raw_params,
45
+ allowed_params: [:report_name],
46
+ allowed_scalar_name: :report_name,
47
+ allowed_scalar_type: String,
48
+ )
49
+
50
+ if validated_params.empty?
51
+ raise ArgumentError, "You must provide the parameter 'report_name' to aws_billing_report."
52
+ end
53
+
54
+ validated_params
55
+ end
56
+
57
+ def fetch_from_api
58
+ report = find_report(report_name)
59
+ @exists = !report.nil?
60
+ if exists?
61
+ @time_unit = report.time_unit.downcase
62
+ @format = report.format.downcase
63
+ @compression = report.compression.downcase
64
+ @s3_bucket = report.s3_bucket
65
+ @s3_prefix = report.s3_prefix
66
+ @s3_region = report.s3_region
67
+ end
68
+ end
69
+
70
+ def find_report(report_name)
71
+ pagination_opts = {}
72
+ found_report_def = nil
73
+ while found_report_def.nil?
74
+ api_result = backend.describe_report_definitions(pagination_opts)
75
+ next_token = api_result.next_token
76
+ found_report_def = api_result.report_definitions.find { |report_def| report_def.report_name == report_name }
77
+ pagination_opts = { next_token: next_token }
78
+
79
+ next if found_report_def.nil? && next_token # Loop again: didn't find it, but there are more results
80
+ break if found_report_def.nil? && next_token.nil? # Give up: didn't find it, no more results
81
+ end
82
+ found_report_def
83
+ end
84
+
85
+ def backend
86
+ @backend ||= BackendFactory.create(inspec_runner)
87
+ end
88
+
89
+ class Backend
90
+ class AwsClientApi < AwsBackendBase
91
+ AwsBillingReport::BackendFactory.set_default_backend(self)
92
+ self.aws_client_class = Aws::CostandUsageReportService::Client
93
+
94
+ def describe_report_definitions(query = {})
95
+ aws_service_client.describe_report_definitions(query)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,69 @@
1
+ require 'utils/filter'
2
+
3
+ class AwsBillingReports < Inspec.resource(1)
4
+ name 'aws_billing_reports'
5
+ supports platform: 'aws'
6
+ desc 'Verifies settings for AWS Cost and Billing Reports.'
7
+ example "
8
+ describe aws_billing_reports do
9
+ its('report_names') { should include 'inspec1' }
10
+ its('s3_buckets') { should include 'inspec1-s3-bucket' }
11
+ end
12
+
13
+ describe aws_billing_reports.where { report_name =~ /inspec.*/ } do
14
+ its ('report_names') { should include ['inspec1'] }
15
+ its ('time_units') { should include ['DAILY'] }
16
+ its ('s3_buckets') { should include ['inspec1-s3-bucket'] }
17
+ end"
18
+
19
+ include AwsPluralResourceMixin
20
+
21
+ filtertable = FilterTable.create
22
+ filtertable.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
23
+ .register_column(:report_names, field: :report_name)
24
+ .register_column(:time_units, field: :time_unit, style: :simple)
25
+ .register_column(:formats, field: :format, style: :simple)
26
+ .register_column(:compressions, field: :compression, style: :simple)
27
+ .register_column(:s3_buckets, field: :s3_bucket, style: :simple)
28
+ .register_column(:s3_prefixes, field: :s3_prefix, style: :simple)
29
+ .register_column(:s3_regions, field: :s3_region, style: :simple)
30
+ filtertable.install_filter_methods_on_resource(self, :table)
31
+
32
+ def validate_params(resource_params)
33
+ unless resource_params.empty?
34
+ raise ArgumentError, 'aws_billing_reports does not accept resource parameters.'
35
+ end
36
+ resource_params
37
+ end
38
+
39
+ def to_s
40
+ 'AWS Billing Reports'
41
+ end
42
+
43
+ def fetch_from_api
44
+ @table = []
45
+ pagination_opts = {}
46
+ backend = BackendFactory.create(inspec_runner)
47
+ loop do
48
+ api_result = backend.describe_report_definitions(pagination_opts)
49
+ api_result.report_definitions.each do |raw_report|
50
+ report = raw_report.to_h
51
+ %i(time_unit compression).each { |field| report[field].downcase! }
52
+ @table << report
53
+ end
54
+ pagination_opts = { next_token: api_result.next_token }
55
+ break unless api_result.next_token
56
+ end
57
+ end
58
+
59
+ class Backend
60
+ class AwsClientApi < AwsBackendBase
61
+ AwsBillingReports::BackendFactory.set_default_backend(self)
62
+ self.aws_client_class = Aws::CostandUsageReportService::Client
63
+
64
+ def describe_report_definitions(options = {})
65
+ aws_service_client.describe_report_definitions(options)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -41,7 +41,7 @@ module Inspec::Resources
41
41
  #
42
42
  # So make sure command output is converted to unicode, as it returns ASCII-8BIT by default
43
43
  utf8_stdout = cmd.stdout.chomp.force_encoding(Encoding::UTF_8)
44
- params = /^\[\d+\]\s+(?:\p{Initial_Punctuation})(.+)(?:\p{Final_Punctuation})$/.match(utf8_stdout)
44
+ params = /^\[\d+\]\s+(?:['\p{Initial_Punctuation}])(.+)(?:['\p{Final_Punctuation}])$/.match(utf8_stdout)
45
45
  @info[:installed] = !params.nil?
46
46
  return @info unless @info[:installed]
47
47
 
@@ -6,14 +6,15 @@
6
6
  class IisAppPool < Inspec.resource(1)
7
7
  name 'iis_app_pool'
8
8
  desc 'Tests IIS application pool configuration on windows.'
9
- example "
9
+ supports platform: 'windows'
10
+ example <<~EOH
10
11
  describe iis_app_pool('DefaultAppPool') do
11
12
  it { should exist }
12
13
  its('enable32bit') { should cmp 'True' }
13
14
  its('runtime_version') { should eq 'v4.0' }
14
15
  its('pipeline_mode') { should eq 'Integrated' }
15
16
  end
16
- "
17
+ EOH
17
18
 
18
19
  def initialize(pool_name)
19
20
  @pool_name = pool_name
@@ -77,18 +78,23 @@ class IisAppPool < Inspec.resource(1)
77
78
  end
78
79
 
79
80
  def to_s
80
- "iis_app_pool '#{@pool_name}'"
81
+ "IIS App Pool '#{@pool_name}'"
81
82
  end
82
83
 
83
84
  private
84
85
 
85
- # I cannot think of a way to shorten this method
86
- # rubocop:disable Metrics/AbcSize
87
86
  def iis_app_pool
88
87
  return @cache unless @cache.nil?
89
88
 
90
- command = "Import-Module WebAdministration; Get-Item '#{@pool_path}' | Select-Object * | ConvertTo-Json"
91
- cmd = inspec.command(command)
89
+ script = <<~EOH
90
+ Import-Module WebAdministration
91
+ If (Test-Path '#{@pool_path}') {
92
+ Get-Item '#{@pool_path}' | Select-Object * | ConvertTo-Json
93
+ } Else {
94
+ Write-Host '{}'
95
+ }
96
+ EOH
97
+ cmd = inspec.powershell(script)
92
98
 
93
99
  begin
94
100
  pool = JSON.parse(cmd.stdout)
@@ -96,21 +102,23 @@ class IisAppPool < Inspec.resource(1)
96
102
  raise Inspec::Exceptions::ResourceFailed, 'Unable to parse app pool JSON'
97
103
  end
98
104
 
105
+ process_model = pool.fetch('processModel', {})
106
+ idle_timeout = process_model.fetch('idleTimeout', {})
107
+
99
108
  # map our values to a hash table
100
109
  @cache = {
101
110
  pool_name: pool['name'],
102
111
  version: pool['managedRuntimeVersion'],
103
112
  e32b: pool['enable32BitAppOnWin64'],
104
113
  mode: pool['managedPipelineMode'],
105
- processes: pool['processModel']['maxProcesses'],
106
- timeout: "#{pool['processModel']['idleTimeout']['Hours']}:#{pool['processModel']['idleTimeout']['Minutes']}:#{pool['processModel']['idleTimeout']['Seconds']}",
107
- timeout_days: pool['processModel']['idleTimeout']['Days'],
108
- timeout_hours: pool['processModel']['idleTimeout']['Hours'],
109
- timeout_minutes: pool['processModel']['idleTimeout']['Minutes'],
110
- timeout_seconds: pool['processModel']['idleTimeout']['Seconds'],
111
- user_identity_type: pool['processModel']['identityType'],
112
- username: pool['processModel']['userName'],
114
+ processes: process_model['maxProcesses'],
115
+ timeout: "#{idle_timeout['Hours']}:#{idle_timeout['Minutes']}:#{idle_timeout['Seconds']}",
116
+ timeout_days: idle_timeout['Days'],
117
+ timeout_hours: idle_timeout['Hours'],
118
+ timeout_minutes: idle_timeout['Minutes'],
119
+ timeout_seconds: idle_timeout['Seconds'],
120
+ user_identity_type: process_model['identityType'],
121
+ username: process_model['userName'],
113
122
  }
114
123
  end
115
- # rubocop:enable Metrics/AbcSize
116
124
  end
@@ -69,7 +69,6 @@ module Inspec::Resources
69
69
  end
70
70
 
71
71
  def locate_data_dir_location_by_version(ver = @version)
72
- data_dir_loc = nil
73
72
  dir_list = [
74
73
  "/var/lib/pgsql/#{ver}/data",
75
74
  '/var/lib/pgsql/data',
@@ -77,10 +76,7 @@ module Inspec::Resources
77
76
  '/var/lib/postgresql/data',
78
77
  ]
79
78
 
80
- dir_list.each do |dir|
81
- data_dir_loc = dir if inspec.directory(dir).exist?
82
- break
83
- end
79
+ data_dir_loc = dir_list.detect { |i| inspec.directory(i).exist? }
84
80
 
85
81
  if data_dir_loc.nil?
86
82
  warn 'Unable to find the PostgreSQL data_dir in expected location(s), please
@@ -0,0 +1,6 @@
1
+ # A system to provide a unified deprecation facility for InSpec
2
+
3
+ require 'utils/deprecation/errors'
4
+ require 'utils/deprecation/config_file'
5
+ require 'utils/deprecation/deprecator'
6
+ require 'utils/deprecation/global_method'
@@ -0,0 +1,105 @@
1
+ require 'stringio'
2
+ require 'json'
3
+ require 'inspec/globals'
4
+
5
+ module Inspec
6
+ module Deprecation
7
+ class ConfigFile
8
+ GroupEntry = Struct.new(:name, :action, :prefix, :suffix, :exit_status)
9
+
10
+ # What actions may you specify to be taken when a deprecation is encountered?
11
+ VALID_ACTIONS = [
12
+ :exit, # Hard exit `inspec`, no stacktrace, exit code specified or Inspec::UI::EXIT_FATAL_DEPRECATION
13
+ :fail_control, # Fail the control with a message. If not in a control, do :warn action instead.
14
+ :ignore, # Do nothing.
15
+ :warn, # Issue a warning
16
+ ].freeze
17
+
18
+ # Note that 'comment' is ignored, but listed here so you can have it present
19
+ # and pass validation.
20
+ VALID_GROUP_FIELDS = %w{action suffix prefix exit_status comment}.freeze
21
+
22
+ attr_reader :groups, :unknown_group_action
23
+
24
+ def initialize(io = nil)
25
+ io ||= open_default_config_io
26
+ begin
27
+ @raw_data = JSON.parse(io.read)
28
+ rescue JSON::ParserError => e
29
+ raise Inspec::Deprecation::MalformedConfigFileError, "Could not parse deprecation config file: #{e.message}"
30
+ end
31
+
32
+ @groups = {}
33
+ @unknown_group_action = :warn
34
+ validate!
35
+ end
36
+
37
+ private
38
+
39
+ def open_default_config_io
40
+ default_path = File.join(Inspec.src_root, 'etc', 'deprecations.json')
41
+ unless File.exist?(default_path)
42
+ raise Inspec::Deprecation::MalformedConfigError, "Missing deprecation config file: #{default_path}"
43
+ end
44
+ File.open(default_path)
45
+ end
46
+
47
+ #====================================================================================================#
48
+ # Validation
49
+ #====================================================================================================#
50
+ def validate!
51
+ validate_file_version
52
+ validate_unknown_group_action
53
+
54
+ unless @raw_data.key?('groups')
55
+ raise Inspec::Deprecation::InvalidConfigFileError, 'Missing groups field'
56
+ end
57
+ unless @raw_data['groups'].is_a?(Hash)
58
+ raise Inspec::Deprecation::InvalidConfigFileError, 'Groups field must be a Hash'
59
+ end
60
+ @raw_data['groups'].each do |group_name, group_info|
61
+ validate_group_entry(group_name, group_info)
62
+ end
63
+ end
64
+
65
+ def validate_file_version
66
+ unless @raw_data.key?('file_version')
67
+ raise Inspec::Deprecation::InvalidConfigFileError, 'Missing file_version field'
68
+ end
69
+ unless @raw_data['file_version'] == '1.0.0'
70
+ raise Inspec::Deprecation::InvalidConfigFileError, "Unrecognized file_version '#{@raw_data['file_version']}' - supported versions: 1.0.0"
71
+ end
72
+ end
73
+
74
+ def validate_unknown_group_action
75
+ seen_action = (@raw_data['unknown_group_action'] || @unknown_group_action).to_sym
76
+ unless VALID_ACTIONS.include?(seen_action)
77
+ raise Inspec::Deprecation::UnrecognizedActionError, "Unrecognized value '#{seen_action}' for field 'unknown_group_action' - supported actions: #{VALID_ACTIONS.map(&:to_s).join(', ')}"
78
+ end
79
+ @unknown_group_action = seen_action
80
+ end
81
+
82
+ def validate_group_entry(name, opts)
83
+ opts.each do |seen_field, _value|
84
+ unless VALID_GROUP_FIELDS.include?(seen_field)
85
+ raise Inspec::Deprecation::InvalidConfigFileError, "Unrecognized field for group '#{name}' - saw '#{seen_field}', supported fields: #{VALID_GROUP_FIELDS.map(&:to_s).join(', ')}"
86
+ end
87
+ end
88
+
89
+ entry = GroupEntry.new(name.to_sym)
90
+
91
+ opts['action'] = (opts['action'] || :warn).to_sym
92
+ unless VALID_ACTIONS.include?(opts['action'])
93
+ raise Inspec::Deprecation::UnrecognizedActionError, "Unrecognized action for group '#{name}' - saw '#{opts['action']}', supported actions: #{VALID_ACTIONS.map(&:to_s).join(', ')}"
94
+ end
95
+ entry.action = opts['action']
96
+
97
+ entry.suffix = opts['suffix']
98
+ entry.prefix = opts['prefix']
99
+ entry.exit_status = opts['exit_status']
100
+
101
+ groups[name.to_sym] = entry
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,117 @@
1
+ require 'utils/deprecation/config_file'
2
+ require 'inspec/log'
3
+
4
+ module Inspec
5
+ module Deprecation
6
+ class Deprecator
7
+ attr_reader :config, :groups
8
+
9
+ def initialize(opts = {})
10
+ @config = Inspec::Deprecation::ConfigFile.new(opts[:config_io])
11
+ @groups = @config.groups
12
+ end
13
+
14
+ def handle_deprecation(group_name, message, opts = {})
15
+ group = groups[group_name.to_sym] || create_group_entry_for_unknown_group(group_name)
16
+ annotate_stack_information(opts)
17
+ assembled_message = assemble_message(message, group, opts)
18
+
19
+ action = group[:action] || :warn
20
+ action_method = ('handle_' + action.to_s + '_action').to_sym
21
+ send(action_method, assembled_message, group)
22
+ end
23
+
24
+ private
25
+
26
+ def create_group_entry_for_unknown_group(group_name)
27
+ group = ConfigFile::GroupEntry.new
28
+ group.name = group_name
29
+ group.action = config.unknown_group_action
30
+ group.suffix = "Additionally, the deprecation message is in an unknown group '#{group_name}'."
31
+ group
32
+ end
33
+
34
+ def annotate_stack_information(opts)
35
+ stack = caller_locations(1, 25)
36
+
37
+ # Attempt to give a meaningful stack location of the place
38
+ # where the deprecated functionality was used. This is likely
39
+ # user (profile) code.
40
+ used_at = nil
41
+
42
+ # If we are in a profile, call stack will first include RSpec its,
43
+ # then a single call from the profile that originated within a load_with_context.
44
+ # rspec-core surrounds these.
45
+
46
+ # First, purge the deprecation system frames
47
+ stack.reject! { |frame| frame.path && frame.path =~ %r{lib/utils/deprecation} }
48
+ # Next, purge all RSpec entries (at least rspec-core, rspec-support, rspec-its).
49
+ stack.reject! { |frame| frame.path && frame.path =~ %r{rspec-.+/lib/rspec} }
50
+ # Now look for the frame that includes load_with_context.
51
+ used_at ||= stack.detect { |frame| frame.label.include? 'load_with_context' }
52
+
53
+ opts[:used_at_stack_frame] = used_at if used_at
54
+ end
55
+
56
+ def assemble_message(message, group, opts)
57
+ prefix = group.prefix || ''
58
+ suffix = group.suffix || ''
59
+ prefix += ' ' unless prefix.empty?
60
+ suffix = ' ' + suffix unless suffix.empty?
61
+
62
+ suffix += (' (used at ' + opts[:used_at_stack_frame].path+ ':' + opts[:used_at_stack_frame].lineno.to_s + ')') if opts.key?(:used_at_stack_frame)
63
+
64
+ 'DEPRECATION: ' + prefix + message + suffix
65
+ end
66
+
67
+ def called_from_control?
68
+ # Heuristics for determining if the deprecation is coming from within a control
69
+ stack = caller_locations(10, 45)
70
+
71
+ # Within a control block, that is actually an RSpec:ExampleGroup
72
+ stack.each do |frame|
73
+ return true if frame.path.end_with?('rspec/core/example_group.rb')
74
+ end
75
+
76
+ false
77
+ end
78
+
79
+ def handle_ignore_action(message, _group)
80
+ handle_log_action(message, :debug)
81
+ end
82
+
83
+ def handle_log_action(message, level)
84
+ case level
85
+ when :debug
86
+ Inspec::Log.debug message
87
+ when :warn
88
+ Inspec::Log.warn message
89
+ when :error
90
+ Inspec::Log.error message
91
+ end
92
+ end
93
+
94
+ def handle_warn_action(message, _group)
95
+ handle_log_action(message, :warn)
96
+ end
97
+
98
+ def handle_error_action(message, _group)
99
+ handle_log_action(message, :error)
100
+ end
101
+
102
+ def handle_fail_control_action(message, group)
103
+ if called_from_control?
104
+ raise Inspec::Exceptions::ResourceFailed, message
105
+ else
106
+ handle_warn_action(message, group)
107
+ end
108
+ end
109
+
110
+ def handle_exit_action(message, group)
111
+ handle_error_action(message, group)
112
+ status = group[:exit_status] || :fatal_deprecation
113
+ Inspec::UI.new.exit(status)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,14 @@
1
+ require 'inspec/errors'
2
+
3
+ module Inspec
4
+ module Deprecation
5
+ class Error < Inspec::Error; end
6
+
7
+ class NoSuchGroupError < Error; end
8
+
9
+ class InvalidConfigFileError < Error; end
10
+ class MalformedConfigFileError < InvalidConfigFileError; end
11
+ class UnrecognizedActionError < InvalidConfigFileError; end
12
+ class UnrecognizedOutputStreamError < InvalidConfigFileError; end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ require 'utils/deprecation/deprecator'
2
+
3
+ module Inspec
4
+ def self.deprecate(group, msg, opts = {})
5
+ config_io = opts.delete(:config_io)
6
+ deprecator = Inspec::Deprecation::Deprecator.new(config_io: config_io)
7
+ deprecator.handle_deprecation(group, msg, opts)
8
+ end
9
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.6
4
+ version: 3.3.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-20 00:00:00.000000000 Z
11
+ date: 2019-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '1.5'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 1.6.3
22
+ version: 1.7.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '1.5'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 1.6.3
32
+ version: 1.7.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: thor
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -365,6 +365,7 @@ files:
365
365
  - LICENSE
366
366
  - README.md
367
367
  - bin/inspec
368
+ - etc/deprecations.json
368
369
  - etc/plugin_filters.json
369
370
  - inspec.gemspec
370
371
  - lib/bundles/README.md
@@ -550,6 +551,8 @@ files:
550
551
  - lib/resources/audit_policy.rb
551
552
  - lib/resources/auditd.rb
552
553
  - lib/resources/auditd_conf.rb
554
+ - lib/resources/aws/aws_billing_report.rb
555
+ - lib/resources/aws/aws_billing_reports.rb
553
556
  - lib/resources/aws/aws_cloudtrail_trail.rb
554
557
  - lib/resources/aws/aws_cloudtrail_trails.rb
555
558
  - lib/resources/aws/aws_cloudwatch_alarm.rb
@@ -699,6 +702,11 @@ files:
699
702
  - lib/utils/command_wrapper.rb
700
703
  - lib/utils/convert.rb
701
704
  - lib/utils/database_helpers.rb
705
+ - lib/utils/deprecation.rb
706
+ - lib/utils/deprecation/config_file.rb
707
+ - lib/utils/deprecation/deprecator.rb
708
+ - lib/utils/deprecation/errors.rb
709
+ - lib/utils/deprecation/global_method.rb
702
710
  - lib/utils/enumerable_delegation.rb
703
711
  - lib/utils/erlang_parser.rb
704
712
  - lib/utils/file_reader.rb