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.
- checksums.yaml +4 -4
- data/chef.gemspec +2 -0
- data/lib/chef/application/base.rb +11 -1
- data/lib/chef/client.rb +1 -2
- data/lib/chef/compliance/input.rb +115 -0
- data/lib/chef/compliance/input_collection.rb +139 -0
- data/lib/chef/compliance/profile.rb +122 -0
- data/lib/chef/compliance/profile_collection.rb +109 -0
- data/lib/chef/compliance/runner.rb +47 -5
- data/lib/chef/compliance/waiver.rb +115 -0
- data/lib/chef/compliance/waiver_collection.rb +143 -0
- data/lib/chef/dsl/compliance.rb +38 -0
- data/lib/chef/dsl/reader_helpers.rb +51 -0
- data/lib/chef/dsl/recipe.rb +4 -2
- data/lib/chef/dsl/secret.rb +2 -4
- data/lib/chef/dsl/universal.rb +2 -0
- data/lib/chef/event_dispatch/base.rb +44 -2
- data/lib/chef/formatters/doc.rb +46 -0
- data/lib/chef/http/basic_client.rb +15 -7
- data/lib/chef/http.rb +7 -3
- data/lib/chef/provider/file.rb +2 -0
- data/lib/chef/provider/link.rb +2 -2
- data/lib/chef/provider/registry_key.rb +3 -2
- data/lib/chef/provider/remote_file/http.rb +1 -1
- data/lib/chef/provider/template.rb +1 -1
- data/lib/chef/resource/archive_file.rb +17 -14
- data/lib/chef/resource/chef_client_scheduled_task.rb +45 -2
- data/lib/chef/resource/chocolatey_config.rb +13 -13
- data/lib/chef/resource/file/verification/json.rb +50 -0
- data/lib/chef/resource/file/verification/yaml.rb +52 -0
- data/lib/chef/resource/inspec_input.rb +128 -0
- data/lib/chef/resource/inspec_waiver.rb +185 -0
- data/lib/chef/resource/mount.rb +1 -1
- data/lib/chef/resource/registry_key.rb +36 -48
- data/lib/chef/resource/remote_file.rb +98 -2
- data/lib/chef/resource/timezone.rb +2 -2
- data/lib/chef/resource/user_ulimit.rb +1 -0
- data/lib/chef/resource/windows_printer.rb +1 -1
- data/lib/chef/resource/windows_uac.rb +3 -1
- data/lib/chef/resource/windows_user_privilege.rb +1 -1
- data/lib/chef/resources.rb +2 -0
- data/lib/chef/run_context/cookbook_compiler.rb +112 -28
- data/lib/chef/run_context.rb +31 -1
- data/lib/chef/secret_fetcher/akeyless_vault.rb +57 -0
- data/lib/chef/secret_fetcher/aws_secrets_manager.rb +1 -1
- data/lib/chef/secret_fetcher/azure_key_vault.rb +1 -1
- data/lib/chef/secret_fetcher/base.rb +1 -1
- data/lib/chef/secret_fetcher/hashi_vault.rb +100 -0
- data/lib/chef/secret_fetcher.rb +8 -2
- data/lib/chef/version.rb +1 -1
- data/spec/data/archive_file/test_archive.tar.gz +0 -0
- data/spec/functional/resource/archive_file_spec.rb +87 -0
- data/spec/functional/resource/group_spec.rb +5 -1
- data/spec/functional/resource/link_spec.rb +8 -0
- data/spec/integration/compliance/compliance_spec.rb +60 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/platform_helpers.rb +4 -0
- data/spec/support/ruby_installer.rb +51 -0
- data/spec/unit/compliance/input_spec.rb +104 -0
- data/spec/unit/compliance/profile_spec.rb +120 -0
- data/spec/unit/compliance/waiver_spec.rb +104 -0
- data/spec/unit/http/basic_client_spec.rb +30 -0
- data/spec/unit/http_spec.rb +8 -2
- data/spec/unit/provider/link_spec.rb +13 -7
- data/spec/unit/provider/remote_file/http_spec.rb +10 -0
- data/spec/unit/provider/template_spec.rb +2 -2
- data/spec/unit/resource/archive_file_spec.rb +414 -3
- data/spec/unit/resource/chef_client_scheduled_task_spec.rb +69 -0
- data/spec/unit/resource/file/verification/json_spec.rb +72 -0
- data/spec/unit/resource/file/verification/yaml_spec.rb +67 -0
- data/spec/unit/resource/inspec_input_spec.rb +300 -0
- data/spec/unit/resource/inspec_waiver_spec.rb +312 -0
- data/spec/unit/resource/mount_spec.rb +10 -0
- data/spec/unit/resource/user_ulimit_spec.rb +14 -1
- data/spec/unit/secret_fetcher/akeyless_vault_spec.rb +37 -0
- data/spec/unit/secret_fetcher/hashi_vault_spec.rb +80 -0
- data/tasks/rspec.rb +2 -1
- metadata +60 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95f1d69acabeb3bd1db7ead6e0e99b6f7b49a737807bc2175160dfe7a079ad21
|
4
|
+
data.tar.gz: ec4b4c4fd756e3c1bb6bb3933e5c5351c5bd2aa03c0f9f49277b5b4036306022
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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:
|
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
|