chef 16.7.61-universal-mingw32 → 16.9.20-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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -5
  3. data/README.md +2 -2
  4. data/chef.gemspec +12 -2
  5. data/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll +0 -0
  6. data/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll +0 -0
  7. data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll +0 -0
  8. data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll +0 -0
  9. data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb +0 -0
  10. data/distro/ruby_bin_folder/x86/Chef.PowerShell.dll +0 -0
  11. data/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll +0 -0
  12. data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll +0 -0
  13. data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll +0 -0
  14. data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb +0 -0
  15. data/lib/chef/application/base.rb +1 -1
  16. data/lib/chef/client.rb +3 -0
  17. data/lib/chef/compliance/default_attributes.rb +93 -0
  18. data/lib/chef/compliance/fetcher/automate.rb +69 -0
  19. data/lib/chef/compliance/fetcher/chef_server.rb +134 -0
  20. data/lib/chef/compliance/reporter/automate.rb +201 -0
  21. data/lib/chef/compliance/reporter/chef_server_automate.rb +94 -0
  22. data/lib/chef/compliance/reporter/compliance_enforcer.rb +20 -0
  23. data/lib/chef/compliance/reporter/json_file.rb +19 -0
  24. data/lib/chef/compliance/runner.rb +262 -0
  25. data/lib/chef/cookbook_manifest.rb +1 -0
  26. data/lib/chef/encrypted_data_bag_item/assertions.rb +1 -1
  27. data/lib/chef/exceptions.rb +4 -0
  28. data/lib/chef/http/ssl_policies.rb +33 -14
  29. data/lib/chef/knife/bootstrap/train_connector.rb +1 -1
  30. data/lib/chef/knife/core/formatting_options.rb +49 -0
  31. data/lib/chef/knife/core/node_presenter.rb +0 -25
  32. data/lib/chef/knife/core/status_presenter.rb +1 -26
  33. data/lib/chef/knife/core/ui.rb +4 -1
  34. data/lib/chef/knife/core/windows_bootstrap_context.rb +1 -1
  35. data/lib/chef/knife/node_show.rb +2 -1
  36. data/lib/chef/knife/search.rb +2 -1
  37. data/lib/chef/knife/ssh.rb +3 -1
  38. data/lib/chef/knife/status.rb +8 -11
  39. data/lib/chef/mixin/powershell_exec.rb +3 -1
  40. data/lib/chef/platform/query_helpers.rb +4 -4
  41. data/lib/chef/policy_builder/policyfile.rb +1 -1
  42. data/lib/chef/powershell.rb +2 -0
  43. data/lib/chef/provider/dsc_resource.rb +12 -24
  44. data/lib/chef/provider/dsc_script.rb +16 -20
  45. data/lib/chef/provider/git.rb +5 -5
  46. data/lib/chef/provider/package.rb +53 -19
  47. data/lib/chef/provider/package/dnf.rb +39 -12
  48. data/lib/chef/provider/package/dnf/dnf_helper.py +18 -5
  49. data/lib/chef/provider/package/dnf/python_helper.rb +6 -6
  50. data/lib/chef/provider/package/freebsd/pkgng.rb +3 -1
  51. data/lib/chef/provider/yum_repository.rb +2 -2
  52. data/lib/chef/resource/chef_client_config.rb +1 -1
  53. data/lib/chef/resource/chef_gem.rb +2 -2
  54. data/lib/chef/resource/cron/cron_d.rb +1 -0
  55. data/lib/chef/resource/dsc_script.rb +8 -1
  56. data/lib/chef/resource/file.rb +1 -1
  57. data/lib/chef/resource/gem_package.rb +2 -2
  58. data/lib/chef/resource/homebrew_cask.rb +3 -3
  59. data/lib/chef/resource/hostname.rb +3 -3
  60. data/lib/chef/resource/http_request.rb +1 -1
  61. data/lib/chef/resource/locale.rb +1 -1
  62. data/lib/chef/resource/mdadm.rb +2 -2
  63. data/lib/chef/resource/osx_profile.rb +7 -7
  64. data/lib/chef/resource/remote_directory.rb +1 -1
  65. data/lib/chef/resource/ruby.rb +1 -5
  66. data/lib/chef/resource/ruby_block.rb +1 -1
  67. data/lib/chef/resource/template.rb +2 -2
  68. data/lib/chef/resource/user/windows_user.rb +5 -0
  69. data/lib/chef/resource/windows_certificate.rb +9 -13
  70. data/lib/chef/resource/yum_repository.rb +5 -0
  71. data/lib/chef/resource_collection/resource_set.rb +1 -1
  72. data/lib/chef/util/dsc/configuration_generator.rb +52 -11
  73. data/lib/chef/util/dsc/lcm_output_parser.rb +3 -4
  74. data/lib/chef/util/dsc/local_configuration_manager.rb +17 -14
  75. data/lib/chef/util/dsc/resource_store.rb +5 -11
  76. data/lib/chef/version.rb +1 -1
  77. data/lib/chef/win32/api/file.rb +4 -0
  78. data/spec/data/rubygems.org/latest_specs.4.8.gz +0 -0
  79. data/spec/data/rubygems.org/nonexistent_gem +0 -0
  80. data/spec/data/rubygems.org/sexp_processor +0 -0
  81. data/spec/data/rubygems.org/sexp_processor-4.15.1.gemspec.rz +0 -0
  82. data/spec/data/ssl/binary/chef-rspec-der.cert +0 -0
  83. data/spec/data/ssl/binary/chef-rspec-der.key +0 -0
  84. data/spec/functional/resource/dnf_package_spec.rb +319 -16
  85. data/spec/functional/resource/dsc_script_spec.rb +3 -6
  86. data/spec/functional/resource/windows_certificate_spec.rb +204 -384
  87. data/spec/integration/client/client_spec.rb +2 -1
  88. data/spec/integration/compliance/compliance_spec.rb +81 -0
  89. data/spec/integration/recipes/recipe_dsl_spec.rb +1 -0
  90. data/spec/spec_helper.rb +1 -1
  91. data/spec/unit/client_spec.rb +1 -0
  92. data/spec/unit/compliance/fetcher/automate_spec.rb +134 -0
  93. data/spec/unit/compliance/fetcher/chef_server_spec.rb +93 -0
  94. data/spec/unit/compliance/reporter/automate_spec.rb +427 -0
  95. data/spec/unit/compliance/reporter/chef_server_automate_spec.rb +177 -0
  96. data/spec/unit/compliance/reporter/compliance_enforcer_spec.rb +48 -0
  97. data/spec/unit/compliance/runner_spec.rb +167 -0
  98. data/spec/unit/http/ssl_policies_spec.rb +107 -68
  99. data/spec/unit/knife/bootstrap_spec.rb +5 -17
  100. data/spec/unit/knife/core/node_editor_spec.rb +1 -1
  101. data/spec/unit/knife/core/status_presenter_spec.rb +54 -0
  102. data/spec/unit/mixin/openssl_helper_spec.rb +0 -7
  103. data/spec/unit/mixin/powershell_exec_spec.rb +1 -1
  104. data/spec/unit/platform/query_helpers_spec.rb +11 -12
  105. data/spec/unit/provider/dsc_resource_spec.rb +10 -27
  106. data/spec/unit/provider/dsc_script_spec.rb +1 -1
  107. data/spec/unit/provider/mount/windows_spec.rb +1 -0
  108. data/spec/unit/provider/package/freebsd/pkgng_spec.rb +1 -1
  109. data/spec/unit/provider/package/rubygems_spec.rb +39 -7
  110. data/spec/unit/provider/systemd_unit_spec.rb +1 -1
  111. data/spec/unit/resource/user/windows_user_spec.rb +36 -0
  112. data/spec/unit/resource/windows_certificate_spec.rb +12 -0
  113. data/spec/unit/util/dsc/configuration_generator_spec.rb +79 -0
  114. data/spec/unit/util/dsc/local_configuration_manager_spec.rb +27 -35
  115. metadata +55 -18
  116. data/lib/chef/util/powershell/cmdlet.rb +0 -169
  117. data/lib/chef/util/powershell/cmdlet_result.rb +0 -61
  118. data/spec/data/trusted_certs_empty/.gitkeep +0 -0
  119. data/spec/data/trusted_certs_empty/README.md +0 -1
  120. data/spec/functional/util/powershell/cmdlet_spec.rb +0 -111
  121. data/spec/scripts/ssl-serve.rb +0 -47
  122. data/spec/unit/util/powershell/cmdlet_spec.rb +0 -106
@@ -97,7 +97,8 @@ describe "chef-client" do
97
97
  before { file ".chef/knife.rb", "xxx.xxx" }
98
98
 
99
99
  it "should load .chef/knife.rb when -z is specified" do
100
- result = shell_out("#{chef_client} -z -o 'x::default'", cwd: path_to(""))
100
+ # On Solaris shell_out will invoke /bin/sh which doesn't understand how to correctly update ENV['PWD']
101
+ result = shell_out("#{chef_client} -z -o 'x::default'", cwd: path_to(""), env: { "PWD" => nil })
101
102
  # FATAL: Configuration error NoMethodError: undefined method `xxx' for nil:NilClass
102
103
  expect(result.stdout).to include("xxx")
103
104
  end
@@ -0,0 +1,81 @@
1
+ require "spec_helper"
2
+
3
+ require "support/shared/integration/integration_helper"
4
+ require "chef/mixin/shell_out"
5
+ require "chef-utils/dist"
6
+
7
+ describe "chef-client with audit mode" do
8
+
9
+ include IntegrationSupport
10
+ include Chef::Mixin::ShellOut
11
+
12
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..", "bin") }
13
+
14
+ # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
15
+ # following constraints are satisfied:
16
+ # * Windows: windows can only run batch scripts as bare executables. Rubygems
17
+ # creates batch wrappers for installed gems, but we don't have batch wrappers
18
+ # in the source tree.
19
+ # * Other `chef-client` in PATH: A common case is running the tests on a
20
+ # machine that has omnibus chef installed. In that case we need to ensure
21
+ # we're running `chef-client` from the source tree and not the external one.
22
+ # cf. CHEF-4914
23
+ let(:chef_client) { "bundle exec #{ChefUtils::Dist::Infra::CLIENT} --minimal-ohai" }
24
+
25
+ when_the_repository "has a custom profile" do
26
+ let(:report_file) { path_to("report_file.json") }
27
+
28
+ before do
29
+ directory "profiles/my-profile" do
30
+ file "inspec.yml", <<~FILE
31
+ ---
32
+ name: my-profile
33
+ FILE
34
+
35
+ directory "controls" do
36
+ file "my_control.rb", <<~FILE
37
+ control "my control" do
38
+ describe Dir.home do
39
+ it { should be_kind_of String }
40
+ end
41
+ end
42
+ FILE
43
+ end
44
+ end
45
+
46
+ file "attributes.json", <<~FILE
47
+ {
48
+ "audit": {
49
+ "json_file": {
50
+ "location": "#{report_file}"
51
+ },
52
+ "profiles": {
53
+ "my-profile": {
54
+ "path": "#{path_to("profiles/my-profile")}"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ FILE
60
+ end
61
+
62
+ it "should complete with success" do
63
+ result = shell_out!("#{chef_client} --local-mode --json-attributes #{path_to("attributes.json")}", cwd: chef_dir)
64
+ result.error!
65
+
66
+ inspec_report = JSON.parse(File.read(report_file))
67
+ expect(inspec_report["profiles"].length).to eq(1)
68
+
69
+ profile = inspec_report["profiles"].first
70
+ expect(profile["name"]).to eq("my-profile")
71
+ expect(profile["controls"].length).to eq(1)
72
+
73
+ control = profile["controls"].first
74
+ expect(control["id"]).to eq("my control")
75
+ expect(control["results"].length).to eq(1)
76
+
77
+ result = control["results"].first
78
+ expect(result["status"]).to eq("passed")
79
+ end
80
+ end
81
+ end
@@ -28,6 +28,7 @@ describe "Recipe DSL methods" do
28
28
  def provider
29
29
  Provider
30
30
  end
31
+
31
32
  class Provider < Chef::Provider
32
33
  def load_current_resource; end
33
34
 
@@ -31,7 +31,7 @@ $LOAD_PATH.unshift File.expand_path("../chef-utils/lib", __dir__)
31
31
 
32
32
  require "rubygems"
33
33
  require "rspec/mocks"
34
-
34
+ require "rexml/document"
35
35
  require "webmock/rspec"
36
36
 
37
37
  require "chef"
@@ -129,6 +129,7 @@ shared_context "a client run" do
129
129
  expect(client.events).to receive(:register).with(instance_of(Chef::DataCollector::Reporter))
130
130
  expect(client.events).to receive(:register).with(instance_of(Chef::ResourceReporter))
131
131
  expect(client.events).to receive(:register).with(instance_of(Chef::ActionCollection))
132
+ expect(client.events).to receive(:register).with(instance_of(Chef::Compliance::Runner))
132
133
  end
133
134
 
134
135
  def stub_for_node_load
@@ -0,0 +1,134 @@
1
+ require "spec_helper"
2
+ require "chef/compliance/fetcher/automate"
3
+
4
+ describe Chef::Compliance::Fetcher::Automate do
5
+ describe ".resolve" do
6
+ before do
7
+ Chef::Config[:data_collector] = {
8
+ server_url: "https://automate.test/data_collector",
9
+ token: token,
10
+ }
11
+ end
12
+
13
+ let(:token) { "fake_token" }
14
+
15
+ context "when target is a string" do
16
+ it "should resolve a compliance URL" do
17
+ res = Chef::Compliance::Fetcher::Automate.resolve("compliance://namespace/profile_name")
18
+
19
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
20
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
21
+ expect(res.target).to eq(expected)
22
+ end
23
+
24
+ it "raises an exception with no data collector token" do
25
+ Chef::Config[:data_collector].delete(:token)
26
+
27
+ expect {
28
+ Chef::Compliance::Fetcher::Automate.resolve("compliance://namespace/profile_name")
29
+ }.to raise_error(/No data-collector token set/)
30
+ end
31
+
32
+ it "includes the data collector token" do
33
+ expect(Chef::Compliance::Fetcher::Automate).to receive(:new).with(
34
+ "https://automate.test/compliance/profiles/namespace/profile_name/tar",
35
+ hash_including("token" => token)
36
+ ).and_call_original
37
+
38
+ res = Chef::Compliance::Fetcher::Automate.resolve("compliance://namespace/profile_name")
39
+
40
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
41
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
42
+ expect(res.target).to eq(expected)
43
+ end
44
+
45
+ it "returns nil with a non-compliance URL" do
46
+ res = Chef::Compliance::Fetcher::Automate.resolve("http://github.com/chef-cookbooks/audit")
47
+
48
+ expect(res).to eq(nil)
49
+ end
50
+ end
51
+
52
+ context "when target is a hash" do
53
+ it "should resolve a target with a version" do
54
+ res = Chef::Compliance::Fetcher::Automate.resolve(
55
+ compliance: "namespace/profile_name",
56
+ version: "1.2.3"
57
+ )
58
+
59
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
60
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/version/1.2.3/tar"
61
+ expect(res.target).to eq(expected)
62
+ end
63
+
64
+ it "should resolve a target without a version" do
65
+ res = Chef::Compliance::Fetcher::Automate.resolve(
66
+ compliance: "namespace/profile_name"
67
+ )
68
+
69
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
70
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
71
+ expect(res.target).to eq(expected)
72
+ end
73
+
74
+ it "uses url key when present" do
75
+ res = Chef::Compliance::Fetcher::Automate.resolve(
76
+ compliance: "namespace/profile_name",
77
+ version: "1.2.3",
78
+ url: "https://profile.server.test/profiles/profile_name/1.2.3"
79
+ )
80
+
81
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
82
+ expected = "https://profile.server.test/profiles/profile_name/1.2.3"
83
+ expect(res.target).to eq(expected)
84
+ end
85
+
86
+ it "does not include token in the config when url key is present" do
87
+ expect(Chef::Compliance::Fetcher::Automate).to receive(:new).with(
88
+ "https://profile.server.test/profiles/profile_name/1.2.3",
89
+ hash_including("token" => nil)
90
+ ).and_call_original
91
+
92
+ res = Chef::Compliance::Fetcher::Automate.resolve(
93
+ compliance: "namespace/profile_name",
94
+ version: "1.2.3",
95
+ url: "https://profile.server.test/profiles/profile_name/1.2.3"
96
+ )
97
+
98
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
99
+ expected = "https://profile.server.test/profiles/profile_name/1.2.3"
100
+ expect(res.target).to eq(expected)
101
+ end
102
+
103
+ it "raises an exception with no data collector token" do
104
+ Chef::Config[:data_collector].delete(:token)
105
+
106
+ expect {
107
+ Chef::Compliance::Fetcher::Automate.resolve(compliance: "namespace/profile_name")
108
+ }.to raise_error(Inspec::FetcherFailure, /No data-collector token set/)
109
+ end
110
+
111
+ it "includes the data collector token" do
112
+ expect(Chef::Compliance::Fetcher::Automate).to receive(:new).with(
113
+ "https://automate.test/compliance/profiles/namespace/profile_name/tar",
114
+ hash_including("token" => token)
115
+ ).and_call_original
116
+
117
+ res = Chef::Compliance::Fetcher::Automate.resolve(compliance: "namespace/profile_name")
118
+
119
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
120
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
121
+ expect(res.target).to eq(expected)
122
+ end
123
+
124
+ it "returns nil with a non-profile Hash" do
125
+ res = Chef::Compliance::Fetcher::Automate.resolve(
126
+ profile: "namespace/profile_name",
127
+ version: "1.2.3"
128
+ )
129
+
130
+ expect(res).to eq(nil)
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,93 @@
1
+ require "spec_helper"
2
+ require "chef/compliance/fetcher/chef_server"
3
+
4
+ describe Chef::Compliance::Fetcher::ChefServer do
5
+ let(:node) do
6
+ Chef::Node.new.tap do |n|
7
+ n.default["audit"] = {}
8
+ end
9
+ end
10
+
11
+ before :each do
12
+ allow(Chef).to receive(:node).and_return(node)
13
+
14
+ Chef::Config[:chef_server_url] = "http://127.0.0.1:8889/organizations/my_org"
15
+ end
16
+
17
+ describe ".resolve" do
18
+ context "when target is a string" do
19
+ it "should resolve a compliance URL" do
20
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("compliance://namespace/profile_name")
21
+
22
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
23
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/namespace/compliance/profile_name/tar"
24
+ expect(res.target).to eq(expected)
25
+ end
26
+
27
+ it "should add /compliance URL prefix if needed" do
28
+ node.default["audit"]["fetcher"] = "chef-server"
29
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("compliance://namespace/profile_name")
30
+
31
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
32
+ expected = "http://127.0.0.1:8889/compliance/organizations/my_org/owners/namespace/compliance/profile_name/tar"
33
+ expect(res.target).to eq(expected)
34
+ end
35
+
36
+ it "includes user in the URL if present" do
37
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("compliance://username@namespace/profile_name")
38
+
39
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
40
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/username@namespace/compliance/profile_name/tar"
41
+ expect(res.target).to eq(expected)
42
+ end
43
+
44
+ it "returns nil with a non-compliance URL" do
45
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("http://github.com/chef-cookbooks/audit")
46
+
47
+ expect(res).to eq(nil)
48
+ end
49
+ end
50
+
51
+ context "when target is a hash" do
52
+ it "should resolve a target with a version" do
53
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
54
+ compliance: "namespace/profile_name",
55
+ version: "1.2.3"
56
+ )
57
+
58
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
59
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/namespace/compliance/profile_name/version/1.2.3/tar"
60
+ expect(res.target).to eq(expected)
61
+ end
62
+
63
+ it "should resolve a target without a version" do
64
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
65
+ compliance: "namespace/profile_name"
66
+ )
67
+
68
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
69
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/namespace/compliance/profile_name/tar"
70
+ expect(res.target).to eq(expected)
71
+ end
72
+
73
+ it "includes user in the URL if present" do
74
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
75
+ compliance: "username@namespace/profile_name"
76
+ )
77
+
78
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
79
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/username@namespace/compliance/profile_name/tar"
80
+ expect(res.target).to eq(expected)
81
+ end
82
+
83
+ it "returns nil with a non-profile Hash" do
84
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
85
+ profile: "namespace/profile_name",
86
+ version: "1.2.3"
87
+ )
88
+
89
+ expect(res).to eq(nil)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,427 @@
1
+ require "spec_helper"
2
+ require "json" # For .to_json
3
+
4
+ describe Chef::Compliance::Reporter::Automate do
5
+ let(:reporter) { Chef::Compliance::Reporter::Automate.new(opts) }
6
+
7
+ let(:opts) do
8
+ {
9
+ entity_uuid: "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
10
+ run_id: "3f0536f7-3361-4bca-ae53-b45118dceb5d",
11
+ node_info: {
12
+ node: "chef-client.solo",
13
+ environment: "My Prod Env",
14
+ roles: %w{base_linux apache_linux},
15
+ recipes: ["some_cookbook::some_recipe", "some_cookbook"],
16
+ policy_name: "test_policy_name",
17
+ policy_group: "test_policy_group",
18
+ chef_tags: ["mylinux", "my.tag", "some=tag"],
19
+ organization_name: "test_org",
20
+ source_fqdn: "api.chef.io",
21
+ ipaddress: "192.168.56.33",
22
+ fqdn: "lb1.prod.example.com",
23
+ },
24
+ run_time_limit: 1.1,
25
+ control_results_limit: 2,
26
+ timestamp: Time.parse("2016-07-19T19:19:19+01:00"),
27
+ }
28
+ end
29
+
30
+ let(:inspec_report) do
31
+ {
32
+ "version": "1.2.1",
33
+ "profiles":
34
+ [{ "name": "tmp_compliance_profile",
35
+ "title": "/tmp Compliance Profile",
36
+ "summary": "An Example Compliance Profile",
37
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
38
+ "version": "0.1.1",
39
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
40
+ "license": "Apache 2.0 License",
41
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
42
+ "supports": [],
43
+ "controls":
44
+ [{ "title": "A /tmp directory must exist",
45
+ "desc": "A /tmp directory must exist",
46
+ "impact": 0.3,
47
+ "refs": [],
48
+ "tags": {},
49
+ "code": "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
50
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
51
+ "id": "tmp-1.0",
52
+ "results": [
53
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
54
+ ],
55
+ },
56
+ { "title": "/tmp directory is owned by the root user",
57
+ "desc": "The /tmp directory must be owned by the root user",
58
+ "impact": 0.3,
59
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
60
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
61
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
62
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
63
+ "id": "tmp-1.1",
64
+ "results": [
65
+ { "status": "passed", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
66
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
67
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
68
+ ],
69
+ },
70
+ ],
71
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
72
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }] }],
73
+ "other_checks": [],
74
+ "statistics": { "duration": 0.032332 },
75
+ }
76
+ end
77
+
78
+ describe "#send_report" do
79
+ before :each do
80
+ WebMock.disable_net_connect!
81
+
82
+ Chef::Config[:data_collector] = { token: token, server_url: "https://automate.test/data_collector" }
83
+ end
84
+
85
+ let(:token) { "fake_token" }
86
+
87
+ it "sends report successfully to ChefAutomate with missing profiles" do
88
+ metasearch_stub = stub_request(:post, "https://automate.test/compliance/profiles/metasearch")
89
+ .with(
90
+ body: '{"sha256": ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"]}',
91
+ headers: {
92
+ "Accept-Encoding" => "identity",
93
+ "X-Chef-Version" => Chef::VERSION,
94
+ "X-Data-Collector-Auth" => "version=1.0",
95
+ "X-Data-Collector-Token" => token,
96
+ }
97
+ ).to_return(
98
+ status: 200,
99
+ body: '{"missing_sha256": ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"]}'
100
+ )
101
+
102
+ report_stub = stub_request(:post, "https://automate.test/data_collector")
103
+ .with(
104
+ body: {
105
+ "version": "1.2.1",
106
+ "profiles": [
107
+ {
108
+ "name": "tmp_compliance_profile",
109
+ "title": "/tmp Compliance Profile",
110
+ "summary": "An Example Compliance Profile",
111
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
112
+ "version": "0.1.1",
113
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
114
+ "license": "Apache 2.0 License",
115
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
116
+ "supports": [],
117
+ "controls": [
118
+ {
119
+ "title": "A /tmp directory must exist",
120
+ "desc": "A /tmp directory must exist",
121
+ "impact": 0.3,
122
+ "refs": [],
123
+ "tags": {},
124
+ "code": "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
125
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
126
+ "id": "tmp-1.0",
127
+ "results": [
128
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
129
+ ],
130
+ },
131
+ {
132
+ "title": "/tmp directory is owned by the root user",
133
+ "desc": "The /tmp directory must be owned by the root user",
134
+ "impact": 0.3,
135
+ "refs": [
136
+ { "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" },
137
+ ],
138
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
139
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
140
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
141
+ "id": "tmp-1.1",
142
+ "results": [
143
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
144
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
145
+ ],
146
+ "removed_results_counts": { "failed": 0, "skipped": 0, "passed": 1 },
147
+ },
148
+ ],
149
+ "groups": [
150
+ { "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" },
151
+ ],
152
+ "attributes": [
153
+ { "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } },
154
+ ],
155
+ },
156
+ ],
157
+ "other_checks": [],
158
+ "statistics": { "duration": 0.032332 },
159
+ "type": "inspec_report",
160
+ "node_name": "chef-client.solo",
161
+ "end_time": "2016-07-19T18:19:19Z",
162
+ "node_uuid": "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
163
+ "environment": "My Prod Env",
164
+ "roles": %w{base_linux apache_linux},
165
+ "recipes": ["some_cookbook::some_recipe", "some_cookbook"],
166
+ "report_uuid": "3f0536f7-3361-4bca-ae53-b45118dceb5d",
167
+ "source_fqdn": "api.chef.io",
168
+ "organization_name": "test_org",
169
+ "policy_group": "test_policy_group",
170
+ "policy_name": "test_policy_name",
171
+ "chef_tags": ["mylinux", "my.tag", "some=tag"],
172
+ "ipaddress": "192.168.56.33",
173
+ "fqdn": "lb1.prod.example.com",
174
+ "run_time_limit": 1.1,
175
+ },
176
+ headers: {
177
+ "Accept-Encoding" => "identity",
178
+ "X-Chef-Version" => Chef::VERSION,
179
+ "X-Data-Collector-Auth" => "version=1.0",
180
+ "X-Data-Collector-Token" => token,
181
+ }
182
+ ).to_return(status: 200)
183
+
184
+ expect(reporter.send_report(inspec_report)).to eq(true)
185
+
186
+ expect(metasearch_stub).to have_been_requested
187
+ expect(report_stub).to have_been_requested
188
+ end
189
+
190
+ it "sends report successfully to ChefAutomate with seen profiles" do
191
+ metasearch_stub = stub_request(:post, "https://automate.test/compliance/profiles/metasearch")
192
+ .with(
193
+ body: '{"sha256": ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"]}',
194
+ headers: {
195
+ "Accept-Encoding" => "identity",
196
+ "X-Chef-Version" => Chef::VERSION,
197
+ "X-Data-Collector-Auth" => "version=1.0",
198
+ "X-Data-Collector-Token" => token,
199
+ }
200
+ ).to_return(
201
+ status: 200,
202
+ body: '{"missing_sha256": []}'
203
+ )
204
+
205
+ report_stub = stub_request(:post, "https://automate.test/data_collector")
206
+ .with(
207
+ body: {
208
+ "version": "1.2.1",
209
+ "profiles": [
210
+ {
211
+ "title": "/tmp Compliance Profile",
212
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
213
+ "version": "0.1.1",
214
+ "controls": [
215
+ {
216
+ "id": "tmp-1.0",
217
+ "results": [
218
+ { "status": "passed", "code_desc": "File /tmp should be directory" },
219
+ ],
220
+ },
221
+ {
222
+ "id": "tmp-1.1",
223
+ "results": [
224
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
225
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
226
+ ],
227
+ "removed_results_counts": { "failed": 0, "skipped": 0, "passed": 1 },
228
+ },
229
+ ],
230
+ "attributes": [
231
+ { "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } },
232
+ ],
233
+ },
234
+ ],
235
+ "other_checks": [],
236
+ "statistics": { "duration": 0.032332 },
237
+ "type": "inspec_report",
238
+ "node_name": "chef-client.solo",
239
+ "end_time": "2016-07-19T18:19:19Z",
240
+ "node_uuid": "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
241
+ "environment": "My Prod Env",
242
+ "roles": %w{base_linux apache_linux},
243
+ "recipes": ["some_cookbook::some_recipe", "some_cookbook"],
244
+ "report_uuid": "3f0536f7-3361-4bca-ae53-b45118dceb5d",
245
+ "source_fqdn": "api.chef.io",
246
+ "organization_name": "test_org",
247
+ "policy_group": "test_policy_group",
248
+ "policy_name": "test_policy_name",
249
+ "chef_tags": ["mylinux", "my.tag", "some=tag"],
250
+ "ipaddress": "192.168.56.33",
251
+ "fqdn": "lb1.prod.example.com",
252
+ "run_time_limit": 1.1,
253
+ },
254
+ headers: {
255
+ "Accept-Encoding" => "identity",
256
+ "X-Chef-Version" => Chef::VERSION,
257
+ "X-Data-Collector-Auth" => "version=1.0",
258
+ "X-Data-Collector-Token" => token,
259
+ }
260
+ ).to_return(status: 200)
261
+
262
+ expect(reporter.send_report(inspec_report)).to eq(true)
263
+
264
+ expect(metasearch_stub).to have_been_requested
265
+ expect(report_stub).to have_been_requested
266
+ end
267
+
268
+ it "does not send report when entity_uuid is missing" do
269
+ opts.delete(:entity_uuid)
270
+ reporter = Chef::Compliance::Reporter::Automate.new(opts)
271
+ expect(reporter.send_report(inspec_report)).to eq(false)
272
+ end
273
+ end
274
+
275
+ describe "#truncate_controls_results" do
276
+ let(:report) do
277
+ {
278
+ "version": "1.2.1",
279
+ "profiles":
280
+ [{ "name": "tmp_compliance_profile",
281
+ "title": "/tmp Compliance Profile",
282
+ "summary": "An Example Compliance Profile",
283
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215ff",
284
+ "version": "0.1.1",
285
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
286
+ "license": "Apache 2.0 License",
287
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
288
+ "supports": [],
289
+ "controls":
290
+ [{ "id": "tmp-2.0",
291
+ "title": "A bunch of directories must exist",
292
+ "desc": "A bunch of directories must exist for testing",
293
+ "impact": 0.3,
294
+ "refs": [],
295
+ "tags": {},
296
+ "code": "control 'tmp-2.0' do\n impact 0.3\n title 'A bunch of directories must exist'\n desc 'A bunch of directories must exist for testing'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
297
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
298
+ "results": [
299
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
300
+ { "status": "passed", "code_desc": "File /etc should be directory", "run_time": 0.002314, "start_time": "2016-10-19 11:09:45 -0400" },
301
+ { "status": "passed", "code_desc": "File /opt should be directory", "run_time": 0.002315, "start_time": "2016-10-19 11:09:46 -0400" },
302
+ { "status": "skipped", "code_desc": "No-op", "run_time": 0.002316, "start_time": "2016-10-19 11:09:44 -0400", "skip_message": "4 testing" },
303
+ { "status": "skipped", "code_desc": "No-op", "run_time": 0.002317, "start_time": "2016-10-19 11:09:44 -0400", "skip_message": "4 testing" },
304
+ { "status": "skipped", "code_desc": "No-op", "run_time": 0.002318, "start_time": "2016-10-19 11:09:44 -0400", "skip_message": "4 testing" },
305
+ { "status": "failed", "code_desc": "File /etc/passwd should be directory", "run_time": 0.002313, "start_time": "2016-10-19 11:09:44 -0400" },
306
+ { "status": "failed", "code_desc": "File /etc/passwd should be directory", "run_time": 0.002313, "start_time": "2016-10-19 11:09:44 -0400" },
307
+ { "status": "failed", "code_desc": "File /etc/passwd should be directory", "run_time": 0.002313, "start_time": "2016-10-19 11:09:44 -0400" },
308
+ ],
309
+ },
310
+ { "id": "tmp-2.1",
311
+ "title": "/tmp directory is owned by the root user",
312
+ "desc": "The /tmp directory must be owned by the root user",
313
+ "impact": 0.3,
314
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
315
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
316
+ "code": "control 'tmp-2.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
317
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
318
+ "results": [
319
+ { "status": "passed", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
320
+ { "status": "passed", "code_desc": 'File /etc should be owned by "root"', "run_time": 1.238845, "start_time": "2016-10-19 11:09:43 -0400" },
321
+ ],
322
+ },
323
+ ],
324
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
325
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }] }],
326
+ "other_checks": [],
327
+ "statistics": { "duration": 0.032332 },
328
+ }
329
+ end
330
+
331
+ it "truncates controls results 1" do
332
+ truncated_report = reporter.truncate_controls_results(report, 5)
333
+ expect(truncated_report[:profiles][0][:controls][0][:results].length).to eq(5)
334
+ statuses = truncated_report[:profiles][0][:controls][0][:results].map { |r| r[:status] }
335
+ expect(statuses).to eq(%w{failed failed failed skipped skipped})
336
+ expect(truncated_report[:profiles][0][:controls][0][:removed_results_counts]).to eq(failed: 0, skipped: 1, passed: 3)
337
+ end
338
+
339
+ it "truncates controls results 2" do
340
+ truncated_report = reporter.truncate_controls_results(report, 5)
341
+ expect(truncated_report[:profiles][0][:controls][1][:results].length).to eq(2)
342
+ statuses = truncated_report[:profiles][0][:controls][1][:results].map { |r| r[:status] }
343
+ expect(statuses).to eq(%w{passed passed})
344
+ expect(truncated_report[:profiles][0][:controls][1][:removed_results_counts]).to eq(nil)
345
+ end
346
+
347
+ it "truncates controls results 3" do
348
+ truncated_report = reporter.truncate_controls_results(report, 0)
349
+ expect(truncated_report[:profiles][0][:controls][0][:results].length).to eq(9)
350
+ end
351
+
352
+ it "truncates controls results 4" do
353
+ truncated_report = reporter.truncate_controls_results(report, 1)
354
+ expect(truncated_report[:profiles][0][:controls][0][:results].length).to eq(1)
355
+ end
356
+ end
357
+
358
+ describe "#strip_profiles_meta" do
359
+ it "removes the metadata from seen profiles" do
360
+ expected = {
361
+ other_checks: [],
362
+ profiles: [
363
+ {
364
+ attributes: [
365
+ {
366
+ name: "syslog_pkg",
367
+ options: {
368
+ default: "rsyslog",
369
+ description: "syslog package...",
370
+ },
371
+ },
372
+ ],
373
+ controls: [
374
+ {
375
+ id: "tmp-1.0",
376
+ results: [
377
+ {
378
+ code_desc: "File /tmp should be directory",
379
+ status: "passed",
380
+ },
381
+ ],
382
+ },
383
+ {
384
+ id: "tmp-1.1",
385
+ results: [
386
+ {
387
+ code_desc: 'File /tmp should be owned by "root"',
388
+ run_time: 1.228845,
389
+ start_time: "2016-10-19 11:09:43 -0400",
390
+ status: "passed",
391
+ },
392
+ {
393
+ code_desc: 'File /tmp should be owned by "root"',
394
+ run_time: 1.228845,
395
+ start_time: "2016-10-19 11:09:43 -0400",
396
+ status: "skipped",
397
+ },
398
+ {
399
+ code_desc: "File /etc/hosts is expected to be directory",
400
+ message: "expected `File /etc/hosts.directory?` to return true, got false",
401
+ run_time: 1.228845,
402
+ start_time: "2016-10-19 11:09:43 -0400",
403
+ status: "failed",
404
+ },
405
+ ],
406
+ },
407
+ ],
408
+ sha256: "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
409
+ title: "/tmp Compliance Profile",
410
+ version: "0.1.1",
411
+ },
412
+ ],
413
+ run_time_limit: 1.1,
414
+ statistics: {
415
+ duration: 0.032332,
416
+ },
417
+ version: "1.2.1",
418
+ }
419
+ expect(reporter.strip_profiles_meta(inspec_report, [], 1.1)).to eq(expected)
420
+ end
421
+
422
+ it "does not remove the metadata from missing profiles" do
423
+ expected = inspec_report.merge(run_time_limit: 1.1)
424
+ expect(reporter.strip_profiles_meta(inspec_report, ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"], 1.1)).to eq(expected)
425
+ end
426
+ end
427
+ end