chef 16.7.61 → 16.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -2
  3. data/README.md +1 -1
  4. data/chef.gemspec +2 -1
  5. data/lib/chef/application/base.rb +1 -1
  6. data/lib/chef/client.rb +3 -0
  7. data/lib/chef/compliance/default_attributes.rb +89 -0
  8. data/lib/chef/compliance/fetcher/automate.rb +69 -0
  9. data/lib/chef/compliance/fetcher/chef_server.rb +134 -0
  10. data/lib/chef/compliance/reporter/automate.rb +202 -0
  11. data/lib/chef/compliance/reporter/chef_server_automate.rb +92 -0
  12. data/lib/chef/compliance/reporter/compliance_enforcer.rb +20 -0
  13. data/lib/chef/compliance/reporter/json_file.rb +19 -0
  14. data/lib/chef/compliance/runner.rb +250 -0
  15. data/lib/chef/cookbook_manifest.rb +1 -0
  16. data/lib/chef/encrypted_data_bag_item/assertions.rb +1 -1
  17. data/lib/chef/exceptions.rb +4 -0
  18. data/lib/chef/http/ssl_policies.rb +6 -0
  19. data/lib/chef/knife/bootstrap/train_connector.rb +1 -1
  20. data/lib/chef/knife/core/ui.rb +4 -1
  21. data/lib/chef/knife/ssh.rb +1 -1
  22. data/lib/chef/mixin/powershell_exec.rb +3 -1
  23. data/lib/chef/platform/query_helpers.rb +4 -4
  24. data/lib/chef/powershell.rb +2 -0
  25. data/lib/chef/provider/dsc_resource.rb +12 -24
  26. data/lib/chef/provider/dsc_script.rb +16 -20
  27. data/lib/chef/provider/git.rb +5 -5
  28. data/lib/chef/resource/chef_client_config.rb +1 -1
  29. data/lib/chef/resource/dsc_script.rb +8 -1
  30. data/lib/chef/resource/hostname.rb +3 -3
  31. data/lib/chef/resource/template.rb +2 -2
  32. data/lib/chef/resource/windows_certificate.rb +7 -1
  33. data/lib/chef/resource_collection/resource_set.rb +1 -1
  34. data/lib/chef/util/dsc/configuration_generator.rb +52 -11
  35. data/lib/chef/util/dsc/lcm_output_parser.rb +3 -4
  36. data/lib/chef/util/dsc/local_configuration_manager.rb +17 -14
  37. data/lib/chef/util/dsc/resource_store.rb +5 -11
  38. data/lib/chef/version.rb +1 -1
  39. data/lib/chef/win32/api/file.rb +4 -0
  40. data/spec/functional/resource/dsc_script_spec.rb +3 -6
  41. data/spec/integration/client/client_spec.rb +2 -1
  42. data/spec/integration/compliance/compliance_spec.rb +81 -0
  43. data/spec/integration/recipes/recipe_dsl_spec.rb +1 -0
  44. data/spec/spec_helper.rb +1 -1
  45. data/spec/unit/client_spec.rb +1 -0
  46. data/spec/unit/compliance/fetcher/automate_spec.rb +134 -0
  47. data/spec/unit/compliance/fetcher/chef_server_spec.rb +93 -0
  48. data/spec/unit/compliance/reporter/automate_spec.rb +427 -0
  49. data/spec/unit/compliance/reporter/chef_server_automate_spec.rb +177 -0
  50. data/spec/unit/compliance/reporter/compliance_enforcer_spec.rb +48 -0
  51. data/spec/unit/compliance/runner_spec.rb +113 -0
  52. data/spec/unit/http/ssl_policies_spec.rb +11 -0
  53. data/spec/unit/knife/core/node_editor_spec.rb +1 -1
  54. data/spec/unit/mixin/powershell_exec_spec.rb +1 -1
  55. data/spec/unit/platform/query_helpers_spec.rb +11 -12
  56. data/spec/unit/provider/dsc_resource_spec.rb +10 -27
  57. data/spec/unit/provider/dsc_script_spec.rb +1 -1
  58. data/spec/unit/provider/mount/windows_spec.rb +1 -0
  59. data/spec/unit/provider/systemd_unit_spec.rb +1 -1
  60. data/spec/unit/resource/windows_certificate_spec.rb +12 -0
  61. data/spec/unit/util/dsc/configuration_generator_spec.rb +79 -0
  62. data/spec/unit/util/dsc/local_configuration_manager_spec.rb +27 -35
  63. metadata +37 -12
  64. data/lib/chef/util/powershell/cmdlet.rb +0 -169
  65. data/lib/chef/util/powershell/cmdlet_result.rb +0 -61
  66. data/spec/functional/util/powershell/cmdlet_spec.rb +0 -111
  67. data/spec/unit/util/powershell/cmdlet_spec.rb +0 -106
@@ -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
@@ -0,0 +1,177 @@
1
+ require "spec_helper"
2
+
3
+ describe Chef::Compliance::Reporter::ChefServerAutomate do
4
+ before do
5
+ WebMock.disable_net_connect!
6
+
7
+ Chef::Config[:client_key] = File.expand_path("../../../data/ssl/private_key.pem", __dir__)
8
+ Chef::Config[:node_name] = "spec-node"
9
+ end
10
+
11
+ let(:reporter) { Chef::Compliance::Reporter::ChefServerAutomate.new(opts) }
12
+
13
+ let(:opts) do
14
+ {
15
+ entity_uuid: "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
16
+ run_id: "3f0536f7-3361-4bca-ae53-b45118dceb5d",
17
+ node_info: {
18
+ node: "chef-client.solo",
19
+ environment: "My Prod Env",
20
+ roles: %w{base_linux apache_linux},
21
+ recipes: ["some_cookbook::some_recipe", "some_cookbook"],
22
+ policy_name: "test_policy_name",
23
+ policy_group: "test_policy_group",
24
+ chef_tags: ["mylinux", "my.tag", "some=tag"],
25
+ organization_name: "test_org",
26
+ source_fqdn: "api.chef.io",
27
+ ipaddress: "192.168.56.33",
28
+ fqdn: "lb1.prod.example.com",
29
+ },
30
+ url: "https://chef.server/data_collector",
31
+ control_results_limit: 2,
32
+ timestamp: Time.parse("2016-07-19T19:19:19+01:00"),
33
+ }
34
+ end
35
+
36
+ let(:inspec_report) do
37
+ {
38
+ "version": "1.2.1",
39
+ "profiles":
40
+ [{ "name": "tmp_compliance_profile",
41
+ "title": "/tmp Compliance Profile",
42
+ "summary": "An Example Compliance Profile",
43
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
44
+ "version": "0.1.1",
45
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
46
+ "license": "Apache 2.0 License",
47
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
48
+ "supports": [],
49
+ "controls":
50
+ [{ "title": "A /tmp directory must exist",
51
+ "desc": "A /tmp directory must exist",
52
+ "impact": 0.3,
53
+ "refs": [],
54
+ "tags": {},
55
+ "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",
56
+ "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 },
57
+ "id": "tmp-1.0",
58
+ "results": [
59
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
60
+ ],
61
+ },
62
+ { "title": "/tmp directory is owned by the root user",
63
+ "desc": "The /tmp directory must be owned by the root user",
64
+ "impact": 0.3,
65
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
66
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
67
+ "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",
68
+ "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 },
69
+ "id": "tmp-1.1",
70
+ "results": [
71
+ { "status": "passed", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
72
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
73
+ { "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" },
74
+ ],
75
+ },
76
+ ],
77
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
78
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }] }],
79
+ "other_checks": [],
80
+ "statistics": { "duration": 0.032332 },
81
+ }
82
+ end
83
+
84
+ let(:enriched_report) do
85
+ {
86
+ "version": "1.2.1",
87
+ "profiles": [
88
+ {
89
+ "name": "tmp_compliance_profile",
90
+ "title": "/tmp Compliance Profile",
91
+ "summary": "An Example Compliance Profile",
92
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
93
+ "version": "0.1.1",
94
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
95
+ "license": "Apache 2.0 License",
96
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
97
+ "supports": [],
98
+ "controls": [
99
+ {
100
+ "title": "A /tmp directory must exist",
101
+ "desc": "A /tmp directory must exist",
102
+ "impact": 0.3,
103
+ "refs": [],
104
+ "tags": {},
105
+ "code":
106
+ "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",
107
+ "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 },
108
+ "id": "tmp-1.0",
109
+ "results": [{ "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" }],
110
+ },
111
+ {
112
+ "title": "/tmp directory is owned by the root user",
113
+ "desc": "The /tmp directory must be owned by the root user",
114
+ "impact": 0.3,
115
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
116
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
117
+ "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",
118
+ "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 },
119
+ "id": "tmp-1.1",
120
+ "results": [
121
+ { "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" },
122
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
123
+ ],
124
+ "removed_results_counts": { "failed": 0, "skipped": 0, "passed": 1 },
125
+ },
126
+ ],
127
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
128
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }],
129
+ },
130
+ ],
131
+ "other_checks": [],
132
+ "statistics": { "duration": 0.032332 },
133
+ "type": "inspec_report",
134
+ "node_name": "chef-client.solo",
135
+ "end_time": "2016-07-19T18:19:19Z",
136
+ "node_uuid": "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
137
+ "environment": "My Prod Env",
138
+ "roles": %w{base_linux apache_linux},
139
+ "recipes": ["some_cookbook::some_recipe", "some_cookbook"],
140
+ "report_uuid": "3f0536f7-3361-4bca-ae53-b45118dceb5d",
141
+ "source_fqdn": "api.chef.io",
142
+ "organization_name": "test_org",
143
+ "policy_group": "test_policy_group",
144
+ "policy_name": "test_policy_name",
145
+ "chef_tags": ["mylinux", "my.tag", "some=tag"],
146
+ "ipaddress": "192.168.56.33",
147
+ "fqdn": "lb1.prod.example.com",
148
+ }
149
+ end
150
+
151
+ it "sends report successfully" do
152
+ # TODO: Had to change 'X-Ops-Server-Api-Version' from 1 to 2, is that correct?
153
+ report_stub = stub_request(:post, "https://chef.server/data_collector")
154
+ .with(
155
+ body: enriched_report,
156
+ headers: {
157
+ "X-Chef-Version" => Chef::VERSION,
158
+ "X-Ops-Authorization-1" => /.+/,
159
+ "X-Ops-Authorization-2" => /.+/,
160
+ "X-Ops-Authorization-3" => /.+/,
161
+ "X-Ops-Authorization-4" => /.+/,
162
+ "X-Ops-Authorization-5" => /.+/,
163
+ "X-Ops-Authorization-6" => /.+/,
164
+ "X-Ops-Content-Hash" => "yfck5nQDcRWta06u45Q+J463LYY=",
165
+ "X-Ops-Server-Api-Version" => "2",
166
+ "X-Ops-Sign" => "algorithm=sha1;version=1.1;",
167
+ "X-Ops-Timestamp" => /.+/,
168
+ "X-Ops-Userid" => "spec-node",
169
+ "X-Remote-Request-Id" => /.+/,
170
+ }
171
+ ).to_return(status: 200)
172
+
173
+ expect(reporter.send_report(inspec_report)).to eq(true)
174
+
175
+ expect(report_stub).to have_been_requested
176
+ end
177
+ end