inspec 3.2.6 → 3.3.14

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: 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