chef 17.4.38-universal-mingw32 → 17.7.22-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/chef.gemspec +3 -0
  4. data/lib/chef/application/base.rb +11 -1
  5. data/lib/chef/chef_fs/file_pattern.rb +1 -1
  6. data/lib/chef/chef_fs/path_utils.rb +1 -1
  7. data/lib/chef/client.rb +1 -2
  8. data/lib/chef/compliance/input.rb +115 -0
  9. data/lib/chef/compliance/input_collection.rb +139 -0
  10. data/lib/chef/compliance/profile.rb +122 -0
  11. data/lib/chef/compliance/profile_collection.rb +109 -0
  12. data/lib/chef/compliance/runner.rb +47 -5
  13. data/lib/chef/compliance/waiver.rb +115 -0
  14. data/lib/chef/compliance/waiver_collection.rb +143 -0
  15. data/lib/chef/data_collector/run_end_message.rb +1 -1
  16. data/lib/chef/dsl/compliance.rb +38 -0
  17. data/lib/chef/dsl/reader_helpers.rb +51 -0
  18. data/lib/chef/dsl/reboot_pending.rb +1 -1
  19. data/lib/chef/dsl/recipe.rb +4 -2
  20. data/lib/chef/dsl/secret.rb +2 -4
  21. data/lib/chef/dsl/universal.rb +2 -0
  22. data/lib/chef/event_dispatch/base.rb +44 -2
  23. data/lib/chef/exceptions.rb +10 -0
  24. data/lib/chef/formatters/doc.rb +46 -0
  25. data/lib/chef/http/basic_client.rb +15 -7
  26. data/lib/chef/http.rb +7 -3
  27. data/lib/chef/provider/cron.rb +4 -1
  28. data/lib/chef/provider/file.rb +2 -0
  29. data/lib/chef/provider/git.rb +1 -1
  30. data/lib/chef/provider/ifconfig/debian.rb +1 -1
  31. data/lib/chef/provider/link.rb +2 -2
  32. data/lib/chef/provider/registry_key.rb +3 -2
  33. data/lib/chef/provider/remote_file/http.rb +1 -1
  34. data/lib/chef/provider/subversion.rb +5 -5
  35. data/lib/chef/provider/template.rb +1 -1
  36. data/lib/chef/resource/archive_file.rb +17 -14
  37. data/lib/chef/resource/chef_client_scheduled_task.rb +45 -2
  38. data/lib/chef/resource/chocolatey_config.rb +14 -14
  39. data/lib/chef/resource/chocolatey_feature.rb +1 -1
  40. data/lib/chef/resource/chocolatey_source.rb +24 -2
  41. data/lib/chef/resource/directory.rb +1 -1
  42. data/lib/chef/resource/file/verification/json.rb +50 -0
  43. data/lib/chef/resource/file/verification/yaml.rb +52 -0
  44. data/lib/chef/resource/habitat_install.rb +3 -3
  45. data/lib/chef/resource/inspec_input.rb +127 -0
  46. data/lib/chef/resource/inspec_waiver.rb +184 -0
  47. data/lib/chef/resource/inspec_waiver_file_entry.rb +1 -1
  48. data/lib/chef/resource/kernel_module.rb +27 -2
  49. data/lib/chef/resource/macos_userdefaults.rb +43 -128
  50. data/lib/chef/resource/mount.rb +1 -1
  51. data/lib/chef/resource/openssl_x509_certificate.rb +1 -1
  52. data/lib/chef/resource/powershell_package_source.rb +234 -70
  53. data/lib/chef/resource/registry_key.rb +36 -48
  54. data/lib/chef/resource/remote_file.rb +98 -2
  55. data/lib/chef/resource/timezone.rb +2 -2
  56. data/lib/chef/resource/user_ulimit.rb +1 -0
  57. data/lib/chef/resource/windows_auto_run.rb +1 -1
  58. data/lib/chef/resource/windows_dfs_namespace.rb +2 -2
  59. data/lib/chef/resource/windows_printer.rb +1 -1
  60. data/lib/chef/resource/windows_uac.rb +3 -1
  61. data/lib/chef/resource/windows_update_settings.rb +3 -3
  62. data/lib/chef/resource/windows_user_privilege.rb +1 -1
  63. data/lib/chef/resource.rb +1 -1
  64. data/lib/chef/resource_reporter.rb +1 -1
  65. data/lib/chef/resources.rb +2 -0
  66. data/lib/chef/run_context/cookbook_compiler.rb +112 -28
  67. data/lib/chef/run_context.rb +31 -1
  68. data/lib/chef/secret_fetcher/akeyless_vault.rb +57 -0
  69. data/lib/chef/secret_fetcher/aws_secrets_manager.rb +1 -1
  70. data/lib/chef/secret_fetcher/azure_key_vault.rb +63 -9
  71. data/lib/chef/secret_fetcher/base.rb +1 -1
  72. data/lib/chef/secret_fetcher/hashi_vault.rb +100 -0
  73. data/lib/chef/secret_fetcher.rb +8 -3
  74. data/lib/chef/version.rb +1 -1
  75. data/lib/chef/win32/version.rb +2 -1
  76. data/spec/data/archive_file/test_archive.tar.gz +0 -0
  77. data/spec/functional/dsl/reboot_pending_spec.rb +3 -3
  78. data/spec/functional/dsl/registry_helper_spec.rb +1 -1
  79. data/spec/functional/resource/archive_file_spec.rb +87 -0
  80. data/spec/functional/resource/dsc_script_spec.rb +2 -2
  81. data/spec/functional/resource/group_spec.rb +5 -1
  82. data/spec/functional/resource/link_spec.rb +8 -0
  83. data/spec/functional/resource/macos_userdefaults_spec.rb +119 -0
  84. data/spec/functional/resource/powershell_package_source_spec.rb +5 -6
  85. data/spec/functional/resource/registry_spec.rb +81 -81
  86. data/spec/functional/win32/registry_spec.rb +8 -8
  87. data/spec/integration/compliance/compliance_spec.rb +60 -0
  88. data/spec/spec_helper.rb +3 -0
  89. data/spec/support/platform_helpers.rb +4 -0
  90. data/spec/support/ruby_installer.rb +51 -0
  91. data/spec/unit/compliance/input_spec.rb +104 -0
  92. data/spec/unit/compliance/profile_spec.rb +120 -0
  93. data/spec/unit/compliance/waiver_spec.rb +104 -0
  94. data/spec/unit/data_collector_spec.rb +24 -1
  95. data/spec/unit/dsl/reboot_pending_spec.rb +1 -1
  96. data/spec/unit/http/basic_client_spec.rb +30 -0
  97. data/spec/unit/http_spec.rb +8 -2
  98. data/spec/unit/mixin/default_paths_spec.rb +1 -1
  99. data/spec/unit/mixin/securable_spec.rb +3 -3
  100. data/spec/unit/provider/cron_spec.rb +45 -0
  101. data/spec/unit/provider/link_spec.rb +13 -7
  102. data/spec/unit/provider/package/rubygems_spec.rb +5 -5
  103. data/spec/unit/provider/package/windows_spec.rb +1 -1
  104. data/spec/unit/provider/registry_key_spec.rb +4 -4
  105. data/spec/unit/provider/remote_file/http_spec.rb +10 -0
  106. data/spec/unit/provider/service/windows_spec.rb +5 -5
  107. data/spec/unit/provider/subversion_spec.rb +4 -4
  108. data/spec/unit/provider/template_spec.rb +2 -2
  109. data/spec/unit/provider/windows_env_spec.rb +1 -1
  110. data/spec/unit/provider/zypper_repository_spec.rb +1 -1
  111. data/spec/unit/resource/archive_file_spec.rb +414 -3
  112. data/spec/unit/resource/chef_client_scheduled_task_spec.rb +69 -0
  113. data/spec/unit/resource/chocolatey_config_spec.rb +1 -1
  114. data/spec/unit/resource/chocolatey_feature_spec.rb +1 -1
  115. data/spec/unit/resource/chocolatey_source_spec.rb +1 -1
  116. data/spec/unit/resource/file/verification/json_spec.rb +72 -0
  117. data/spec/unit/resource/file/verification/yaml_spec.rb +67 -0
  118. data/spec/unit/resource/inspec_input_spec.rb +300 -0
  119. data/spec/unit/resource/inspec_waiver_spec.rb +312 -0
  120. data/spec/unit/resource/kernel_module_spec.rb +2 -1
  121. data/spec/unit/resource/macos_user_defaults_spec.rb +36 -96
  122. data/spec/unit/resource/mount_spec.rb +10 -0
  123. data/spec/unit/resource/powershell_package_source_spec.rb +63 -62
  124. data/spec/unit/resource/registry_key_spec.rb +10 -10
  125. data/spec/unit/resource/user_ulimit_spec.rb +14 -1
  126. data/spec/unit/resource/windows_auto_run_spec.rb +1 -1
  127. data/spec/unit/resource/windows_feature_powershell_spec.rb +1 -1
  128. data/spec/unit/resource/windows_firewall_rule_spec.rb +2 -2
  129. data/spec/unit/resource/windows_task_spec.rb +3 -3
  130. data/spec/unit/resource_reporter_spec.rb +2 -2
  131. data/spec/unit/resource_spec.rb +5 -0
  132. data/spec/unit/secret_fetcher/akeyless_vault_spec.rb +37 -0
  133. data/spec/unit/secret_fetcher/azure_key_vault_spec.rb +99 -20
  134. data/spec/unit/secret_fetcher/hashi_vault_spec.rb +80 -0
  135. data/spec/unit/util/backup_spec.rb +1 -1
  136. data/spec/unit/win32/registry_spec.rb +3 -3
  137. data/tasks/rspec.rb +2 -1
  138. metadata +75 -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: de3500fef9fe640bfe7197594dcf426e223aa7f2780c7672abad2f307596236a
4
+ data.tar.gz: 4b30b19efeea5d20d3b039dd2a04e8a1b478750676961a92d25f2e5041c5cac6
5
5
  SHA512:
6
- metadata.gz: ad784e028b0347e81dfb3b75dd6c6d58c143d2d87a3bbd746eadbcf4d35f1c3cb242d4c4f1dcd2a2d16d66bb05232eb902ef65b4dd31bbc68f41dd5a81d9e5a1
7
- data.tar.gz: f78342cb9b9410cc931f9158388faff31e1a34786e93c4e6892c7c903086b171a363f295adc203c473a3806de987a7a403b3699b105622d87dbe73085b1600f3
6
+ metadata.gz: 203a3046e662b1ecd6584e6d82284da42053787cdc133dba3fc859c642008d16f82a58f3ee97e75621cca97cb8d2d3f246927905de54697b1729f5c52b4c7d90
7
+ data.tar.gz: 6b4faa047fdfe94f1223716980e5399e8a3343eb9d00305d65e05414a8957f1ba42fcf29b327123aff5b293f28c3b86cb92992ea2961811a25ddb1d31898bb62
data/Gemfile CHANGED
@@ -39,6 +39,11 @@ group(:ruby_shadow) do
39
39
  gem "ruby-shadow", git: "https://github.com/chef/ruby-shadow", branch: "lcg/ruby-3.0", platforms: :ruby
40
40
  end
41
41
 
42
+ # deps that cannot be put in the knife gem because they require a compiler and fail on windows nodes
43
+ group(:knife_windows_deps) do
44
+ gem "ed25519", "~> 1.2" # ed25519 ssh key support
45
+ end
46
+
42
47
  group(:development, :test) do
43
48
  gem "rake"
44
49
  gem "rspec"
data/chef.gemspec CHANGED
@@ -52,10 +52,13 @@ Gem::Specification.new do |s|
52
52
  s.add_dependency "addressable"
53
53
  s.add_dependency "syslog-logger", "~> 1.6"
54
54
  s.add_dependency "uuidtools", ">= 2.1.5", "< 3.0" # osx_profile resource
55
+ s.add_dependency "corefoundation", "~> 0.3.4" # macos_userdefaults resource
55
56
 
56
57
  s.add_dependency "proxifier", "~> 1.0"
57
58
 
59
+ s.add_dependency "aws-sdk-s3", "~> 1.91" # s3 recipe-url support
58
60
  s.add_dependency "aws-sdk-secretsmanager", "~> 1.46"
61
+ s.add_dependency "vault", "~> 0.16" # hashi vault official client gem
59
62
  s.bindir = "bin"
60
63
  s.executables = %w{ }
61
64
 
@@ -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
@@ -276,7 +276,7 @@ class Chef
276
276
  regexp << ".*"
277
277
  when "*"
278
278
  exact = nil
279
- regexp << '[^\/]*'
279
+ regexp << "[^\\/]*"
280
280
  when "?"
281
281
  exact = nil
282
282
  regexp << "."
@@ -58,7 +58,7 @@ class Chef
58
58
  end
59
59
 
60
60
  def self.regexp_path_separator
61
- ChefUtils.windows? ? '[\/\\\\]' : "/"
61
+ ChefUtils.windows? ? "[\\/\\\\]" : "/"
62
62
  end
63
63
 
64
64
  # Given a server path, determines if it is absolute.
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