chef 17.4.38-universal-mingw32 → 17.5.22-universal-mingw32

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/chef.gemspec +2 -0
  3. data/lib/chef/application/base.rb +11 -1
  4. data/lib/chef/client.rb +1 -2
  5. data/lib/chef/compliance/input.rb +115 -0
  6. data/lib/chef/compliance/input_collection.rb +139 -0
  7. data/lib/chef/compliance/profile.rb +122 -0
  8. data/lib/chef/compliance/profile_collection.rb +109 -0
  9. data/lib/chef/compliance/runner.rb +47 -5
  10. data/lib/chef/compliance/waiver.rb +115 -0
  11. data/lib/chef/compliance/waiver_collection.rb +143 -0
  12. data/lib/chef/dsl/compliance.rb +38 -0
  13. data/lib/chef/dsl/reader_helpers.rb +51 -0
  14. data/lib/chef/dsl/recipe.rb +4 -2
  15. data/lib/chef/dsl/secret.rb +2 -4
  16. data/lib/chef/dsl/universal.rb +2 -0
  17. data/lib/chef/event_dispatch/base.rb +44 -2
  18. data/lib/chef/formatters/doc.rb +46 -0
  19. data/lib/chef/http/basic_client.rb +15 -7
  20. data/lib/chef/http.rb +7 -3
  21. data/lib/chef/provider/file.rb +2 -0
  22. data/lib/chef/provider/link.rb +2 -2
  23. data/lib/chef/provider/registry_key.rb +3 -2
  24. data/lib/chef/provider/remote_file/http.rb +1 -1
  25. data/lib/chef/provider/template.rb +1 -1
  26. data/lib/chef/resource/archive_file.rb +17 -14
  27. data/lib/chef/resource/chef_client_scheduled_task.rb +45 -2
  28. data/lib/chef/resource/chocolatey_config.rb +13 -13
  29. data/lib/chef/resource/file/verification/json.rb +50 -0
  30. data/lib/chef/resource/file/verification/yaml.rb +52 -0
  31. data/lib/chef/resource/inspec_input.rb +128 -0
  32. data/lib/chef/resource/inspec_waiver.rb +185 -0
  33. data/lib/chef/resource/mount.rb +1 -1
  34. data/lib/chef/resource/registry_key.rb +36 -48
  35. data/lib/chef/resource/remote_file.rb +98 -2
  36. data/lib/chef/resource/timezone.rb +2 -2
  37. data/lib/chef/resource/user_ulimit.rb +1 -0
  38. data/lib/chef/resource/windows_printer.rb +1 -1
  39. data/lib/chef/resource/windows_uac.rb +3 -1
  40. data/lib/chef/resource/windows_user_privilege.rb +1 -1
  41. data/lib/chef/resources.rb +2 -0
  42. data/lib/chef/run_context/cookbook_compiler.rb +112 -28
  43. data/lib/chef/run_context.rb +31 -1
  44. data/lib/chef/secret_fetcher/akeyless_vault.rb +57 -0
  45. data/lib/chef/secret_fetcher/aws_secrets_manager.rb +1 -1
  46. data/lib/chef/secret_fetcher/azure_key_vault.rb +1 -1
  47. data/lib/chef/secret_fetcher/base.rb +1 -1
  48. data/lib/chef/secret_fetcher/hashi_vault.rb +100 -0
  49. data/lib/chef/secret_fetcher.rb +8 -2
  50. data/lib/chef/version.rb +1 -1
  51. data/spec/data/archive_file/test_archive.tar.gz +0 -0
  52. data/spec/functional/resource/archive_file_spec.rb +87 -0
  53. data/spec/functional/resource/group_spec.rb +5 -1
  54. data/spec/functional/resource/link_spec.rb +8 -0
  55. data/spec/integration/compliance/compliance_spec.rb +60 -0
  56. data/spec/spec_helper.rb +3 -0
  57. data/spec/support/platform_helpers.rb +4 -0
  58. data/spec/support/ruby_installer.rb +51 -0
  59. data/spec/unit/compliance/input_spec.rb +104 -0
  60. data/spec/unit/compliance/profile_spec.rb +120 -0
  61. data/spec/unit/compliance/waiver_spec.rb +104 -0
  62. data/spec/unit/http/basic_client_spec.rb +30 -0
  63. data/spec/unit/http_spec.rb +8 -2
  64. data/spec/unit/provider/link_spec.rb +13 -7
  65. data/spec/unit/provider/remote_file/http_spec.rb +10 -0
  66. data/spec/unit/provider/template_spec.rb +2 -2
  67. data/spec/unit/resource/archive_file_spec.rb +414 -3
  68. data/spec/unit/resource/chef_client_scheduled_task_spec.rb +69 -0
  69. data/spec/unit/resource/file/verification/json_spec.rb +72 -0
  70. data/spec/unit/resource/file/verification/yaml_spec.rb +67 -0
  71. data/spec/unit/resource/inspec_input_spec.rb +300 -0
  72. data/spec/unit/resource/inspec_waiver_spec.rb +312 -0
  73. data/spec/unit/resource/mount_spec.rb +10 -0
  74. data/spec/unit/resource/user_ulimit_spec.rb +14 -1
  75. data/spec/unit/secret_fetcher/akeyless_vault_spec.rb +37 -0
  76. data/spec/unit/secret_fetcher/hashi_vault_spec.rb +80 -0
  77. data/tasks/rspec.rb +2 -1
  78. metadata +60 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84ec7e8ea8d183bc0319fbf78ed7eb5f6ea0830020bde33233f96ca1c26947bc
4
- data.tar.gz: e49b65d28c30682629d84258b8352d4076fe650156ef5f314507a9fe0adb2ba8
3
+ metadata.gz: 95f1d69acabeb3bd1db7ead6e0e99b6f7b49a737807bc2175160dfe7a079ad21
4
+ data.tar.gz: ec4b4c4fd756e3c1bb6bb3933e5c5351c5bd2aa03c0f9f49277b5b4036306022
5
5
  SHA512:
6
- metadata.gz: ad784e028b0347e81dfb3b75dd6c6d58c143d2d87a3bbd746eadbcf4d35f1c3cb242d4c4f1dcd2a2d16d66bb05232eb902ef65b4dd31bbc68f41dd5a81d9e5a1
7
- data.tar.gz: f78342cb9b9410cc931f9158388faff31e1a34786e93c4e6892c7c903086b171a363f295adc203c473a3806de987a7a403b3699b105622d87dbe73085b1600f3
6
+ metadata.gz: 44d58787691091de198d15514755bb446df1add42ec991ced1616ea71befaf056628b3fe2f2925a44c40bf57da512c3d2d31a31beef32f1953f4e30d5d19bd08
7
+ data.tar.gz: 90ba00a5ef06ef90942d33ed0c9bbd2c22e7099301307daea21b595031c390ac6f21d6412e66b43920bafec80c0a1d20a8e7d2f11a65f8f85538bff567d8a9e3
data/chef.gemspec CHANGED
@@ -55,7 +55,9 @@ Gem::Specification.new do |s|
55
55
 
56
56
  s.add_dependency "proxifier", "~> 1.0"
57
57
 
58
+ s.add_dependency "aws-sdk-s3", "~> 1.91" # s3 recipe-url support
58
59
  s.add_dependency "aws-sdk-secretsmanager", "~> 1.46"
60
+ s.add_dependency "vault", "~> 0.16" # hashi vault official client gem
59
61
  s.bindir = "bin"
60
62
  s.executables = %w{ }
61
63
 
@@ -378,9 +378,19 @@ class Chef::Application::Base < Chef::Application
378
378
 
379
379
  def fetch_recipe_tarball(url, path)
380
380
  require "open-uri" unless defined?(OpenURI)
381
+ uri = URI.parse(url)
382
+
381
383
  Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
382
384
  if File.exist?(url)
383
385
  FileUtils.cp(url, path)
386
+ elsif uri.scheme == "s3"
387
+ require "aws-sdk-s3" unless defined?(Aws::S3)
388
+
389
+ s3 = Aws::S3::Client.new
390
+ object = s3.get_object(bucket: uri.hostname, key: uri.path[1..-1])
391
+ File.open(path, "wb") do |f|
392
+ f.write(object.body.read)
393
+ end
384
394
  elsif URI::DEFAULT_PARSER.make_regexp.match?(url)
385
395
  File.open(path, "wb") do |f|
386
396
  URI.open(url) do |r|
@@ -388,7 +398,7 @@ class Chef::Application::Base < Chef::Application
388
398
  end
389
399
  end
390
400
  else
391
- Chef::Application.fatal! "You specified --recipe-url but the value is neither a valid URL nor a path to a file that exists on disk." +
401
+ Chef::Application.fatal! "You specified --recipe-url but the value is neither a valid URL, an S3 bucket nor a path to a file that exists on disk." +
392
402
  "Please confirm the location of the tarball and try again."
393
403
  end
394
404
  end
data/lib/chef/client.rb CHANGED
@@ -241,8 +241,7 @@ class Chef
241
241
 
242
242
  run_status.run_id = request_id = Chef::RequestID.instance.request_id
243
243
 
244
- @run_context = Chef::RunContext.new
245
- run_context.events = events
244
+ @run_context = Chef::RunContext.new(nil, nil, events)
246
245
  run_status.run_context = run_context
247
246
 
248
247
  events.run_start(Chef::VERSION, run_status)
@@ -0,0 +1,115 @@
1
+ #
2
+ # Copyright:: Copyright (c) Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "yaml"
19
+
20
+ class Chef
21
+ module Compliance
22
+ #
23
+ # Chef object that represents a single input file in the compliance segment
24
+ # of a cookbook.
25
+ #
26
+ class Input
27
+ # @return [Boolean] if the input has been enabled
28
+ attr_reader :enabled
29
+
30
+ # @return [String] The name of the cookbook that the input is in
31
+ attr_reader :cookbook_name
32
+
33
+ # @return [String] The full path on the host to the input yml file
34
+ attr_reader :path
35
+
36
+ # @return [String] the pathname in the cookbook
37
+ attr_reader :pathname
38
+
39
+ # Event dispatcher for this run.
40
+ #
41
+ # @return [Chef::EventDispatch::Dispatcher]
42
+ #
43
+ attr_reader :events
44
+
45
+ # @api private
46
+ attr_reader :data
47
+
48
+ def initialize(events, data, path, cookbook_name)
49
+ @events = events
50
+ @data = data
51
+ @cookbook_name = cookbook_name
52
+ @path = path
53
+ @pathname = File.basename(path, File.extname(path)) unless path.nil?
54
+ disable!
55
+ end
56
+
57
+ # @return [Boolean] if the input has been enabled
58
+ #
59
+ def enabled?
60
+ !!@enabled
61
+ end
62
+
63
+ # Set the input to being enabled
64
+ #
65
+ def enable!
66
+ events.compliance_input_enabled(self)
67
+ @enabled = true
68
+ end
69
+
70
+ # Set the input as being disabled
71
+ #
72
+ def disable!
73
+ @enabled = false
74
+ end
75
+
76
+ # Render the input in a way that it can be consumed by inspec
77
+ #
78
+ def inspec_data
79
+ data
80
+ end
81
+
82
+ HIDDEN_IVARS = [ :@events ].freeze
83
+
84
+ # Omit the event object from error output
85
+ #
86
+ def inspect
87
+ ivar_string = (instance_variables.map(&:to_sym) - HIDDEN_IVARS).map do |ivar|
88
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
89
+ end.join(", ")
90
+ "#<#{self.class}:#{object_id} #{ivar_string}>"
91
+ end
92
+
93
+ # Helper to construct a input object from a hash. Since the path and
94
+ # cookbook_name are required this is probably not externally useful.
95
+ #
96
+ def self.from_hash(events, hash, path = nil, cookbook_name = nil)
97
+ new(events, hash, path, cookbook_name)
98
+ end
99
+
100
+ # Helper to construct a input object from a yaml string. Since the path
101
+ # and cookbook_name are required this is probably not externally useful.
102
+ #
103
+ def self.from_yaml(events, string, path = nil, cookbook_name = nil)
104
+ from_hash(events, YAML.load(string), path, cookbook_name)
105
+ end
106
+
107
+ # @param filename [String] full path to the yml file in the cookbook
108
+ # @param cookbook_name [String] cookbook that the input is in
109
+ #
110
+ def self.from_file(events, filename, cookbook_name = nil)
111
+ from_yaml(events, IO.read(filename), filename, cookbook_name)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,139 @@
1
+ # Copyright:: Copyright (c) Chef Software Inc.
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require_relative "input"
18
+
19
+ class Chef
20
+ module Compliance
21
+ class InputCollection < Array
22
+
23
+ # Event dispatcher for this run.
24
+ #
25
+ # @return [Chef::EventDispatch::Dispatcher]
26
+ #
27
+ attr_reader :events
28
+
29
+ def initialize(events)
30
+ @events = events
31
+ end
32
+
33
+ # Add a input to the input collection. The cookbook_name needs to be determined by the
34
+ # caller and is used in the `include_input` API to match on. The path should be the complete
35
+ # path on the host of the yml file, including the filename.
36
+ #
37
+ # @param path [String]
38
+ # @param cookbook_name [String]
39
+ #
40
+ def from_file(filename, cookbook_name)
41
+ new_input = Input.from_file(events, filename, cookbook_name)
42
+ self << new_input
43
+ events.compliance_input_loaded(new_input)
44
+ end
45
+
46
+ # Add a input from a raw hash. This input will be enabled by default.
47
+ #
48
+ # @param path [String]
49
+ # @param cookbook_name [String]
50
+ #
51
+ def from_hash(hash)
52
+ new_input = Input.from_hash(events, hash)
53
+ new_input.enable!
54
+ self << new_input
55
+ end
56
+
57
+ # @return [Array<Input>] inspec inputs which are enabled in a form suitable to pass to inspec
58
+ #
59
+ def inspec_data
60
+ select(&:enabled?).each_with_object({}) { |input, hash| hash.merge(input.inspec_data) }
61
+ end
62
+
63
+ # DSL method to enable input files. This matches on the filename of the input file.
64
+ # If the specific input is omitted then it uses the default input. The string
65
+ # supports regular expression matching.
66
+ #
67
+ # @example Specific input file in a cookbook
68
+ #
69
+ # include_input "acme_cookbook::ssh-001"
70
+ #
71
+ # @example The compliance/inputs/default.yml input in a cookbook
72
+ #
73
+ # include_input "acme_cookbook"
74
+ #
75
+ # @example Every input file in a cookbook
76
+ #
77
+ # include_input "acme_cookbook::.*"
78
+ #
79
+ # @example Matching inputs by regexp in a cookbook
80
+ #
81
+ # include_input "acme_cookbook::ssh.*"
82
+ #
83
+ # @example Matching inputs by regexp in any cookbook in the cookbook collection
84
+ #
85
+ # include_input ".*::ssh.*"
86
+ #
87
+ # @example Adding an arbitrary hash of data (not from any file in a cookbook)
88
+ #
89
+ # include_input({ "ssh_custom_path": "/usr/local/bin" })
90
+ #
91
+ def include_input(arg)
92
+ raise "include_input was given a nil value" if arg.nil?
93
+
94
+ # if we're given a hash argument just shove it in the raw_hash
95
+ if arg.is_a?(Hash)
96
+ from_hash(arg)
97
+ return
98
+ end
99
+
100
+ matching_inputs(arg).each(&:enable!)
101
+ end
102
+
103
+ def valid?(arg)
104
+ !matching_inputs(arg).empty?
105
+ end
106
+
107
+ HIDDEN_IVARS = [ :@events ].freeze
108
+
109
+ # Omit the event object from error output
110
+ #
111
+ def inspect
112
+ ivar_string = (instance_variables.map(&:to_sym) - HIDDEN_IVARS).map do |ivar|
113
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
114
+ end.join(", ")
115
+ "#<#{self.class}:#{object_id} #{ivar_string}>"
116
+ end
117
+
118
+ private
119
+
120
+ def matching_inputs(arg, should_raise: false)
121
+ (cookbook_name, input_name) = arg.split("::")
122
+
123
+ input_name = "default" if input_name.nil?
124
+
125
+ inputs = select { |input| /^#{cookbook_name}$/.match?(input.cookbook_name) && /^#{input_name}$/.match?(input.pathname) }
126
+
127
+ if inputs.empty? && should_raise
128
+ raise "No inspec inputs matching '#{input_name}' found in cookbooks matching '#{cookbook_name}'"
129
+ end
130
+
131
+ inputs
132
+ end
133
+
134
+ def matching_inputs!(arg)
135
+ matching_inputs(arg, should_raise: true)
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,122 @@
1
+ #
2
+ # Copyright:: Copyright (c) Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ class Chef
19
+ module Compliance
20
+ class Profile
21
+ # @return [Boolean] if the profile has been enabled
22
+ attr_accessor :enabled
23
+
24
+ # @return [String] The full path on the host to the profile inspec.yml
25
+ attr_reader :path
26
+
27
+ # @return [String] The name of the cookbook that the profile is in
28
+ attr_reader :cookbook_name
29
+
30
+ # @return [String] the pathname in the cookbook
31
+ attr_accessor :pathname
32
+
33
+ # @return [Chef::EventDispatch::Dispatcher] Event dispatcher for this run.
34
+ attr_reader :events
35
+
36
+ # @api private
37
+ attr_reader :data
38
+
39
+ def initialize(events, data, path, cookbook_name)
40
+ @events = events
41
+ @data = data
42
+ @path = path
43
+ @cookbook_name = cookbook_name
44
+ @pathname = File.basename(File.dirname(path))
45
+ disable!
46
+ validate!
47
+ end
48
+
49
+ # @return [String] name of the inspec profile from parsing the inspec.yml
50
+ def name
51
+ @data["name"]
52
+ end
53
+
54
+ # @return [String] version of the inspec profile from parsing the inspec.yml
55
+ def version
56
+ @data["version"]
57
+ end
58
+
59
+ # Raises if the inspec profile is not valid.
60
+ #
61
+ def validate!
62
+ raise "Inspec profile at #{path} has no name" unless name
63
+ end
64
+
65
+ # @return [Boolean] if the profile has been enabled
66
+ def enabled?
67
+ !!@enabled
68
+ end
69
+
70
+ # Set the profile to being enabled
71
+ #
72
+ def enable!
73
+ events.compliance_profile_enabled(self)
74
+ @enabled = true
75
+ end
76
+
77
+ # Set the profile as being disabled
78
+ #
79
+ def disable!
80
+ @enabled = false
81
+ end
82
+
83
+ # Render the profile in a way that it can be consumed by inspec
84
+ #
85
+ def inspec_data
86
+ { name: name, path: File.dirname(path) }
87
+ end
88
+
89
+ HIDDEN_IVARS = [ :@events ].freeze
90
+
91
+ # Omit the event object from error output
92
+ #
93
+ def inspect
94
+ ivar_string = (instance_variables.map(&:to_sym) - HIDDEN_IVARS).map do |ivar|
95
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
96
+ end.join(", ")
97
+ "#<#{self.class}:#{object_id} #{ivar_string}>"
98
+ end
99
+
100
+ # Helper to construct a profile object from a hash. Since the path and
101
+ # cookbook_name are required this is probably not externally useful.
102
+ #
103
+ def self.from_hash(events, hash, path, cookbook_name)
104
+ new(events, hash, path, cookbook_name)
105
+ end
106
+
107
+ # Helper to construct a profile object from a yaml string. Since the path
108
+ # and cookbook_name are required this is probably not externally useful.
109
+ #
110
+ def self.from_yaml(events, string, path, cookbook_name)
111
+ from_hash(events, YAML.load(string), path, cookbook_name)
112
+ end
113
+
114
+ # @param filename [String] full path to the inspec.yml file in the cookbook
115
+ # @param cookbook_name [String] cookbook that the profile is in
116
+ #
117
+ def self.from_file(events, filename, cookbook_name)
118
+ from_yaml(events, IO.read(filename), filename, cookbook_name)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,109 @@
1
+ #
2
+ # Copyright:: Copyright (c) Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require_relative "profile"
19
+
20
+ class Chef
21
+ module Compliance
22
+ class ProfileCollection < Array
23
+
24
+ # Event dispatcher for this run.
25
+ #
26
+ # @return [Chef::EventDispatch::Dispatcher]
27
+ #
28
+ attr_reader :events
29
+
30
+ def initialize(events)
31
+ @events = events
32
+ end
33
+
34
+ # Add a profile to the profile collection. The cookbook_name needs to be determined by the
35
+ # caller and is used in the `include_profile` API to match on. The path should be the complete
36
+ # path on the host of the inspec.yml file, including the filename.
37
+ #
38
+ # @param path [String]
39
+ # @param cookbook_name [String]
40
+ #
41
+ def from_file(path, cookbook_name)
42
+ new_profile = Profile.from_file(events, path, cookbook_name)
43
+ self << new_profile
44
+ events.compliance_profile_loaded(new_profile)
45
+ end
46
+
47
+ # @return [Boolean] if any of the profiles are enabled
48
+ #
49
+ def using_profiles?
50
+ any?(&:enabled?)
51
+ end
52
+
53
+ # @return [Array<Profile>] inspec profiles which are enabled in a form suitable to pass to inspec
54
+ #
55
+ def inspec_data
56
+ select(&:enabled?).each_with_object([]) { |profile, arry| arry << profile.inspec_data }
57
+ end
58
+
59
+ # DSL method to enable profile files. This matches on the name of the profile being included it
60
+ # does not match on the filename of the input file. If the specific profile is omitted then
61
+ # it uses the default profile. The string supports regular expression matching.
62
+ #
63
+ # @example Specific profile in a cookbook
64
+ #
65
+ # include_profile "acme_cookbook::ssh-001"
66
+ #
67
+ # @example The profile named "default" in a cookbook
68
+ #
69
+ # include_profile "acme_cookbook"
70
+ #
71
+ # @example Every profile in a cookbook
72
+ #
73
+ # include_profile "acme_cookbook::.*"
74
+ #
75
+ # @example Matching profiles by regexp in a cookbook
76
+ #
77
+ # include_profile "acme_cookbook::ssh.*"
78
+ #
79
+ # @example Matching profiles by regexp in any cookbook in the cookbook collection
80
+ #
81
+ # include_profile ".*::ssh.*"
82
+ #
83
+ def include_profile(arg)
84
+ (cookbook_name, profile_name) = arg.split("::")
85
+
86
+ profile_name = "default" if profile_name.nil?
87
+
88
+ profiles = select { |profile| /^#{cookbook_name}$/.match?(profile.cookbook_name) && /^#{profile_name}$/.match?(profile.pathname) }
89
+
90
+ if profiles.empty?
91
+ raise "No inspec profiles matching '#{profile_name}' found in cookbooks matching '#{cookbook_name}'"
92
+ end
93
+
94
+ profiles.each(&:enable!)
95
+ end
96
+
97
+ HIDDEN_IVARS = [ :@events ].freeze
98
+
99
+ # Omit the event object from error output
100
+ #
101
+ def inspect
102
+ ivar_string = (instance_variables.map(&:to_sym) - HIDDEN_IVARS).map do |ivar|
103
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
104
+ end.join(", ")
105
+ "#<#{self.class}:#{object_id} #{ivar_string}>"
106
+ end
107
+ end
108
+ end
109
+ end
@@ -12,6 +12,8 @@ class Chef
12
12
 
13
13
  attr_accessor :run_id
14
14
  attr_reader :node
15
+ attr_reader :run_context
16
+
15
17
  def_delegators :node, :logger
16
18
 
17
19
  def enabled?
@@ -25,7 +27,9 @@ class Chef
25
27
  logger.debug("#{self.class}##{__method__}: audit cookbook? #{audit_cookbook_present}")
26
28
  logger.debug("#{self.class}##{__method__}: compliance phase attr? #{node["audit"]["compliance_phase"]}")
27
29
 
28
- if node["audit"]["compliance_phase"].nil?
30
+ if safe_profile_collection&.using_profiles?
31
+ true
32
+ elsif node["audit"]["compliance_phase"].nil?
29
33
  inspec_profiles.any? && !audit_cookbook_present
30
34
  else
31
35
  node["audit"]["compliance_phase"]
@@ -41,6 +45,14 @@ class Chef
41
45
  self.node = node
42
46
  end
43
47
 
48
+ # This hook gives us the run_context immediately after it is created so that we can wire up this object to it.
49
+ #
50
+ # (see EventDispatch::Base#)
51
+ #
52
+ def cookbook_compilation_start(run_context)
53
+ @run_context = run_context
54
+ end
55
+
44
56
  def run_started(run_status)
45
57
  self.run_id = run_status.run_id
46
58
  end
@@ -121,8 +133,16 @@ class Chef
121
133
  end
122
134
  end
123
135
 
136
+ def inputs_from_collection
137
+ safe_input_collection&.inspec_data || {}
138
+ end
139
+
140
+ def waivers_from_collection
141
+ safe_waiver_collection&.inspec_data || {}
142
+ end
143
+
124
144
  def inspec_opts
125
- inputs = inputs_from_attributes
145
+ inputs = inputs_from_attributes.merge(inputs_from_collection).merge(waivers_from_collection)
126
146
 
127
147
  if node["audit"]["chef_node_attribute_enabled"]
128
148
  inputs["chef_node"] = node.to_h
@@ -133,24 +153,34 @@ class Chef
133
153
  backend_cache: node["audit"]["inspec_backend_cache"],
134
154
  inputs: inputs,
135
155
  logger: logger,
156
+ # output: STDOUT,
136
157
  output: node["audit"]["quiet"] ? ::File::NULL : STDOUT,
137
158
  report: true,
138
159
  reporter: ["json-automate"],
160
+ # reporter: ["cli"],
139
161
  reporter_backtrace_inclusion: node["audit"]["result_include_backtrace"],
140
162
  reporter_message_truncation: node["audit"]["result_message_limit"],
141
- waiver_file: Array(node["audit"]["waiver_file"]),
163
+ waiver_file: waiver_files,
142
164
  }
143
165
  end
144
166
 
167
+ def waiver_files
168
+ Array(node["audit"]["waiver_file"])
169
+ end
170
+
145
171
  def inspec_profiles
146
172
  profiles = node["audit"]["profiles"]
147
173
  unless profiles.respond_to?(:map) && profiles.all? { |_, p| p.respond_to?(:transform_keys) && p.respond_to?(:update) }
148
174
  raise "CMPL010: #{Inspec::Dist::PRODUCT_NAME} profiles specified in an unrecognized format, expected a hash of hashes."
149
175
  end
150
176
 
151
- profiles.map do |name, profile|
177
+ from_attributes = profiles.map do |name, profile|
152
178
  profile.transform_keys(&:to_sym).update(name: name)
153
- end
179
+ end || []
180
+
181
+ from_cookbooks = safe_profile_collection&.inspec_data || []
182
+
183
+ from_attributes + from_cookbooks
154
184
  end
155
185
 
156
186
  def load_fetchers!
@@ -316,6 +346,18 @@ class Chef
316
346
 
317
347
  @validation_passed = true
318
348
  end
349
+
350
+ def safe_profile_collection
351
+ run_context&.profile_collection
352
+ end
353
+
354
+ def safe_waiver_collection
355
+ run_context&.waiver_collection
356
+ end
357
+
358
+ def safe_input_collection
359
+ run_context&.input_collection
360
+ end
319
361
  end
320
362
  end
321
363
  end