inspec 1.0.0 → 1.35.1

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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +933 -101
  3. data/Gemfile +8 -17
  4. data/MAINTAINERS.md +2 -1
  5. data/MAINTAINERS.toml +9 -4
  6. data/README.md +49 -28
  7. data/Rakefile +54 -39
  8. data/bin/inspec +1 -1
  9. data/docs/dsl_inspec.md +17 -4
  10. data/docs/dsl_resource.md +1 -1
  11. data/docs/habitat.md +192 -0
  12. data/docs/inspec_and_friends.md +4 -2
  13. data/docs/matchers.md +11 -0
  14. data/docs/migration.md +293 -0
  15. data/docs/profiles.md +124 -76
  16. data/docs/resources/aide_conf.md.erb +81 -0
  17. data/docs/resources/apache_conf.md.erb +10 -10
  18. data/docs/resources/apt.md.erb +13 -13
  19. data/docs/resources/audit_policy.md.erb +10 -10
  20. data/docs/resources/auditd_conf.md.erb +10 -10
  21. data/docs/resources/auditd_rules.md.erb +11 -11
  22. data/docs/resources/bash.md.erb +12 -12
  23. data/docs/resources/bond.md.erb +15 -15
  24. data/docs/resources/bridge.md.erb +11 -11
  25. data/docs/resources/bsd_service.md.erb +11 -11
  26. data/docs/resources/command.md.erb +23 -23
  27. data/docs/resources/crontab.md.erb +84 -0
  28. data/docs/resources/csv.md.erb +10 -10
  29. data/docs/resources/dh_params.md +191 -0
  30. data/docs/resources/directory.md.erb +8 -8
  31. data/docs/resources/docker.md.erb +160 -0
  32. data/docs/resources/docker_container.md.erb +89 -0
  33. data/docs/resources/docker_image.md.erb +86 -0
  34. data/docs/resources/etc_group.md.erb +16 -16
  35. data/docs/resources/etc_hosts.md.erb +62 -0
  36. data/docs/resources/file.md.erb +80 -58
  37. data/docs/resources/gem.md.erb +32 -13
  38. data/docs/resources/group.md.erb +12 -12
  39. data/docs/resources/grub_conf.md.erb +11 -11
  40. data/docs/resources/host.md.erb +27 -18
  41. data/docs/resources/http.md.erb +104 -0
  42. data/docs/resources/iis_app.md.erb +126 -0
  43. data/docs/resources/iis_site.md.erb +17 -17
  44. data/docs/resources/inetd_conf.md.erb +10 -10
  45. data/docs/resources/ini.md.erb +9 -9
  46. data/docs/resources/interface.md.erb +13 -13
  47. data/docs/resources/iptables.md.erb +21 -14
  48. data/docs/resources/json.md.erb +11 -11
  49. data/docs/resources/kernel_module.md.erb +73 -12
  50. data/docs/resources/kernel_parameter.md.erb +12 -12
  51. data/docs/resources/key_rsa.md.erb +70 -0
  52. data/docs/resources/launchd_service.md.erb +11 -11
  53. data/docs/resources/limits_conf.md.erb +10 -10
  54. data/docs/resources/login_def.md.erb +12 -12
  55. data/docs/resources/mount.md.erb +14 -14
  56. data/docs/resources/mssql_session.md.erb +62 -0
  57. data/docs/resources/mysql_conf.md.erb +14 -14
  58. data/docs/resources/mysql_session.md.erb +22 -10
  59. data/docs/resources/npm.md.erb +13 -13
  60. data/docs/resources/ntp_conf.md.erb +9 -9
  61. data/docs/resources/oneget.md.erb +11 -11
  62. data/docs/resources/oracledb_session.md.erb +53 -0
  63. data/docs/resources/os.md.erb +17 -17
  64. data/docs/resources/os_env.md.erb +12 -12
  65. data/docs/resources/package.md.erb +29 -16
  66. data/docs/resources/parse_config.md.erb +18 -18
  67. data/docs/resources/parse_config_file.md.erb +29 -23
  68. data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +17 -17
  69. data/docs/resources/pip.md.erb +15 -15
  70. data/docs/resources/port.md.erb +23 -23
  71. data/docs/resources/postgres_conf.md.erb +15 -13
  72. data/docs/resources/postgres_hba_conf.md.erb +104 -0
  73. data/docs/resources/postgres_ident_conf.md.erb +87 -0
  74. data/docs/resources/postgres_session.md.erb +28 -21
  75. data/docs/resources/powershell.md.erb +14 -14
  76. data/docs/resources/processes.md.erb +123 -0
  77. data/docs/resources/rabbitmq_config.md.erb +55 -0
  78. data/docs/resources/registry_key.md.erb +44 -21
  79. data/docs/resources/runit_service.md.erb +11 -11
  80. data/docs/resources/security_policy.md.erb +10 -10
  81. data/docs/resources/service.md.erb +18 -18
  82. data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +19 -19
  83. data/docs/resources/ssh_config.md.erb +13 -13
  84. data/docs/resources/sshd_config.md.erb +15 -15
  85. data/docs/resources/ssl.md.erb +13 -13
  86. data/docs/resources/sys_info.md.erb +10 -10
  87. data/docs/resources/systemd_service.md.erb +11 -11
  88. data/docs/resources/sysv_service.md.erb +11 -11
  89. data/docs/resources/upstart_service.md.erb +11 -11
  90. data/docs/resources/user.md.erb +20 -20
  91. data/docs/resources/users.md.erb +19 -19
  92. data/docs/resources/vbscript.md.erb +13 -13
  93. data/docs/resources/virtualization.md.erb +71 -0
  94. data/docs/resources/windows_feature.md.erb +10 -10
  95. data/docs/resources/windows_task.md.erb +103 -0
  96. data/docs/resources/wmi.md.erb +10 -10
  97. data/docs/resources/x509_certificate.md.erb +146 -0
  98. data/docs/resources/xinetd_conf.md.erb +17 -17
  99. data/docs/resources/xml.md.erb +75 -0
  100. data/docs/resources/yaml.md.erb +25 -11
  101. data/docs/resources/yum.md.erb +24 -16
  102. data/docs/resources/zfs_dataset.md.erb +63 -0
  103. data/docs/resources/zfs_pool.md.erb +57 -0
  104. data/docs/ruby_usage.md +50 -0
  105. data/docs/shared/matcher_cmp.md.erb +4 -6
  106. data/examples/inheritance/README.md +49 -3
  107. data/examples/inheritance/controls/example.rb +4 -1
  108. data/examples/inheritance/inspec.yml +3 -1
  109. data/examples/meta-profile/README.md +29 -3
  110. data/examples/meta-profile/controls/example.rb +10 -5
  111. data/examples/meta-profile/inspec.yml +4 -8
  112. data/examples/profile/README.md +1 -1
  113. data/examples/profile/controls/example.rb +0 -1
  114. data/examples/profile/controls/gordon.rb +0 -1
  115. data/examples/profile/inspec.yml +1 -1
  116. data/examples/profile-attribute/inspec.yml +1 -1
  117. data/examples/profile-sensitive/README.md +29 -0
  118. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
  119. data/examples/profile-sensitive/controls/sensitive.rb +9 -0
  120. data/examples/profile-sensitive/inspec.yml +8 -0
  121. data/inspec.gemspec +10 -2
  122. data/lib/bundles/inspec-artifact/README.md +1 -0
  123. data/lib/bundles/inspec-artifact/cli.rb +284 -0
  124. data/lib/bundles/inspec-artifact.rb +7 -0
  125. data/lib/bundles/inspec-compliance/README.md +44 -15
  126. data/lib/bundles/inspec-compliance/api.rb +135 -29
  127. data/lib/bundles/inspec-compliance/cli.rb +128 -21
  128. data/lib/bundles/inspec-compliance/configuration.rb +8 -0
  129. data/lib/bundles/inspec-compliance/http.rb +26 -19
  130. data/lib/bundles/inspec-compliance/target.rb +50 -25
  131. data/lib/bundles/inspec-habitat/cli.rb +31 -0
  132. data/lib/bundles/inspec-habitat/log.rb +10 -0
  133. data/lib/bundles/inspec-habitat/profile.rb +372 -0
  134. data/lib/bundles/inspec-habitat.rb +12 -0
  135. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +0 -1
  136. data/lib/bundles/inspec-init/templates/profile/inspec.yml +1 -1
  137. data/lib/bundles/inspec-supermarket/api.rb +7 -5
  138. data/lib/bundles/inspec-supermarket/cli.rb +2 -2
  139. data/lib/fetchers/git.rb +4 -2
  140. data/lib/fetchers/local.rb +4 -1
  141. data/lib/fetchers/url.rb +64 -10
  142. data/lib/inspec/archive/tar.rb +1 -1
  143. data/lib/inspec/backend.rb +26 -3
  144. data/lib/inspec/base_cli.rb +44 -7
  145. data/lib/inspec/cached_fetcher.rb +2 -2
  146. data/lib/inspec/cli.rb +34 -12
  147. data/lib/inspec/completions/fish.sh.erb +34 -0
  148. data/lib/inspec/control_eval_context.rb +0 -2
  149. data/lib/inspec/dependencies/cache.rb +0 -1
  150. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  151. data/lib/inspec/dependencies/lockfile.rb +13 -6
  152. data/lib/inspec/dependencies/requirement.rb +6 -6
  153. data/lib/inspec/dependencies/resolver.rb +4 -4
  154. data/lib/inspec/dsl.rb +3 -5
  155. data/lib/inspec/env_printer.rb +9 -1
  156. data/lib/inspec/exceptions.rb +8 -0
  157. data/lib/inspec/fetcher.rb +1 -1
  158. data/lib/inspec/file_provider.rb +47 -11
  159. data/lib/inspec/library_eval_context.rb +1 -1
  160. data/lib/inspec/metadata.rb +62 -18
  161. data/lib/inspec/method_source.rb +24 -0
  162. data/lib/inspec/objects/attribute.rb +21 -0
  163. data/lib/inspec/objects/control.rb +21 -3
  164. data/lib/inspec/objects/each_loop.rb +2 -1
  165. data/lib/inspec/objects/list.rb +1 -1
  166. data/lib/inspec/objects/or_test.rb +17 -2
  167. data/lib/inspec/objects/tag.rb +27 -0
  168. data/lib/inspec/objects/test.rb +9 -8
  169. data/lib/inspec/objects.rb +1 -0
  170. data/lib/inspec/plugins/fetcher.rb +4 -5
  171. data/lib/inspec/plugins/resource.rb +1 -2
  172. data/lib/inspec/plugins/source_reader.rb +3 -3
  173. data/lib/inspec/plugins.rb +1 -1
  174. data/lib/inspec/polyfill.rb +0 -1
  175. data/lib/inspec/profile.rb +92 -23
  176. data/lib/inspec/profile_context.rb +1 -1
  177. data/lib/inspec/profile_vendor.rb +66 -0
  178. data/lib/inspec/resource.rb +29 -3
  179. data/lib/inspec/rspec_json_formatter.rb +672 -285
  180. data/lib/inspec/rule.rb +12 -16
  181. data/lib/inspec/runner.rb +26 -15
  182. data/lib/inspec/runner_rspec.rb +1 -0
  183. data/lib/inspec/runtime_profile.rb +26 -0
  184. data/lib/inspec/schema.rb +185 -0
  185. data/lib/inspec/secrets/yaml.rb +8 -1
  186. data/lib/inspec/secrets.rb +1 -1
  187. data/lib/inspec/shell.rb +92 -22
  188. data/lib/inspec/source_reader.rb +1 -1
  189. data/lib/inspec/version.rb +1 -1
  190. data/lib/inspec.rb +1 -1
  191. data/lib/matchers/matchers.rb +67 -17
  192. data/lib/resources/aide_conf.rb +162 -0
  193. data/lib/resources/apache.rb +0 -1
  194. data/lib/resources/apache_conf.rb +3 -3
  195. data/lib/resources/apt.rb +2 -1
  196. data/lib/resources/audit_policy.rb +0 -1
  197. data/lib/resources/auditd_conf.rb +1 -2
  198. data/lib/resources/auditd_rules.rb +5 -5
  199. data/lib/resources/bond.rb +1 -1
  200. data/lib/resources/bridge.rb +1 -1
  201. data/lib/resources/command.rb +5 -3
  202. data/lib/resources/crontab.rb +102 -0
  203. data/lib/resources/csv.rb +18 -1
  204. data/lib/resources/dh_params.rb +84 -0
  205. data/lib/resources/directory.rb +7 -3
  206. data/lib/resources/docker.rb +198 -0
  207. data/lib/resources/docker_container.rb +114 -0
  208. data/lib/resources/docker_image.rb +96 -0
  209. data/lib/resources/etc_group.rb +2 -3
  210. data/lib/resources/etc_hosts.rb +81 -0
  211. data/lib/resources/file.rb +88 -30
  212. data/lib/resources/gem.rb +26 -6
  213. data/lib/resources/groups.rb +4 -5
  214. data/lib/resources/grub_conf.rb +3 -4
  215. data/lib/resources/host.rb +175 -35
  216. data/lib/resources/http.rb +73 -0
  217. data/lib/resources/iis_app.rb +103 -0
  218. data/lib/resources/iis_site.rb +23 -0
  219. data/lib/resources/inetd_conf.rb +9 -4
  220. data/lib/resources/interface.rb +4 -3
  221. data/lib/resources/json.rb +37 -14
  222. data/lib/resources/kernel_module.rb +77 -10
  223. data/lib/resources/kernel_parameter.rb +0 -1
  224. data/lib/resources/key_rsa.rb +67 -0
  225. data/lib/resources/limits_conf.rb +3 -4
  226. data/lib/resources/login_def.rb +2 -3
  227. data/lib/resources/mount.rb +34 -4
  228. data/lib/resources/mssql_session.rb +81 -12
  229. data/lib/resources/mysql.rb +0 -1
  230. data/lib/resources/mysql_conf.rb +1 -2
  231. data/lib/resources/mysql_session.rb +18 -7
  232. data/lib/resources/nginx_conf.rb +95 -0
  233. data/lib/resources/ntp_conf.rb +2 -3
  234. data/lib/resources/oracledb_session.rb +138 -0
  235. data/lib/resources/os_env.rb +7 -5
  236. data/lib/resources/package.rb +70 -13
  237. data/lib/resources/packages.rb +111 -0
  238. data/lib/resources/parse_config.rb +3 -4
  239. data/lib/resources/passwd.rb +4 -5
  240. data/lib/resources/pip.rb +29 -21
  241. data/lib/resources/port.rb +133 -14
  242. data/lib/resources/postgres.rb +53 -43
  243. data/lib/resources/postgres_conf.rb +40 -13
  244. data/lib/resources/postgres_hba_conf.rb +101 -0
  245. data/lib/resources/postgres_ident_conf.rb +79 -0
  246. data/lib/resources/postgres_session.rb +28 -20
  247. data/lib/resources/powershell.rb +2 -2
  248. data/lib/resources/processes.rb +61 -26
  249. data/lib/resources/rabbitmq_conf.rb +53 -0
  250. data/lib/resources/registry_key.rb +34 -11
  251. data/lib/resources/security_policy.rb +2 -2
  252. data/lib/resources/service.rb +17 -7
  253. data/lib/resources/ssh_conf.rb +17 -6
  254. data/lib/resources/ssl.rb +3 -4
  255. data/lib/resources/sys_info.rb +1 -1
  256. data/lib/resources/toml.rb +26 -0
  257. data/lib/resources/users.rb +41 -41
  258. data/lib/resources/virtualization.rb +252 -0
  259. data/lib/resources/windows_task.rb +106 -0
  260. data/lib/resources/x509_certificate.rb +143 -0
  261. data/lib/resources/xinetd.rb +18 -2
  262. data/lib/resources/xml.rb +27 -0
  263. data/lib/resources/yaml.rb +10 -2
  264. data/lib/resources/yum.rb +25 -7
  265. data/lib/resources/zfs_dataset.rb +60 -0
  266. data/lib/resources/zfs_pool.rb +49 -0
  267. data/lib/source_readers/flat.rb +2 -1
  268. data/lib/source_readers/inspec.rb +30 -11
  269. data/lib/utils/command_wrapper.rb +3 -3
  270. data/lib/utils/database_helpers.rb +77 -0
  271. data/lib/utils/erlang_parser.rb +192 -0
  272. data/lib/utils/filter.rb +2 -1
  273. data/lib/utils/filter_array.rb +0 -1
  274. data/lib/utils/find_files.rb +1 -2
  275. data/lib/utils/latest_version.rb +21 -0
  276. data/lib/utils/nginx_parser.rb +76 -0
  277. data/lib/utils/object_traversal.rb +5 -4
  278. data/lib/utils/parser.rb +23 -4
  279. data/lib/utils/plugin_registry.rb +3 -3
  280. data/lib/utils/simpleconfig.rb +25 -8
  281. data/lib/utils/spdx.rb +13 -0
  282. data/lib/utils/spdx.txt +344 -0
  283. metadata +168 -22
  284. data/docs/cli.md +0 -342
  285. data/docs/resources/process.md.erb +0 -73
  286. /data/docs/{plugin_kitchen_inspec.html.md → plugin_kitchen_inspec.md} +0 -0
@@ -9,8 +9,9 @@ This extensions offers the following features:
9
9
  To use the CLI, this InSpec add-on adds the following commands:
10
10
 
11
11
  * `$ inspec compliance login` - authentication of the API token against Chef Compliance
12
+ * `$ inspec compliance login_automate` - authentication of the API token against Chef Automate
12
13
  * `$ inspec compliance profiles` - list all available Chef Compliance profiles
13
- * `$ inspec compliance exec profile` - runs a Chef Compliance profile
14
+ * `$ inspec exec compliance://profile` - runs a Chef Compliance profile
14
15
  * `$ inspec compliance upload path/to/local/profile` - uploads a local profile to Chef Compliance
15
16
  * `$ inspec compliance logout` - logout of Chef Compliance
16
17
 
@@ -19,8 +20,37 @@ Compliance profiles can be executed in two mays:
19
20
  - via compliance exec: `inspec compliance exec profile`
20
21
  - via compliance scheme: `inspec exec compliance://profile`
21
22
 
23
+
22
24
  ## Usage
23
25
 
26
+ ### Command options
27
+
28
+ ```
29
+ $ inspec compliance
30
+ Commands:
31
+ inspec compliance download PROFILE # downloads a profile from Chef Compliance
32
+ inspec compliance exec PROFILE # executes a Chef Compliance profile
33
+ inspec compliance help [COMMAND] # Describe subcommands or one specific subcommand
34
+ inspec compliance login SERVER # Log in to a Chef Compliance SERVER
35
+ inspec compliance login_automate SERVER # Log in to an Automate SERVER
36
+ inspec compliance logout # user logout from Chef Compliance
37
+ inspec compliance profiles # list all available profiles in Chef Compliance
38
+ inspec compliance upload PATH # uploads a local profile to Chef Compliance
39
+ inspec compliance version # displays the version of the Chef Compliance server
40
+ ```
41
+
42
+ ### Login with Chef Automate
43
+
44
+ You need a Chef Automate server up and running. Compliance features need to [be activated](https://docs.chef.io/install_chef_automate.html#compliance), too.
45
+
46
+ Now, you need a user token. You can retrieve that via [UI](https://docs.chef.io/api_delivery.html) or [CLI](https://docs.chef.io/ctl_delivery.html#delivery-token).
47
+
48
+ ```
49
+ inspec compliance login_automate https://automate.compliance.test --insecure --user 'admin' --ent 'brewinc' --usertoken 'zuop..._KzE'
50
+ ```
51
+
52
+ ### Login with Chef Compliance
53
+
24
54
  Before you start using the compliance plugin, you need a running [Chef Compliance](https://www.chef.io/compliance/) server. Please login and gather the access token:
25
55
 
26
56
  ![Chef Compliance Token](images/cc-token.png)
@@ -28,24 +58,17 @@ Before you start using the compliance plugin, you need a running [Chef Complianc
28
58
  You can choose the access token (`--token`) or the refresh token (`--refresh_token`)
29
59
 
30
60
  ```
31
- $ inspec compliance
32
- Commands:
33
- inspec compliance exec PROFILE # executes a Chef Compliance profile
34
- inspec compliance help [COMMAND] # Describe subcommands or one specific subcommand
35
- inspec compliance login SERVER # Log in to a Chef Compliance SERVER
36
- inspec compliance logout # user logout from Chef Compliance
37
- inspec compliance profiles # list all available profiles in Chef Compliance
38
- inspec compliance upload PATH # uploads a local profile to Chef Compliance
39
- inspec compliance version # displays the version of the Chef Compliance server
40
-
41
61
  # login to chef compliance server
42
62
  $ inspec compliance login https://compliance.test --user admin --insecure --token '...'
43
63
 
44
64
  # display the chef compliance server version
45
65
  $ inspec compliance version
46
66
  Chef Compliance version: 1.0.11
67
+ ```
68
+
69
+ ### List available profiles via Chef Compliance / Automate
47
70
 
48
- # list available profiles via Chef Compliance
71
+ ```
49
72
  $ inspec compliance profiles
50
73
  Available profiles:
51
74
  -------------------
@@ -65,8 +88,11 @@ Available profiles:
65
88
  * cis/cis-ubuntu12.04lts-level2
66
89
  * cis/cis-ubuntu14.04lts-level1
67
90
  * cis/cis-ubuntu14.04lts-level2
91
+ ```
92
+
93
+ ### Upload a profile to Chef Compliance / Automate
68
94
 
69
- # upload a profile to chef Compliance
95
+ ```
70
96
  $ inspec compliance version
71
97
  Chef Compliance version: 1.0.11
72
98
  ➜ inspec git:(chris-rock/cc-error-not-loggedin) ✗ b inspec compliance upload examples/profile
@@ -103,8 +129,11 @@ Available profiles:
103
129
  * cis/cis-ubuntu12.04lts-level2
104
130
  * cis/cis-ubuntu14.04lts-level1
105
131
  * cis/cis-ubuntu14.04lts-level2
132
+ ```
133
+
134
+ ### Run a profile from Chef Compliance / Automate on Workstation
106
135
 
107
- # run a profile from Chef Compliance locally
136
+ ```
108
137
  $ inspec exec compliance://admin/profile
109
138
  .*...
110
139
 
@@ -119,7 +148,7 @@ Finished in 0.02862 seconds (files took 0.62628 seconds to load)
119
148
  5 examples, 0 failures, 1 pending
120
149
  ```
121
150
 
122
- # Logout from Chef Compliance
151
+ ### To Logout from Chef Compliance
123
152
 
124
153
  ```
125
154
  $ inspec compliance logout
@@ -6,14 +6,26 @@ require 'net/http'
6
6
  require 'uri'
7
7
 
8
8
  module Compliance
9
+ class ServerConfigurationMissing < StandardError
10
+ end
11
+
9
12
  # API Implementation does not hold any state by itself,
10
13
  # everything will be stored in local Configuration store
11
- class API
14
+ class API # rubocop:disable Metrics/ClassLength
12
15
  # return all compliance profiles available for the user
13
16
  def self.profiles(config)
14
- url = "#{config['server']}/user/compliance"
15
- # TODO, api should not be dependent on .supported?
16
- response = Compliance::HTTP.get(url, config['token'], config['insecure'], !config.supported?(:oidc))
17
+ # Chef Compliance
18
+ if is_compliance_server?(config)
19
+ url = "#{config['server']}/user/compliance"
20
+ # Chef Automate
21
+ elsif is_automate_server?(config)
22
+ url = "#{config['server']}/profiles/#{config['user']}"
23
+ else
24
+ raise ServerConfigurationMissing
25
+ end
26
+
27
+ headers = get_headers(config)
28
+ response = Compliance::HTTP.get(url, headers, config['insecure'])
17
29
  data = response.body
18
30
  response_code = response.code
19
31
  case response_code
@@ -21,11 +33,21 @@ module Compliance
21
33
  msg = 'success'
22
34
  profiles = JSON.parse(data)
23
35
  # iterate over profiles
24
- mapped_profiles = profiles.map do |owner, ps|
25
- ps.keys.map do |name|
26
- { org: owner, name: name }
27
- end
28
- end.flatten
36
+ if is_compliance_server?(config)
37
+ mapped_profiles = []
38
+ profiles.values.each { |org|
39
+ mapped_profiles += org.values
40
+ }
41
+ # Chef Automate pre 0.8.0
42
+ elsif is_automate_server_pre_080?(config)
43
+ mapped_profiles = profiles.values.flatten
44
+ else
45
+ owner_id = config['user']
46
+ mapped_profiles = profiles.map { |e|
47
+ e['owner_id'] = owner_id
48
+ e
49
+ }
50
+ end
29
51
  return msg, mapped_profiles
30
52
  when '401'
31
53
  msg = '401 Unauthorized. Please check your token.'
@@ -39,28 +61,30 @@ module Compliance
39
61
  # return the server api version
40
62
  # NB this method does not use Compliance::Configuration to allow for using
41
63
  # it before we know the version (e.g. oidc or not)
42
- def self.version(url, insecure)
43
- if url.nil?
44
- puts "
45
- Server configuration information is missing.
46
- Please login using `inspec compliance login https://compliance.test --user admin --insecure --token 'PASTE TOKEN HERE' `
47
- "
48
- else
49
- response = Compliance::HTTP.get(url+'/version', nil, insecure)
50
- data = response.body
51
- end
52
- if !data.nil?
53
- JSON.parse(data)
54
- else
55
- {}
56
- end
64
+ def self.version(config)
65
+ url = config['server']
66
+ insecure = config['insecure']
67
+
68
+ raise ServerConfigurationMissing if url.nil?
69
+
70
+ headers = get_headers(config)
71
+ response = Compliance::HTTP.get(url+'/version', headers, insecure)
72
+ return {} if response.code == '404'
73
+
74
+ data = response.body
75
+ return {} if data.nil? || data.empty?
76
+
77
+ parsed = JSON.parse(data)
78
+ return {} unless parsed.key?('version') && !parsed['version'].empty?
79
+
80
+ parsed
57
81
  end
58
82
 
59
83
  # verifies that a profile
60
84
  def self.exist?(config, profile)
61
85
  _msg, profiles = Compliance::API.profiles(config)
62
86
  if !profiles.empty?
63
- index = profiles.index { |p| "#{p[:org]}/#{p[:name]}" == profile }
87
+ index = profiles.index { |p| "#{p['owner_id']}/#{p['name']}" == profile }
64
88
  !index.nil? && index >= 0
65
89
  else
66
90
  false
@@ -68,13 +92,23 @@ Please login using `inspec compliance login https://compliance.test --user admin
68
92
  end
69
93
 
70
94
  def self.upload(config, owner, profile_name, archive_path)
71
- # upload the tar to Chef Compliance
72
- url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar"
73
- res = Compliance::HTTP.post_file(url, config['token'], archive_path, config['insecure'], !config.supported?(:oidc))
95
+ # Chef Compliance
96
+ if is_compliance_server?(config)
97
+ url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar"
98
+ # Chef Automate pre 0.8.0
99
+ elsif is_automate_server_pre_080?(config)
100
+ url = "#{config['server']}/#{config['user']}"
101
+ # Chef Automate
102
+ else
103
+ url = "#{config['server']}/profiles/#{config['user']}"
104
+ end
105
+
106
+ headers = get_headers(config)
107
+ res = Compliance::HTTP.post_file(url, headers, archive_path, config['insecure'])
74
108
  [res.is_a?(Net::HTTPSuccess), res.body]
75
109
  end
76
110
 
77
- # Use username and refresh_toke to get an API access token
111
+ # Use username and refresh_token to get an API access token
78
112
  def self.get_token_via_refresh_token(url, refresh_token, insecure)
79
113
  uri = URI.parse("#{url}/login")
80
114
  req = Net::HTTP::Post.new(uri.path)
@@ -121,5 +155,77 @@ Please login using `inspec compliance login https://compliance.test --user admin
121
155
 
122
156
  [success, msg, access_token]
123
157
  end
158
+
159
+ def self.get_headers(config)
160
+ token = get_token(config)
161
+ if is_automate_server?(config)
162
+ headers = { 'chef-delivery-enterprise' => config['automate']['ent'] }
163
+ if config['automate']['token_type'] == 'dctoken'
164
+ headers['x-data-collector-token'] = token
165
+ else
166
+ headers['chef-delivery-user'] = config['user']
167
+ headers['chef-delivery-token'] = token
168
+ end
169
+ else
170
+ headers = { 'Authorization' => "Bearer #{token}" }
171
+ end
172
+ headers
173
+ end
174
+
175
+ def self.get_token(config)
176
+ return config['token'] unless config['refresh_token']
177
+ _success, _msg, token = get_token_via_refresh_token(config['server'], config['refresh_token'], config['insecure'])
178
+ token
179
+ end
180
+
181
+ def self.target_url(config, profile)
182
+ if is_automate_server?(config)
183
+ owner, id = profile.split('/')
184
+ target = "#{config['server']}/profiles/#{owner}/#{id}/tar"
185
+ else
186
+ owner, id = profile.split('/')
187
+ target = "#{config['server']}/owners/#{owner}/compliance/#{id}/tar"
188
+ end
189
+ target
190
+ end
191
+
192
+ # returns a parsed url for `admin/profile` or `compliance://admin/profile`
193
+ def self.sanitize_profile_name(profile)
194
+ if URI(profile).scheme == 'compliance'
195
+ uri = URI(profile)
196
+ else
197
+ uri = URI("compliance://#{profile}")
198
+ end
199
+ uri.to_s.sub(%r{^compliance:\/\/}, '')
200
+ end
201
+
202
+ def self.is_compliance_server?(config)
203
+ config['server_type'] == 'compliance'
204
+ end
205
+
206
+ def self.is_automate_server_pre_080?(config)
207
+ # Automate versions before 0.8.x do not have a valid version in the config
208
+ return false unless config['server_type'] == 'automate'
209
+ server_version_from_config(config).nil?
210
+ end
211
+
212
+ def self.is_automate_server_080_and_later?(config)
213
+ # Automate versions 0.8.x and later will have a "version" key in the config
214
+ # that is properly parsed out via server_version_from_config below
215
+ return false unless config['server_type'] == 'automate'
216
+ !server_version_from_config(config).nil?
217
+ end
218
+
219
+ def self.is_automate_server?(config)
220
+ config['server_type'] == 'automate'
221
+ end
222
+
223
+ def self.server_version_from_config(config)
224
+ # Automate versions 0.8.x and later will have a "version" key in the config
225
+ # that looks like: "version":{"api":"compliance","version":"0.8.24"}
226
+ return nil unless config.key?('version')
227
+ return nil unless config['version'].is_a?(Hash)
228
+ config['version']['version']
229
+ end
124
230
  end
125
231
  end
@@ -36,6 +36,7 @@ module Compliance
36
36
 
37
37
  options['server'] = server
38
38
  url = options['server'] + options['apipath']
39
+
39
40
  if !options['user'].nil? && !options['password'].nil?
40
41
  # username / password
41
42
  _success, msg = login_username_password(url, options['user'], options['password'], options['insecure'])
@@ -56,22 +57,62 @@ module Compliance
56
57
  puts '', msg
57
58
  end
58
59
 
60
+ desc "login_automate SERVER --insecure --user='USER' --ent='ENT' --usertoken='TOKEN'", 'Log in to an Automate SERVER'
61
+ long_desc <<-LONGDESC
62
+ `login_automate` allows you to use InSpec with Chef Automate
63
+
64
+ You need to a user-token for communication with Chef Automate. More
65
+ information about token retrieval for Automate is available at:
66
+ https://docs.chef.io/api_delivery.html
67
+ LONGDESC
68
+ option :dctoken, type: :string,
69
+ desc: 'Data Collector token'
70
+ option :usertoken, type: :string,
71
+ desc: 'Automate user token'
72
+ option :user, type: :string,
73
+ desc: 'Automate username'
74
+ option :ent, type: :string,
75
+ desc: 'Enterprise for Chef Automate reporting'
76
+ option :insecure, aliases: :k, type: :boolean,
77
+ desc: 'Explicitly allows InSpec to perform "insecure" SSL connections and transfers'
78
+ def login_automate(server) # rubocop:disable Metrics/AbcSize
79
+ options['server'] = server
80
+ url = options['server'] + '/compliance'
81
+
82
+ if url && !options['user'].nil? && !options['ent'].nil? && (!options['dctoken'].nil? || !options['usertoken'].nil?)
83
+ msg = login_automate_config(url, options['user'], options['dctoken'], options['usertoken'], options['ent'], options['insecure'])
84
+ puts '', msg
85
+ exit 0
86
+ end
87
+
88
+ # parameters are missing if we reach here
89
+ puts 'Please specify an user using `--user \'USER\'`' if options['user'].nil?
90
+ puts 'Please specify an enterprise using `--ent \'cd\'`' if options['ent'].nil?
91
+ puts 'Please specify a token using `--usertoken=\'AUTOMATE_TOKEN\'`' if options['usertoken'].nil? && options['dctoken'].nil?
92
+ exit 1
93
+ end
94
+
59
95
  desc 'profiles', 'list all available profiles in Chef Compliance'
96
+
60
97
  def profiles
61
98
  config = Compliance::Configuration.new
62
99
  return if !loggedin(config)
63
100
 
64
101
  msg, profiles = Compliance::API.profiles(config)
102
+ profiles.sort_by! { |hsh| hsh['title'] }
65
103
  if !profiles.empty?
66
104
  # iterate over profiles
67
105
  headline('Available profiles:')
68
106
  profiles.each { |profile|
69
- li("#{profile[:org]}/#{profile[:name]}")
107
+ li("#{profile['title']} #{mark_text(profile['owner_id'] + '/' + profile['name'])}")
70
108
  }
71
109
  else
72
110
  puts msg, 'Could not find any profiles'
73
111
  exit 1
74
112
  end
113
+ rescue Compliance::ServerConfigurationMissing
114
+ puts "\nServer configuration information is missing. Please login using `inspec compliance login`"
115
+ exit 1
75
116
  end
76
117
 
77
118
  desc 'exec PROFILE', 'executes a Chef Compliance profile'
@@ -79,15 +120,43 @@ module Compliance
79
120
  def exec(*tests)
80
121
  config = Compliance::Configuration.new
81
122
  return if !loggedin(config)
82
-
83
123
  # iterate over tests and add compliance scheme
84
- tests = tests.map { |t| 'compliance://' + t }
85
-
124
+ tests = tests.map { |t| 'compliance://' + Compliance::API.sanitize_profile_name(t) }
86
125
  # execute profile from inspec exec implementation
87
126
  diagnose
88
127
  run_tests(tests, opts)
89
128
  end
90
129
 
130
+ desc 'download PROFILE', 'downloads a profile from Chef Compliance'
131
+ option :name, type: :string,
132
+ desc: 'Name of the archive filename (file type will be added)'
133
+ def download(profile_name)
134
+ o = options.dup
135
+ configure_logger(o)
136
+
137
+ config = Compliance::Configuration.new
138
+ return if !loggedin(config)
139
+
140
+ profile_name = Compliance::API.sanitize_profile_name(profile_name)
141
+ if Compliance::API.exist?(config, profile_name)
142
+ puts "Downloading `#{profile_name}`"
143
+
144
+ fetcher = Compliance::Fetcher.resolve(
145
+ {
146
+ compliance: profile_name,
147
+ },
148
+ )
149
+
150
+ # we provide a name, the fetcher adds the extension
151
+ _owner, id = profile_name.split('/')
152
+ file_name = fetcher.fetch(o.name || id)
153
+ puts "Profile stored to #{file_name}"
154
+ else
155
+ puts "Profile #{profile_name} is not available in Chef Compliance."
156
+ exit 1
157
+ end
158
+ end
159
+
91
160
  desc 'upload PATH', 'uploads a local profile to Chef Compliance'
92
161
  option :overwrite, type: :boolean, default: false,
93
162
  desc: 'Overwrite existing profile on Chef Compliance.'
@@ -100,6 +169,8 @@ module Compliance
100
169
  exit 1
101
170
  end
102
171
 
172
+ vendor_deps(path, options) if File.directory?(path)
173
+
103
174
  o = options.dup
104
175
  configure_logger(o)
105
176
  # check the profile, we only allow to upload valid profiles
@@ -120,7 +191,7 @@ module Compliance
120
191
  end
121
192
 
122
193
  # determine user information
123
- if config['token'].nil? || config['user'].nil?
194
+ if (config['token'].nil? && config['refresh_token'].nil?) || config['user'].nil?
124
195
  error.call('Please login via `inspec compliance login`')
125
196
  end
126
197
 
@@ -144,7 +215,6 @@ module Compliance
144
215
  # if it is a directory, tar it to tmp directory
145
216
  if File.directory?(path)
146
217
  archive_path = Dir::Tmpname.create([profile_name, '.tar.gz']) {}
147
- # archive_path = file.path
148
218
  puts "Generate temporary profile archive at #{archive_path}"
149
219
  profile.archive({ output: archive_path, ignore_errors: false, overwrite: true })
150
220
  else
@@ -154,7 +224,8 @@ module Compliance
154
224
  puts "Start upload to #{owner}/#{profile_name}"
155
225
  pname = ERB::Util.url_encode(profile_name)
156
226
 
157
- puts 'Uploading to Chef Compliance'
227
+ Compliance::API.is_automate_server?(config) ? upload_msg = 'Uploading to Chef Automate' : upload_msg = 'Uploading to Chef Compliance'
228
+ puts upload_msg
158
229
  success, msg = Compliance::API.upload(config, owner, pname, archive_path)
159
230
 
160
231
  if success
@@ -169,24 +240,27 @@ module Compliance
169
240
  desc 'version', 'displays the version of the Chef Compliance server'
170
241
  def version
171
242
  config = Compliance::Configuration.new
172
- info = Compliance::API.version(config['server'], config['insecure'])
243
+ info = Compliance::API.version(config)
173
244
  if !info.nil? && info['version']
174
- puts "Chef Compliance version: #{info['version']}"
245
+ puts "Name: #{info['api']}"
246
+ puts "Version: #{info['version']}"
175
247
  else
176
248
  puts 'Could not determine server version.'
177
249
  exit 1
178
250
  end
251
+ rescue Compliance::ServerConfigurationMissing
252
+ puts "\nServer configuration information is missing. Please login using `inspec compliance login`"
253
+ exit 1
179
254
  end
180
255
 
181
256
  desc 'logout', 'user logout from Chef Compliance'
182
257
  def logout
183
258
  config = Compliance::Configuration.new
184
- unless config.supported?(:oidc) || config['token'].nil?
259
+ unless config.supported?(:oidc) || config['token'].nil? || config['server_type'] == 'automate'
185
260
  config = Compliance::Configuration.new
186
261
  url = "#{config['server']}/logout"
187
262
  Compliance::API.post(url, config['token'], config['insecure'], !config.supported?(:oidc))
188
263
  end
189
-
190
264
  success = config.destroy
191
265
 
192
266
  if success
@@ -198,14 +272,42 @@ module Compliance
198
272
 
199
273
  private
200
274
 
275
+ def login_automate_config(url, user, dctoken, usertoken, ent, insecure) # rubocop:disable Metrics/ParameterLists
276
+ config = Compliance::Configuration.new
277
+ config.clean
278
+ config['user'] = user
279
+ config['server'] = url
280
+ config['automate'] = {}
281
+ config['automate']['ent'] = ent
282
+ config['server_type'] = 'automate'
283
+ config['insecure'] = insecure
284
+
285
+ # determine token method being used
286
+ if !dctoken.nil?
287
+ config['token'] = dctoken
288
+ token_type = 'dctoken'
289
+ token_msg = 'data collector token'
290
+ else
291
+ config['token'] = usertoken
292
+ token_type = 'usertoken'
293
+ token_msg = 'automate user token'
294
+ end
295
+ config['automate']['token_type'] = token_type
296
+ config['version'] = Compliance::API.version(config)
297
+ config.store
298
+ msg = "Stored configuration for Chef Automate: '#{url}' with user: '#{user}', ent: '#{ent}' and your #{token_msg}"
299
+ msg
300
+ end
301
+
201
302
  def login_refreshtoken(url, options)
202
- success, msg, access_token = Compliance::API.get_token_via_refresh_token(url, options['refresh_token'], options['insecure'])
303
+ success, msg, _access_token = Compliance::API.get_token_via_refresh_token(url, options['refresh_token'], options['insecure'])
203
304
  if success
204
305
  config = Compliance::Configuration.new
306
+ config.clean
205
307
  config['server'] = url
206
- config['token'] = access_token
207
308
  config['insecure'] = options['insecure']
208
- config['version'] = Compliance::API.version(url, options['insecure'])
309
+ config['server_type'] = 'compliance'
310
+ config['version'] = Compliance::API.version(config)
209
311
  config.store
210
312
  end
211
313
 
@@ -214,13 +316,15 @@ module Compliance
214
316
 
215
317
  def login_username_password(url, username, password, insecure)
216
318
  config = Compliance::Configuration.new
319
+ config.clean
217
320
  success, msg, api_token = Compliance::API.get_token_via_password(url, username, password, insecure)
218
321
  if success
219
322
  config['server'] = url
220
323
  config['user'] = username
221
324
  config['token'] = api_token
222
325
  config['insecure'] = insecure
223
- config['version'] = Compliance::API.version(url, insecure)
326
+ config['server_type'] = 'compliance'
327
+ config['version'] = Compliance::API.version(config)
224
328
  config.store
225
329
  success = true
226
330
  end
@@ -230,11 +334,13 @@ module Compliance
230
334
  # saves a user access token (limited time)
231
335
  def store_access_token(url, user, token, insecure)
232
336
  config = Compliance::Configuration.new
337
+ config.clean
233
338
  config['server'] = url
234
339
  config['insecure'] = insecure
235
340
  config['user'] = user
236
341
  config['token'] = token
237
- config['version'] = Compliance::API.version(url, insecure)
342
+ config['server_type'] = 'compliance'
343
+ config['version'] = Compliance::API.version(config)
238
344
  config.store
239
345
 
240
346
  [true, 'API access token stored']
@@ -243,22 +349,23 @@ module Compliance
243
349
  # saves a refresh token supplied by the user
244
350
  def store_refresh_token(url, refresh_token, verify, user, insecure)
245
351
  config = Compliance::Configuration.new
352
+ config.clean
246
353
  config['server'] = url
247
354
  config['refresh_token'] = refresh_token
248
355
  config['user'] = user
249
356
  config['insecure'] = insecure
250
- config['version'] = Compliance::API.version(url, insecure)
357
+ config['server_type'] = 'compliance'
358
+ config['version'] = Compliance::API.version(config)
251
359
 
252
360
  if !verify
253
361
  config.store
254
362
  success = true
255
363
  msg = 'API refresh token stored'
256
364
  else
257
- success, msg, access_token = Compliance::API.get_token_via_refresh_token(url, refresh_token, insecure)
365
+ success, msg, _access_token= Compliance::API.get_token_via_refresh_token(url, refresh_token, insecure)
258
366
  if success
259
- config['token'] = access_token
260
367
  config.store
261
- msg = 'API access token verified and stored'
368
+ msg = 'API access token verified'
262
369
  end
263
370
  end
264
371
 
@@ -267,7 +374,7 @@ module Compliance
267
374
 
268
375
  def loggedin(config)
269
376
  serverknown = !config['server'].nil?
270
- puts 'You need to login first with `inspec compliance login`' if !serverknown
377
+ puts 'You need to login first with `inspec compliance login` or `inspec compliance login_automate`' if !serverknown
271
378
  serverknown
272
379
  end
273
380
  end
@@ -28,6 +28,14 @@ module Compliance
28
28
  @config[key] = value
29
29
  end
30
30
 
31
+ def key?(key)
32
+ @config.key?(key)
33
+ end
34
+
35
+ def clean
36
+ @config = {}
37
+ end
38
+
31
39
  # return the json data
32
40
  def get
33
41
  if File.exist?(@config_file)