inspec 1.51.15 → 1.51.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (404) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +101 -101
  3. data/CHANGELOG.md +2922 -2915
  4. data/Gemfile +53 -53
  5. data/LICENSE +14 -14
  6. data/MAINTAINERS.md +31 -31
  7. data/MAINTAINERS.toml +47 -47
  8. data/README.md +419 -419
  9. data/Rakefile +167 -167
  10. data/bin/inspec +12 -12
  11. data/docs/.gitignore +2 -2
  12. data/docs/README.md +40 -40
  13. data/docs/dsl_inspec.md +258 -258
  14. data/docs/dsl_resource.md +93 -93
  15. data/docs/glossary.md +99 -99
  16. data/docs/habitat.md +191 -191
  17. data/docs/inspec_and_friends.md +107 -107
  18. data/docs/matchers.md +165 -165
  19. data/docs/migration.md +293 -293
  20. data/docs/plugin_kitchen_inspec.md +49 -49
  21. data/docs/profiles.md +370 -370
  22. data/docs/resources/aide_conf.md.erb +78 -78
  23. data/docs/resources/apache.md.erb +66 -66
  24. data/docs/resources/apache_conf.md.erb +67 -67
  25. data/docs/resources/apt.md.erb +70 -70
  26. data/docs/resources/audit_policy.md.erb +46 -46
  27. data/docs/resources/auditd.md.erb +78 -78
  28. data/docs/resources/auditd_conf.md.erb +68 -68
  29. data/docs/resources/auditd_rules.md.erb +116 -116
  30. data/docs/resources/bash.md.erb +74 -74
  31. data/docs/resources/bond.md.erb +89 -89
  32. data/docs/resources/bridge.md.erb +54 -54
  33. data/docs/resources/bsd_service.md.erb +65 -65
  34. data/docs/resources/command.md.erb +137 -137
  35. data/docs/resources/cpan.md.erb +77 -77
  36. data/docs/resources/cran.md.erb +63 -63
  37. data/docs/resources/crontab.md.erb +87 -87
  38. data/docs/resources/csv.md.erb +53 -53
  39. data/docs/resources/dh_params.md.erb +216 -216
  40. data/docs/resources/directory.md.erb +28 -28
  41. data/docs/resources/docker.md.erb +163 -163
  42. data/docs/resources/docker_container.md.erb +99 -99
  43. data/docs/resources/docker_image.md.erb +93 -93
  44. data/docs/resources/docker_service.md.erb +113 -113
  45. data/docs/resources/elasticsearch.md.erb +230 -230
  46. data/docs/resources/etc_fstab.md.erb +124 -124
  47. data/docs/resources/etc_group.md.erb +74 -74
  48. data/docs/resources/etc_hosts.md.erb +75 -75
  49. data/docs/resources/etc_hosts_allow.md.erb +73 -73
  50. data/docs/resources/etc_hosts_deny.md.erb +73 -73
  51. data/docs/resources/file.md.erb +512 -512
  52. data/docs/resources/filesystem.md.erb +40 -40
  53. data/docs/resources/firewalld.md.erb +105 -105
  54. data/docs/resources/gem.md.erb +78 -78
  55. data/docs/resources/group.md.erb +60 -60
  56. data/docs/resources/grub_conf.md.erb +101 -101
  57. data/docs/resources/host.md.erb +77 -77
  58. data/docs/resources/http.md.erb +104 -104
  59. data/docs/resources/iis_app.md.erb +120 -120
  60. data/docs/resources/iis_site.md.erb +132 -132
  61. data/docs/resources/inetd_conf.md.erb +95 -95
  62. data/docs/resources/ini.md.erb +72 -72
  63. data/docs/resources/interface.md.erb +55 -55
  64. data/docs/resources/iptables.md.erb +63 -63
  65. data/docs/resources/json.md.erb +61 -61
  66. data/docs/resources/kernel_module.md.erb +106 -106
  67. data/docs/resources/kernel_parameter.md.erb +58 -58
  68. data/docs/resources/key_rsa.md.erb +73 -73
  69. data/docs/resources/launchd_service.md.erb +56 -56
  70. data/docs/resources/limits_conf.md.erb +66 -66
  71. data/docs/resources/login_def.md.erb +62 -62
  72. data/docs/resources/mount.md.erb +68 -68
  73. data/docs/resources/mssql_session.md.erb +59 -59
  74. data/docs/resources/mysql_conf.md.erb +98 -98
  75. data/docs/resources/mysql_session.md.erb +73 -73
  76. data/docs/resources/nginx.md.erb +78 -78
  77. data/docs/resources/nginx_conf.md.erb +127 -127
  78. data/docs/resources/npm.md.erb +59 -59
  79. data/docs/resources/ntp_conf.md.erb +59 -59
  80. data/docs/resources/oneget.md.erb +52 -52
  81. data/docs/resources/oracledb_session.md.erb +51 -51
  82. data/docs/resources/os.md.erb +140 -140
  83. data/docs/resources/os_env.md.erb +77 -77
  84. data/docs/resources/package.md.erb +119 -119
  85. data/docs/resources/packages.md.erb +66 -66
  86. data/docs/resources/parse_config.md.erb +102 -102
  87. data/docs/resources/parse_config_file.md.erb +137 -137
  88. data/docs/resources/passwd.md.erb +140 -140
  89. data/docs/resources/pip.md.erb +66 -66
  90. data/docs/resources/port.md.erb +136 -136
  91. data/docs/resources/postgres_conf.md.erb +78 -78
  92. data/docs/resources/postgres_hba_conf.md.erb +92 -92
  93. data/docs/resources/postgres_ident_conf.md.erb +75 -75
  94. data/docs/resources/postgres_session.md.erb +68 -68
  95. data/docs/resources/powershell.md.erb +101 -101
  96. data/docs/resources/processes.md.erb +107 -107
  97. data/docs/resources/rabbitmq_config.md.erb +40 -40
  98. data/docs/resources/registry_key.md.erb +157 -157
  99. data/docs/resources/runit_service.md.erb +56 -56
  100. data/docs/resources/security_policy.md.erb +46 -46
  101. data/docs/resources/service.md.erb +120 -120
  102. data/docs/resources/shadow.md.erb +143 -143
  103. data/docs/resources/ssh_config.md.erb +79 -79
  104. data/docs/resources/sshd_config.md.erb +82 -82
  105. data/docs/resources/ssl.md.erb +118 -118
  106. data/docs/resources/sys_info.md.erb +41 -41
  107. data/docs/resources/systemd_service.md.erb +56 -56
  108. data/docs/resources/sysv_service.md.erb +56 -56
  109. data/docs/resources/upstart_service.md.erb +56 -56
  110. data/docs/resources/user.md.erb +139 -139
  111. data/docs/resources/users.md.erb +126 -126
  112. data/docs/resources/vbscript.md.erb +54 -54
  113. data/docs/resources/virtualization.md.erb +56 -56
  114. data/docs/resources/windows_feature.md.erb +46 -46
  115. data/docs/resources/windows_hotfix.md.erb +52 -52
  116. data/docs/resources/windows_task.md.erb +89 -89
  117. data/docs/resources/wmi.md.erb +80 -80
  118. data/docs/resources/x509_certificate.md.erb +150 -150
  119. data/docs/resources/xinetd_conf.md.erb +155 -155
  120. data/docs/resources/xml.md.erb +84 -84
  121. data/docs/resources/yaml.md.erb +68 -68
  122. data/docs/resources/yum.md.erb +97 -97
  123. data/docs/resources/zfs_dataset.md.erb +52 -52
  124. data/docs/resources/zfs_pool.md.erb +46 -46
  125. data/docs/ruby_usage.md +203 -203
  126. data/docs/shared/matcher_be.md.erb +1 -1
  127. data/docs/shared/matcher_cmp.md.erb +43 -43
  128. data/docs/shared/matcher_eq.md.erb +3 -3
  129. data/docs/shared/matcher_include.md.erb +1 -1
  130. data/docs/shared/matcher_match.md.erb +1 -1
  131. data/docs/shell.md +172 -172
  132. data/examples/README.md +8 -8
  133. data/examples/inheritance/README.md +65 -65
  134. data/examples/inheritance/controls/example.rb +14 -14
  135. data/examples/inheritance/inspec.yml +15 -15
  136. data/examples/kitchen-ansible/.kitchen.yml +25 -25
  137. data/examples/kitchen-ansible/Gemfile +19 -19
  138. data/examples/kitchen-ansible/README.md +53 -53
  139. data/examples/kitchen-ansible/files/nginx.repo +6 -6
  140. data/examples/kitchen-ansible/tasks/main.yml +16 -16
  141. data/examples/kitchen-ansible/test/integration/default/default.yml +5 -5
  142. data/examples/kitchen-ansible/test/integration/default/web_spec.rb +28 -28
  143. data/examples/kitchen-chef/.kitchen.yml +20 -20
  144. data/examples/kitchen-chef/Berksfile +3 -3
  145. data/examples/kitchen-chef/Gemfile +19 -19
  146. data/examples/kitchen-chef/README.md +27 -27
  147. data/examples/kitchen-chef/metadata.rb +7 -7
  148. data/examples/kitchen-chef/recipes/default.rb +6 -6
  149. data/examples/kitchen-chef/recipes/nginx.rb +30 -30
  150. data/examples/kitchen-chef/test/integration/default/web_spec.rb +28 -28
  151. data/examples/kitchen-puppet/.kitchen.yml +22 -22
  152. data/examples/kitchen-puppet/Gemfile +20 -20
  153. data/examples/kitchen-puppet/Puppetfile +25 -25
  154. data/examples/kitchen-puppet/README.md +53 -53
  155. data/examples/kitchen-puppet/manifests/site.pp +33 -33
  156. data/examples/kitchen-puppet/metadata.json +11 -11
  157. data/examples/kitchen-puppet/test/integration/default/web_spec.rb +28 -28
  158. data/examples/meta-profile/README.md +37 -37
  159. data/examples/meta-profile/controls/example.rb +13 -13
  160. data/examples/meta-profile/inspec.yml +13 -13
  161. data/examples/profile-attribute.yml +2 -2
  162. data/examples/profile-attribute/README.md +14 -14
  163. data/examples/profile-attribute/controls/example.rb +11 -11
  164. data/examples/profile-attribute/inspec.yml +8 -8
  165. data/examples/profile-sensitive/README.md +29 -29
  166. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -9
  167. data/examples/profile-sensitive/controls/sensitive.rb +9 -9
  168. data/examples/profile-sensitive/inspec.yml +8 -8
  169. data/examples/profile/README.md +48 -48
  170. data/examples/profile/controls/example.rb +23 -23
  171. data/examples/profile/controls/gordon.rb +36 -36
  172. data/examples/profile/controls/meta.rb +34 -34
  173. data/examples/profile/inspec.yml +10 -10
  174. data/examples/profile/libraries/gordon_config.rb +53 -53
  175. data/inspec.gemspec +47 -47
  176. data/lib/bundles/README.md +3 -3
  177. data/lib/bundles/inspec-artifact.rb +7 -7
  178. data/lib/bundles/inspec-artifact/README.md +1 -1
  179. data/lib/bundles/inspec-artifact/cli.rb +277 -277
  180. data/lib/bundles/inspec-compliance.rb +16 -16
  181. data/lib/bundles/inspec-compliance/.kitchen.yml +20 -20
  182. data/lib/bundles/inspec-compliance/README.md +185 -185
  183. data/lib/bundles/inspec-compliance/api.rb +316 -316
  184. data/lib/bundles/inspec-compliance/api/login.rb +152 -152
  185. data/lib/bundles/inspec-compliance/bootstrap.sh +41 -41
  186. data/lib/bundles/inspec-compliance/cli.rb +277 -277
  187. data/lib/bundles/inspec-compliance/configuration.rb +103 -103
  188. data/lib/bundles/inspec-compliance/http.rb +86 -86
  189. data/lib/bundles/inspec-compliance/support.rb +36 -36
  190. data/lib/bundles/inspec-compliance/target.rb +98 -98
  191. data/lib/bundles/inspec-compliance/test/integration/default/cli.rb +93 -93
  192. data/lib/bundles/inspec-habitat.rb +12 -12
  193. data/lib/bundles/inspec-habitat/cli.rb +36 -36
  194. data/lib/bundles/inspec-habitat/log.rb +10 -10
  195. data/lib/bundles/inspec-habitat/profile.rb +390 -390
  196. data/lib/bundles/inspec-init.rb +8 -8
  197. data/lib/bundles/inspec-init/README.md +31 -31
  198. data/lib/bundles/inspec-init/cli.rb +97 -97
  199. data/lib/bundles/inspec-init/templates/profile/README.md +3 -3
  200. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +19 -19
  201. data/lib/bundles/inspec-init/templates/profile/inspec.yml +8 -8
  202. data/lib/bundles/inspec-supermarket.rb +13 -13
  203. data/lib/bundles/inspec-supermarket/README.md +45 -45
  204. data/lib/bundles/inspec-supermarket/api.rb +84 -84
  205. data/lib/bundles/inspec-supermarket/cli.rb +65 -65
  206. data/lib/bundles/inspec-supermarket/target.rb +34 -34
  207. data/lib/fetchers/git.rb +163 -163
  208. data/lib/fetchers/local.rb +74 -74
  209. data/lib/fetchers/mock.rb +35 -35
  210. data/lib/fetchers/url.rb +204 -204
  211. data/lib/inspec.rb +24 -24
  212. data/lib/inspec/archive/tar.rb +29 -29
  213. data/lib/inspec/archive/zip.rb +19 -19
  214. data/lib/inspec/backend.rb +92 -92
  215. data/lib/inspec/base_cli.rb +327 -324
  216. data/lib/inspec/cached_fetcher.rb +66 -66
  217. data/lib/inspec/cli.rb +298 -298
  218. data/lib/inspec/completions/bash.sh.erb +45 -45
  219. data/lib/inspec/completions/fish.sh.erb +34 -34
  220. data/lib/inspec/completions/zsh.sh.erb +61 -61
  221. data/lib/inspec/control_eval_context.rb +179 -179
  222. data/lib/inspec/dependencies/cache.rb +72 -72
  223. data/lib/inspec/dependencies/dependency_set.rb +92 -92
  224. data/lib/inspec/dependencies/lockfile.rb +115 -115
  225. data/lib/inspec/dependencies/requirement.rb +123 -123
  226. data/lib/inspec/dependencies/resolver.rb +86 -86
  227. data/lib/inspec/describe.rb +27 -27
  228. data/lib/inspec/dsl.rb +66 -66
  229. data/lib/inspec/dsl_shared.rb +33 -33
  230. data/lib/inspec/env_printer.rb +157 -157
  231. data/lib/inspec/errors.rb +13 -13
  232. data/lib/inspec/exceptions.rb +12 -12
  233. data/lib/inspec/expect.rb +45 -45
  234. data/lib/inspec/fetcher.rb +45 -45
  235. data/lib/inspec/file_provider.rb +275 -275
  236. data/lib/inspec/formatters.rb +3 -3
  237. data/lib/inspec/formatters/base.rb +208 -208
  238. data/lib/inspec/formatters/json_rspec.rb +20 -20
  239. data/lib/inspec/formatters/show_progress.rb +12 -12
  240. data/lib/inspec/library_eval_context.rb +58 -58
  241. data/lib/inspec/log.rb +11 -11
  242. data/lib/inspec/metadata.rb +253 -253
  243. data/lib/inspec/method_source.rb +24 -24
  244. data/lib/inspec/objects.rb +14 -14
  245. data/lib/inspec/objects/attribute.rb +65 -65
  246. data/lib/inspec/objects/control.rb +61 -61
  247. data/lib/inspec/objects/describe.rb +92 -92
  248. data/lib/inspec/objects/each_loop.rb +36 -36
  249. data/lib/inspec/objects/list.rb +15 -15
  250. data/lib/inspec/objects/or_test.rb +40 -40
  251. data/lib/inspec/objects/ruby_helper.rb +15 -15
  252. data/lib/inspec/objects/tag.rb +27 -27
  253. data/lib/inspec/objects/test.rb +87 -87
  254. data/lib/inspec/objects/value.rb +27 -27
  255. data/lib/inspec/plugins.rb +60 -60
  256. data/lib/inspec/plugins/cli.rb +24 -24
  257. data/lib/inspec/plugins/fetcher.rb +86 -86
  258. data/lib/inspec/plugins/resource.rb +132 -132
  259. data/lib/inspec/plugins/secret.rb +15 -15
  260. data/lib/inspec/plugins/source_reader.rb +40 -40
  261. data/lib/inspec/polyfill.rb +12 -12
  262. data/lib/inspec/profile.rb +510 -510
  263. data/lib/inspec/profile_context.rb +207 -207
  264. data/lib/inspec/profile_vendor.rb +66 -66
  265. data/lib/inspec/reporters.rb +50 -50
  266. data/lib/inspec/reporters/base.rb +24 -24
  267. data/lib/inspec/reporters/cli.rb +395 -395
  268. data/lib/inspec/reporters/json.rb +138 -134
  269. data/lib/inspec/reporters/json_min.rb +48 -48
  270. data/lib/inspec/reporters/junit.rb +77 -77
  271. data/lib/inspec/require_loader.rb +33 -33
  272. data/lib/inspec/resource.rb +176 -176
  273. data/lib/inspec/rule.rb +266 -266
  274. data/lib/inspec/runner.rb +342 -340
  275. data/lib/inspec/runner_mock.rb +41 -41
  276. data/lib/inspec/runner_rspec.rb +163 -163
  277. data/lib/inspec/runtime_profile.rb +26 -26
  278. data/lib/inspec/schema.rb +192 -186
  279. data/lib/inspec/secrets.rb +19 -19
  280. data/lib/inspec/secrets/yaml.rb +30 -30
  281. data/lib/inspec/shell.rb +223 -223
  282. data/lib/inspec/shell_detector.rb +90 -90
  283. data/lib/inspec/source_reader.rb +29 -29
  284. data/lib/inspec/version.rb +8 -8
  285. data/lib/matchers/matchers.rb +397 -397
  286. data/lib/resources/aide_conf.rb +160 -160
  287. data/lib/resources/apache.rb +49 -49
  288. data/lib/resources/apache_conf.rb +158 -158
  289. data/lib/resources/apt.rb +150 -150
  290. data/lib/resources/audit_policy.rb +64 -64
  291. data/lib/resources/auditd.rb +233 -233
  292. data/lib/resources/auditd_conf.rb +56 -56
  293. data/lib/resources/auditd_rules.rb +205 -205
  294. data/lib/resources/bash.rb +36 -36
  295. data/lib/resources/bond.rb +69 -69
  296. data/lib/resources/bridge.rb +123 -123
  297. data/lib/resources/command.rb +69 -69
  298. data/lib/resources/cpan.rb +60 -60
  299. data/lib/resources/cran.rb +66 -66
  300. data/lib/resources/crontab.rb +169 -169
  301. data/lib/resources/csv.rb +58 -58
  302. data/lib/resources/dh_params.rb +83 -83
  303. data/lib/resources/directory.rb +25 -25
  304. data/lib/resources/docker.rb +239 -239
  305. data/lib/resources/docker_container.rb +92 -92
  306. data/lib/resources/docker_image.rb +86 -86
  307. data/lib/resources/docker_object.rb +57 -57
  308. data/lib/resources/docker_service.rb +94 -94
  309. data/lib/resources/elasticsearch.rb +168 -168
  310. data/lib/resources/etc_fstab.rb +102 -102
  311. data/lib/resources/etc_group.rb +157 -157
  312. data/lib/resources/etc_hosts.rb +81 -81
  313. data/lib/resources/etc_hosts_allow_deny.rb +122 -122
  314. data/lib/resources/file.rb +298 -298
  315. data/lib/resources/filesystem.rb +31 -31
  316. data/lib/resources/firewalld.rb +144 -144
  317. data/lib/resources/gem.rb +71 -71
  318. data/lib/resources/groups.rb +213 -213
  319. data/lib/resources/grub_conf.rb +237 -237
  320. data/lib/resources/host.rb +300 -300
  321. data/lib/resources/http.rb +252 -252
  322. data/lib/resources/iis_app.rb +103 -103
  323. data/lib/resources/iis_site.rb +147 -147
  324. data/lib/resources/inetd_conf.rb +63 -63
  325. data/lib/resources/ini.rb +29 -29
  326. data/lib/resources/interface.rb +130 -130
  327. data/lib/resources/iptables.rb +70 -70
  328. data/lib/resources/json.rb +115 -115
  329. data/lib/resources/kernel_module.rb +110 -110
  330. data/lib/resources/kernel_parameter.rb +58 -58
  331. data/lib/resources/key_rsa.rb +67 -67
  332. data/lib/resources/limits_conf.rb +56 -56
  333. data/lib/resources/login_def.rb +67 -67
  334. data/lib/resources/mount.rb +90 -90
  335. data/lib/resources/mssql_session.rb +103 -103
  336. data/lib/resources/mysql.rb +82 -82
  337. data/lib/resources/mysql_conf.rb +133 -133
  338. data/lib/resources/mysql_session.rb +72 -72
  339. data/lib/resources/nginx.rb +97 -97
  340. data/lib/resources/nginx_conf.rb +228 -228
  341. data/lib/resources/npm.rb +48 -48
  342. data/lib/resources/ntp_conf.rb +59 -59
  343. data/lib/resources/oneget.rb +72 -72
  344. data/lib/resources/oracledb_session.rb +140 -140
  345. data/lib/resources/os.rb +46 -46
  346. data/lib/resources/os_env.rb +76 -76
  347. data/lib/resources/package.rb +357 -357
  348. data/lib/resources/packages.rb +112 -112
  349. data/lib/resources/parse_config.rb +116 -116
  350. data/lib/resources/passwd.rb +96 -96
  351. data/lib/resources/pip.rb +89 -89
  352. data/lib/resources/platform.rb +112 -112
  353. data/lib/resources/port.rb +771 -771
  354. data/lib/resources/postgres.rb +132 -132
  355. data/lib/resources/postgres_conf.rb +122 -122
  356. data/lib/resources/postgres_hba_conf.rb +101 -101
  357. data/lib/resources/postgres_ident_conf.rb +79 -79
  358. data/lib/resources/postgres_session.rb +72 -72
  359. data/lib/resources/powershell.rb +58 -58
  360. data/lib/resources/processes.rb +204 -204
  361. data/lib/resources/rabbitmq_conf.rb +53 -53
  362. data/lib/resources/registry_key.rb +296 -296
  363. data/lib/resources/security_policy.rb +181 -181
  364. data/lib/resources/service.rb +784 -784
  365. data/lib/resources/shadow.rb +141 -141
  366. data/lib/resources/ssh_conf.rb +102 -102
  367. data/lib/resources/ssl.rb +99 -99
  368. data/lib/resources/sys_info.rb +26 -26
  369. data/lib/resources/toml.rb +32 -32
  370. data/lib/resources/users.rb +652 -652
  371. data/lib/resources/vbscript.rb +70 -70
  372. data/lib/resources/virtualization.rb +251 -251
  373. data/lib/resources/windows_feature.rb +85 -85
  374. data/lib/resources/windows_hotfix.rb +35 -35
  375. data/lib/resources/windows_task.rb +106 -106
  376. data/lib/resources/wmi.rb +114 -114
  377. data/lib/resources/x509_certificate.rb +143 -143
  378. data/lib/resources/xinetd.rb +112 -112
  379. data/lib/resources/xml.rb +45 -45
  380. data/lib/resources/yaml.rb +45 -45
  381. data/lib/resources/yum.rb +181 -181
  382. data/lib/resources/zfs_dataset.rb +60 -60
  383. data/lib/resources/zfs_pool.rb +49 -49
  384. data/lib/source_readers/flat.rb +39 -39
  385. data/lib/source_readers/inspec.rb +75 -75
  386. data/lib/utils/command_wrapper.rb +27 -27
  387. data/lib/utils/convert.rb +12 -12
  388. data/lib/utils/database_helpers.rb +77 -77
  389. data/lib/utils/erlang_parser.rb +192 -192
  390. data/lib/utils/filter.rb +272 -272
  391. data/lib/utils/filter_array.rb +27 -27
  392. data/lib/utils/find_files.rb +44 -44
  393. data/lib/utils/hash.rb +41 -41
  394. data/lib/utils/json_log.rb +18 -18
  395. data/lib/utils/latest_version.rb +22 -22
  396. data/lib/utils/modulator.rb +12 -12
  397. data/lib/utils/nginx_parser.rb +85 -85
  398. data/lib/utils/object_traversal.rb +49 -49
  399. data/lib/utils/parser.rb +274 -274
  400. data/lib/utils/plugin_registry.rb +93 -93
  401. data/lib/utils/simpleconfig.rb +132 -132
  402. data/lib/utils/spdx.rb +13 -13
  403. data/lib/utils/spdx.txt +343 -343
  404. metadata +2 -2
@@ -1,15 +1,15 @@
1
- # encoding: utf-8
2
- # author: Dominik Richter
3
- # author: Christoph Hartmann
4
-
5
- require 'utils/plugin_registry'
6
-
7
- module Inspec
8
- module Plugins
9
- class Secret < PluginRegistry::Plugin
10
- def self.plugin_registry
11
- Inspec::SecretsBackend
12
- end
13
- end
14
- end
15
- end
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'utils/plugin_registry'
6
+
7
+ module Inspec
8
+ module Plugins
9
+ class Secret < PluginRegistry::Plugin
10
+ def self.plugin_registry
11
+ Inspec::SecretsBackend
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,40 +1,40 @@
1
- # encoding: utf-8
2
- # author: Dominik Richter
3
- # author: Christoph Hartmann
4
-
5
- require 'utils/plugin_registry'
6
-
7
- module Inspec
8
- module Plugins
9
- class SourceReader < PluginRegistry::Plugin
10
- def self.plugin_registry
11
- Inspec::SourceReader
12
- end
13
-
14
- # Retrieve this profile's metadata.
15
- #
16
- # @return [Inspec::Metadata] profile metadata
17
- def metadata
18
- raise "SourceReader #{self} does not implement `metadata()`. This method is required"
19
- end
20
-
21
- # Retrieve this profile's tests
22
- #
23
- # "tests" here refers to a test file. Individual controls and anonymous
24
- # tests are later extracted from the raw contents of a test file. The map
25
- # her simply maps from a test file name to the file contents.
26
- #
27
- # @return [Hash] Collection with references pointing to test contents
28
- def tests
29
- raise "SourceReader #{self} does not implement `tests()`. This method is required"
30
- end
31
-
32
- # Retrieve this profile's libraries
33
- #
34
- # @return [Hash] Collection with references pointing to library contents
35
- def libraries
36
- raise "SourceReader #{self} does not implement `libraries()`. This method is required"
37
- end
38
- end
39
- end
40
- end
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'utils/plugin_registry'
6
+
7
+ module Inspec
8
+ module Plugins
9
+ class SourceReader < PluginRegistry::Plugin
10
+ def self.plugin_registry
11
+ Inspec::SourceReader
12
+ end
13
+
14
+ # Retrieve this profile's metadata.
15
+ #
16
+ # @return [Inspec::Metadata] profile metadata
17
+ def metadata
18
+ raise "SourceReader #{self} does not implement `metadata()`. This method is required"
19
+ end
20
+
21
+ # Retrieve this profile's tests
22
+ #
23
+ # "tests" here refers to a test file. Individual controls and anonymous
24
+ # tests are later extracted from the raw contents of a test file. The map
25
+ # her simply maps from a test file name to the file contents.
26
+ #
27
+ # @return [Hash] Collection with references pointing to test contents
28
+ def tests
29
+ raise "SourceReader #{self} does not implement `tests()`. This method is required"
30
+ end
31
+
32
+ # Retrieve this profile's libraries
33
+ #
34
+ # @return [Hash] Collection with references pointing to library contents
35
+ def libraries
36
+ raise "SourceReader #{self} does not implement `libraries()`. This method is required"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,12 +1,12 @@
1
- # encoding: utf-8
2
- # copyright: 2016, Chef Software Inc.
3
- # author: Dominik Richter
4
- # author: Christoph Hartmann
5
-
6
- class Struct
7
- unless instance_methods.include? :to_h
8
- def to_h
9
- Hash[each_pair.to_a]
10
- end
11
- end
12
- end
1
+ # encoding: utf-8
2
+ # copyright: 2016, Chef Software Inc.
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+
6
+ class Struct
7
+ unless instance_methods.include? :to_h
8
+ def to_h
9
+ Hash[each_pair.to_a]
10
+ end
11
+ end
12
+ end
@@ -1,510 +1,510 @@
1
- # encoding: utf-8
2
- # Copyright 2015 Dominik Richter
3
- # author: Dominik Richter
4
- # author: Christoph Hartmann
5
-
6
- require 'forwardable'
7
- require 'openssl'
8
- require 'inspec/polyfill'
9
- require 'inspec/cached_fetcher'
10
- require 'inspec/file_provider'
11
- require 'inspec/source_reader'
12
- require 'inspec/metadata'
13
- require 'inspec/backend'
14
- require 'inspec/rule'
15
- require 'inspec/log'
16
- require 'inspec/profile_context'
17
- require 'inspec/runtime_profile'
18
- require 'inspec/method_source'
19
- require 'inspec/dependencies/cache'
20
- require 'inspec/dependencies/lockfile'
21
- require 'inspec/dependencies/dependency_set'
22
-
23
- module Inspec
24
- class Profile
25
- extend Forwardable
26
-
27
- def self.resolve_target(target, cache)
28
- Inspec::Log.debug "Resolve #{target} into cache #{cache.path}"
29
- Inspec::CachedFetcher.new(target, cache)
30
- end
31
-
32
- # Check if the profile contains a vendored cache, move content into global cache
33
- # TODO: use relative file provider
34
- # TODO: use source reader for Cache as well
35
- def self.copy_deps_into_cache(file_provider, opts)
36
- # filter content
37
- cache = file_provider.files.find_all do |entry|
38
- entry.start_with?('vendor')
39
- end
40
- content = Hash[cache.map { |x| [x, file_provider.binread(x)] }]
41
- keys = content.keys
42
- keys.each do |key|
43
- next if content[key].nil?
44
- # remove prefix
45
- rel = Pathname.new(key).relative_path_from(Pathname.new('vendor')).to_s
46
- tar = Pathname.new(opts[:vendor_cache].path).join(rel)
47
-
48
- FileUtils.mkdir_p tar.dirname.to_s
49
- Inspec::Log.debug "Copy #{tar} to cache directory"
50
- File.binwrite(tar.to_s, content[key])
51
- end
52
- end
53
-
54
- def self.for_path(path, opts)
55
- file_provider = FileProvider.for_path(path)
56
- rp = file_provider.relative_provider
57
-
58
- # copy embedded dependecies into global cache
59
- copy_deps_into_cache(rp, opts) unless opts[:vendor_cache].nil?
60
-
61
- reader = Inspec::SourceReader.resolve(rp)
62
- if reader.nil?
63
- raise("Don't understand inspec profile in #{path}, it " \
64
- "doesn't look like a supported profile structure.")
65
- end
66
- new(reader, opts)
67
- end
68
-
69
- def self.for_fetcher(fetcher, opts)
70
- opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
71
- path, writable = fetcher.fetch
72
- for_path(path, opts.merge(target: fetcher.target, writable: writable))
73
- end
74
-
75
- def self.for_target(target, opts = {})
76
- opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
77
- fetcher = resolve_target(target, opts[:vendor_cache])
78
- for_fetcher(fetcher, opts)
79
- end
80
-
81
- attr_reader :source_reader, :backend, :runner_context, :check_mode
82
- def_delegator :@source_reader, :tests
83
- def_delegator :@source_reader, :libraries
84
- def_delegator :@source_reader, :metadata
85
-
86
- # rubocop:disable Metrics/AbcSize
87
- def initialize(source_reader, options = {})
88
- @source_reader = source_reader
89
- @target = options[:target]
90
- @logger = options[:logger] || Logger.new(nil)
91
- @locked_dependencies = options[:dependencies]
92
- @controls = options[:controls] || []
93
- @writable = options[:writable] || false
94
- @profile_id = options[:id]
95
- @cache = options[:vendor_cache] || Cache.new
96
- @attr_values = options[:attributes]
97
- @tests_collected = false
98
- @libraries_loaded = false
99
- @check_mode = options[:check_mode] || false
100
- Metadata.finalize(@source_reader.metadata, @profile_id, options)
101
-
102
- # if a backend has already been created, clone it so each profile has its own unique backend object
103
- # otherwise, create a new backend object
104
- #
105
- # This is necessary since we store the RuntimeProfile on the backend object. If a user runs `inspec exec`
106
- # with multiple profiles, only the RuntimeProfile for the last-loaded profile will be available if
107
- # we share the backend between profiles.
108
- #
109
- # This will cause issues if a profile attempts to load a file via `inspec.profile.file`
110
- train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646
111
- @backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
112
- @runtime_profile = RuntimeProfile.new(self)
113
- @backend.profile = @runtime_profile
114
-
115
- @runner_context =
116
- options[:profile_context] ||
117
- Inspec::ProfileContext.for_profile(self, @backend, @attr_values)
118
- end
119
-
120
- def name
121
- metadata.params[:name]
122
- end
123
-
124
- def version
125
- metadata.params[:version]
126
- end
127
-
128
- def writable?
129
- @writable
130
- end
131
-
132
- #
133
- # Is this profile is supported on the current platform of the
134
- # backend machine and the current inspec version.
135
- #
136
- # @returns [TrueClass, FalseClass]
137
- #
138
- def supported?
139
- supports_platform? && supports_runtime?
140
- end
141
-
142
- def supports_platform?
143
- if @supports_platform.nil?
144
- @supports_platform = metadata.supports_platform?(@backend)
145
- end
146
- @supports_platform
147
- end
148
-
149
- def supports_runtime?
150
- if @supports_runtime.nil?
151
- @supports_runtime = metadata.supports_runtime?
152
- end
153
- @supports_runtime
154
- end
155
-
156
- def params
157
- @params ||= load_params
158
- end
159
-
160
- def collect_tests(include_list = @controls)
161
- if !@tests_collected
162
- locked_dependencies.each(&:collect_tests)
163
-
164
- tests.each do |path, content|
165
- next if content.nil? || content.empty?
166
- abs_path = source_reader.target.abs_path(path)
167
- @runner_context.load_control_file(content, abs_path, nil)
168
- end
169
- @tests_collected = true
170
- end
171
- filter_controls(@runner_context.all_rules, include_list)
172
- end
173
-
174
- def filter_controls(controls_array, include_list)
175
- return controls_array if include_list.nil? || include_list.empty?
176
- controls_array.select do |c|
177
- id = ::Inspec::Rule.rule_id(c)
178
- include_list.include?(id)
179
- end
180
- end
181
-
182
- def load_libraries
183
- return @runner_context if @libraries_loaded
184
-
185
- locked_dependencies.each do |d|
186
- c = d.load_libraries
187
- @runner_context.add_resources(c)
188
- end
189
-
190
- libs = libraries.map do |path, content|
191
- [content, path]
192
- end
193
-
194
- @runner_context.load_libraries(libs)
195
- @libraries_loaded = true
196
- @runner_context
197
- end
198
-
199
- def to_s
200
- "Inspec::Profile<#{name}>"
201
- end
202
-
203
- # return info using uncached params
204
- def info!
205
- info(load_params.dup)
206
- end
207
-
208
- def info(res = params.dup)
209
- # add information about the controls
210
- res[:controls] = res[:controls].map do |id, rule|
211
- next if id.to_s.empty?
212
- data = rule.dup
213
- data.delete(:checks)
214
- data[:impact] ||= 0.5
215
- data[:impact] = 1.0 if data[:impact] > 1.0
216
- data[:impact] = 0.0 if data[:impact] < 0.0
217
- data[:id] = id
218
- data
219
- end.compact
220
-
221
- # resolve hash structure in groups
222
- res[:groups] = res[:groups].map do |id, group|
223
- group[:id] = id
224
- group
225
- end
226
-
227
- # add information about the required attributes
228
- res[:attributes] = res[:attributes].map(&:to_hash) unless res[:attributes].nil? || res[:attributes].empty?
229
- res[:sha256] = sha256
230
- res
231
- end
232
-
233
- # Check if the profile is internally well-structured. The logger will be
234
- # used to print information on errors and warnings which are found.
235
- #
236
- # @return [Boolean] true if no errors were found, false otherwise
237
- def check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
238
- # initial values for response object
239
- result = {
240
- summary: {
241
- valid: false,
242
- timestamp: Time.now.iso8601,
243
- location: @target,
244
- profile: nil,
245
- controls: 0,
246
- },
247
- errors: [],
248
- warnings: [],
249
- }
250
-
251
- entry = lambda { |file, line, column, control, msg|
252
- {
253
- file: file,
254
- line: line,
255
- column: column,
256
- control_id: control,
257
- msg: msg,
258
- }
259
- }
260
-
261
- warn = lambda { |file, line, column, control, msg|
262
- @logger.warn(msg)
263
- result[:warnings].push(entry.call(file, line, column, control, msg))
264
- }
265
-
266
- error = lambda { |file, line, column, control, msg|
267
- @logger.error(msg)
268
- result[:errors].push(entry.call(file, line, column, control, msg))
269
- }
270
-
271
- @logger.info "Checking profile in #{@target}"
272
- meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
273
- if meta_path =~ /metadata\.rb$/
274
- warn.call(@target, 0, 0, nil, 'The use of `metadata.rb` is deprecated. Use `inspec.yml`.')
275
- end
276
-
277
- # verify metadata
278
- m_errors, m_warnings = metadata.valid
279
- m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) }
280
- m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) }
281
- m_unsupported = metadata.unsupported
282
- m_unsupported.each { |u| warn.call(meta_path, 0, 0, nil, "doesn't support: #{u}") }
283
- @logger.info 'Metadata OK.' if m_errors.empty? && m_unsupported.empty?
284
-
285
- # extract profile name
286
- result[:summary][:profile] = metadata.params[:name]
287
-
288
- # check if the profile is using the old test directory instead of the
289
- # new controls directory
290
- if @source_reader.tests.keys.any? { |x| x =~ %r{^test/$} }
291
- warn.call(@target, 0, 0, nil, 'Profile uses deprecated `test` directory, rename it to `controls`.')
292
- end
293
-
294
- count = controls_count
295
- result[:summary][:controls] = count
296
- if count == 0
297
- warn.call(nil, nil, nil, nil, 'No controls or tests were defined.')
298
- else
299
- @logger.info("Found #{count} controls.")
300
- end
301
-
302
- # iterate over hash of groups
303
- params[:controls].each { |id, control|
304
- sfile = control[:source_location][:ref]
305
- sline = control[:source_location][:line]
306
- error.call(sfile, sline, nil, id, 'Avoid controls with empty IDs') if id.nil? or id.empty?
307
- next if id.start_with? '(generated '
308
- warn.call(sfile, sline, nil, id, "Control #{id} has no title") if control[:title].to_s.empty?
309
- warn.call(sfile, sline, nil, id, "Control #{id} has no description") if control[:desc].to_s.empty?
310
- warn.call(sfile, sline, nil, id, "Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0
311
- warn.call(sfile, sline, nil, id, "Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0
312
- warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? or control[:checks].empty?
313
- }
314
-
315
- # profile is valid if we could not find any error
316
- result[:summary][:valid] = result[:errors].empty?
317
-
318
- @logger.info 'Control definitions OK.' if result[:warnings].empty?
319
- result
320
- end
321
-
322
- def controls_count
323
- params[:controls].values.length
324
- end
325
-
326
- # generates a archive of a folder profile
327
- # assumes that the profile was checked before
328
- def archive(opts)
329
- # check if file exists otherwise overwrite the archive
330
- dst = archive_name(opts)
331
- if dst.exist? && !opts[:overwrite]
332
- @logger.info "Archive #{dst} exists already. Use --overwrite."
333
- return false
334
- end
335
-
336
- # remove existing archive
337
- File.delete(dst) if dst.exist?
338
- @logger.info "Generate archive #{dst}."
339
-
340
- # filter files that should not be part of the profile
341
- # TODO ignore all .files, but add the files to debug output
342
-
343
- # display all files that will be part of the archive
344
- @logger.debug 'Add the following files to archive:'
345
- files.each { |f| @logger.debug ' ' + f }
346
-
347
- if opts[:zip]
348
- # generate zip archive
349
- require 'inspec/archive/zip'
350
- zag = Inspec::Archive::ZipArchiveGenerator.new
351
- zag.archive(root_path, files, dst)
352
- else
353
- # generate tar archive
354
- require 'inspec/archive/tar'
355
- tag = Inspec::Archive::TarArchiveGenerator.new
356
- tag.archive(root_path, files, dst)
357
- end
358
-
359
- @logger.info 'Finished archive generation.'
360
- true
361
- end
362
-
363
- def locked_dependencies
364
- @locked_dependencies ||= load_dependencies
365
- end
366
-
367
- def lockfile_exists?
368
- @source_reader.target.files.include?('inspec.lock')
369
- end
370
-
371
- def lockfile_path
372
- File.join(cwd, 'inspec.lock')
373
- end
374
-
375
- def root_path
376
- @source_reader.target.prefix
377
- end
378
-
379
- def files
380
- @source_reader.target.files
381
- end
382
-
383
- #
384
- # TODO(ssd): Relative path handling really needs to be carefully
385
- # thought through, especially with respect to relative paths in
386
- # tarballs.
387
- #
388
- def cwd
389
- @target.is_a?(String) && File.directory?(@target) ? @target : './'
390
- end
391
-
392
- def lockfile
393
- @lockfile ||= if lockfile_exists?
394
- Inspec::Lockfile.from_content(@source_reader.target.read('inspec.lock'))
395
- else
396
- generate_lockfile
397
- end
398
- end
399
-
400
- #
401
- # Generate an in-memory lockfile. This won't render the lock file
402
- # to disk, it must be explicitly written to disk by the caller.
403
- #
404
- # @param vendor_path [String] Path to the on-disk vendor dir
405
- # @return [Inspec::Lockfile]
406
- #
407
- def generate_lockfile
408
- res = Inspec::DependencySet.new(cwd, @cache, nil, @backend)
409
- res.vendor(metadata.dependencies)
410
- Inspec::Lockfile.from_dependency_set(res)
411
- end
412
-
413
- def load_dependencies
414
- Inspec::DependencySet.from_lockfile(lockfile, cwd, @cache, @backend, { attributes: @attr_values })
415
- end
416
-
417
- # Calculate this profile's SHA256 checksum. Includes metadata, dependencies,
418
- # libraries, data files, and controls.
419
- #
420
- # @return [Type] description of returned object
421
- def sha256
422
- # get all dependency checksums
423
- deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }]
424
-
425
- res = OpenSSL::Digest::SHA256.new
426
- files = source_reader.tests.to_a + source_reader.libraries.to_a +
427
- source_reader.data_files.to_a +
428
- [['inspec.yml', source_reader.metadata.content]] +
429
- [['inspec.lock.deps', YAML.dump(deps)]]
430
-
431
- files.sort_by { |a| a[0] }
432
- .map { |f| res << f[0] << "\0" << f[1] << "\0" }
433
-
434
- res.digest.unpack('H*')[0]
435
- end
436
-
437
- private
438
-
439
- # Create an archive name for this profile and an additional options
440
- # configuration. Either use :output or generate the name from metadata.
441
- #
442
- # @param [Hash] configuration options
443
- # @return [Pathname] path for the archive
444
- def archive_name(opts)
445
- if (name = opts[:output])
446
- return Pathname.new(name)
447
- end
448
-
449
- name = params[:name] ||
450
- raise('Cannot create an archive without a profile name! Please '\
451
- 'specify the name in metadata or use --output to create the archive.')
452
- version = params[:version] ||
453
- raise('Cannot create an archive without a profile version! Please '\
454
- 'specify the version in metadata or use --output to create the archive.')
455
- ext = opts[:zip] ? 'zip' : 'tar.gz'
456
- slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_')
457
- Pathname.new(Dir.pwd).join("#{slug}-#{version}.#{ext}")
458
- end
459
-
460
- def load_params
461
- params = @source_reader.metadata.params
462
- params[:name] = @profile_id unless @profile_id.nil?
463
- load_checks_params(params)
464
- @profile_id ||= params[:name]
465
- params
466
- end
467
-
468
- def load_checks_params(params)
469
- load_libraries
470
- tests = collect_tests
471
- params[:controls] = controls = {}
472
- params[:groups] = groups = {}
473
- prefix = @source_reader.target.prefix || ''
474
- tests.each do |rule|
475
- next if rule.nil?
476
- f = load_rule_filepath(prefix, rule)
477
- load_rule(rule, f, controls, groups)
478
- end
479
- params[:attributes] = @runner_context.attributes
480
- params
481
- end
482
-
483
- def load_rule_filepath(prefix, rule)
484
- file = rule.instance_variable_get(:@__file)
485
- file = file[prefix.length..-1] if file.start_with?(prefix)
486
- file
487
- end
488
-
489
- def load_rule(rule, file, controls, groups)
490
- id = Inspec::Rule.rule_id(rule)
491
- location = rule.instance_variable_get(:@__source_location)
492
- controls[id] = {
493
- title: rule.title,
494
- desc: rule.desc,
495
- impact: rule.impact,
496
- refs: rule.ref,
497
- tags: rule.tag,
498
- checks: Inspec::Rule.checks(rule),
499
- code: Inspec::MethodSource.code_at(location, source_reader),
500
- source_location: location,
501
- }
502
-
503
- groups[file] ||= {
504
- title: rule.instance_variable_get(:@__group_title),
505
- controls: [],
506
- }
507
- groups[file][:controls].push(id)
508
- end
509
- end
510
- end
1
+ # encoding: utf-8
2
+ # Copyright 2015 Dominik Richter
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+
6
+ require 'forwardable'
7
+ require 'openssl'
8
+ require 'inspec/polyfill'
9
+ require 'inspec/cached_fetcher'
10
+ require 'inspec/file_provider'
11
+ require 'inspec/source_reader'
12
+ require 'inspec/metadata'
13
+ require 'inspec/backend'
14
+ require 'inspec/rule'
15
+ require 'inspec/log'
16
+ require 'inspec/profile_context'
17
+ require 'inspec/runtime_profile'
18
+ require 'inspec/method_source'
19
+ require 'inspec/dependencies/cache'
20
+ require 'inspec/dependencies/lockfile'
21
+ require 'inspec/dependencies/dependency_set'
22
+
23
+ module Inspec
24
+ class Profile
25
+ extend Forwardable
26
+
27
+ def self.resolve_target(target, cache)
28
+ Inspec::Log.debug "Resolve #{target} into cache #{cache.path}"
29
+ Inspec::CachedFetcher.new(target, cache)
30
+ end
31
+
32
+ # Check if the profile contains a vendored cache, move content into global cache
33
+ # TODO: use relative file provider
34
+ # TODO: use source reader for Cache as well
35
+ def self.copy_deps_into_cache(file_provider, opts)
36
+ # filter content
37
+ cache = file_provider.files.find_all do |entry|
38
+ entry.start_with?('vendor')
39
+ end
40
+ content = Hash[cache.map { |x| [x, file_provider.binread(x)] }]
41
+ keys = content.keys
42
+ keys.each do |key|
43
+ next if content[key].nil?
44
+ # remove prefix
45
+ rel = Pathname.new(key).relative_path_from(Pathname.new('vendor')).to_s
46
+ tar = Pathname.new(opts[:vendor_cache].path).join(rel)
47
+
48
+ FileUtils.mkdir_p tar.dirname.to_s
49
+ Inspec::Log.debug "Copy #{tar} to cache directory"
50
+ File.binwrite(tar.to_s, content[key])
51
+ end
52
+ end
53
+
54
+ def self.for_path(path, opts)
55
+ file_provider = FileProvider.for_path(path)
56
+ rp = file_provider.relative_provider
57
+
58
+ # copy embedded dependecies into global cache
59
+ copy_deps_into_cache(rp, opts) unless opts[:vendor_cache].nil?
60
+
61
+ reader = Inspec::SourceReader.resolve(rp)
62
+ if reader.nil?
63
+ raise("Don't understand inspec profile in #{path}, it " \
64
+ "doesn't look like a supported profile structure.")
65
+ end
66
+ new(reader, opts)
67
+ end
68
+
69
+ def self.for_fetcher(fetcher, opts)
70
+ opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
71
+ path, writable = fetcher.fetch
72
+ for_path(path, opts.merge(target: fetcher.target, writable: writable))
73
+ end
74
+
75
+ def self.for_target(target, opts = {})
76
+ opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
77
+ fetcher = resolve_target(target, opts[:vendor_cache])
78
+ for_fetcher(fetcher, opts)
79
+ end
80
+
81
+ attr_reader :source_reader, :backend, :runner_context, :check_mode
82
+ def_delegator :@source_reader, :tests
83
+ def_delegator :@source_reader, :libraries
84
+ def_delegator :@source_reader, :metadata
85
+
86
+ # rubocop:disable Metrics/AbcSize
87
+ def initialize(source_reader, options = {})
88
+ @source_reader = source_reader
89
+ @target = options[:target]
90
+ @logger = options[:logger] || Logger.new(nil)
91
+ @locked_dependencies = options[:dependencies]
92
+ @controls = options[:controls] || []
93
+ @writable = options[:writable] || false
94
+ @profile_id = options[:id]
95
+ @cache = options[:vendor_cache] || Cache.new
96
+ @attr_values = options[:attributes]
97
+ @tests_collected = false
98
+ @libraries_loaded = false
99
+ @check_mode = options[:check_mode] || false
100
+ Metadata.finalize(@source_reader.metadata, @profile_id, options)
101
+
102
+ # if a backend has already been created, clone it so each profile has its own unique backend object
103
+ # otherwise, create a new backend object
104
+ #
105
+ # This is necessary since we store the RuntimeProfile on the backend object. If a user runs `inspec exec`
106
+ # with multiple profiles, only the RuntimeProfile for the last-loaded profile will be available if
107
+ # we share the backend between profiles.
108
+ #
109
+ # This will cause issues if a profile attempts to load a file via `inspec.profile.file`
110
+ train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646
111
+ @backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
112
+ @runtime_profile = RuntimeProfile.new(self)
113
+ @backend.profile = @runtime_profile
114
+
115
+ @runner_context =
116
+ options[:profile_context] ||
117
+ Inspec::ProfileContext.for_profile(self, @backend, @attr_values)
118
+ end
119
+
120
+ def name
121
+ metadata.params[:name]
122
+ end
123
+
124
+ def version
125
+ metadata.params[:version]
126
+ end
127
+
128
+ def writable?
129
+ @writable
130
+ end
131
+
132
+ #
133
+ # Is this profile is supported on the current platform of the
134
+ # backend machine and the current inspec version.
135
+ #
136
+ # @returns [TrueClass, FalseClass]
137
+ #
138
+ def supported?
139
+ supports_platform? && supports_runtime?
140
+ end
141
+
142
+ def supports_platform?
143
+ if @supports_platform.nil?
144
+ @supports_platform = metadata.supports_platform?(@backend)
145
+ end
146
+ @supports_platform
147
+ end
148
+
149
+ def supports_runtime?
150
+ if @supports_runtime.nil?
151
+ @supports_runtime = metadata.supports_runtime?
152
+ end
153
+ @supports_runtime
154
+ end
155
+
156
+ def params
157
+ @params ||= load_params
158
+ end
159
+
160
+ def collect_tests(include_list = @controls)
161
+ if !@tests_collected
162
+ locked_dependencies.each(&:collect_tests)
163
+
164
+ tests.each do |path, content|
165
+ next if content.nil? || content.empty?
166
+ abs_path = source_reader.target.abs_path(path)
167
+ @runner_context.load_control_file(content, abs_path, nil)
168
+ end
169
+ @tests_collected = true
170
+ end
171
+ filter_controls(@runner_context.all_rules, include_list)
172
+ end
173
+
174
+ def filter_controls(controls_array, include_list)
175
+ return controls_array if include_list.nil? || include_list.empty?
176
+ controls_array.select do |c|
177
+ id = ::Inspec::Rule.rule_id(c)
178
+ include_list.include?(id)
179
+ end
180
+ end
181
+
182
+ def load_libraries
183
+ return @runner_context if @libraries_loaded
184
+
185
+ locked_dependencies.each do |d|
186
+ c = d.load_libraries
187
+ @runner_context.add_resources(c)
188
+ end
189
+
190
+ libs = libraries.map do |path, content|
191
+ [content, path]
192
+ end
193
+
194
+ @runner_context.load_libraries(libs)
195
+ @libraries_loaded = true
196
+ @runner_context
197
+ end
198
+
199
+ def to_s
200
+ "Inspec::Profile<#{name}>"
201
+ end
202
+
203
+ # return info using uncached params
204
+ def info!
205
+ info(load_params.dup)
206
+ end
207
+
208
+ def info(res = params.dup)
209
+ # add information about the controls
210
+ res[:controls] = res[:controls].map do |id, rule|
211
+ next if id.to_s.empty?
212
+ data = rule.dup
213
+ data.delete(:checks)
214
+ data[:impact] ||= 0.5
215
+ data[:impact] = 1.0 if data[:impact] > 1.0
216
+ data[:impact] = 0.0 if data[:impact] < 0.0
217
+ data[:id] = id
218
+ data
219
+ end.compact
220
+
221
+ # resolve hash structure in groups
222
+ res[:groups] = res[:groups].map do |id, group|
223
+ group[:id] = id
224
+ group
225
+ end
226
+
227
+ # add information about the required attributes
228
+ res[:attributes] = res[:attributes].map(&:to_hash) unless res[:attributes].nil? || res[:attributes].empty?
229
+ res[:sha256] = sha256
230
+ res
231
+ end
232
+
233
+ # Check if the profile is internally well-structured. The logger will be
234
+ # used to print information on errors and warnings which are found.
235
+ #
236
+ # @return [Boolean] true if no errors were found, false otherwise
237
+ def check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
238
+ # initial values for response object
239
+ result = {
240
+ summary: {
241
+ valid: false,
242
+ timestamp: Time.now.iso8601,
243
+ location: @target,
244
+ profile: nil,
245
+ controls: 0,
246
+ },
247
+ errors: [],
248
+ warnings: [],
249
+ }
250
+
251
+ entry = lambda { |file, line, column, control, msg|
252
+ {
253
+ file: file,
254
+ line: line,
255
+ column: column,
256
+ control_id: control,
257
+ msg: msg,
258
+ }
259
+ }
260
+
261
+ warn = lambda { |file, line, column, control, msg|
262
+ @logger.warn(msg)
263
+ result[:warnings].push(entry.call(file, line, column, control, msg))
264
+ }
265
+
266
+ error = lambda { |file, line, column, control, msg|
267
+ @logger.error(msg)
268
+ result[:errors].push(entry.call(file, line, column, control, msg))
269
+ }
270
+
271
+ @logger.info "Checking profile in #{@target}"
272
+ meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
273
+ if meta_path =~ /metadata\.rb$/
274
+ warn.call(@target, 0, 0, nil, 'The use of `metadata.rb` is deprecated. Use `inspec.yml`.')
275
+ end
276
+
277
+ # verify metadata
278
+ m_errors, m_warnings = metadata.valid
279
+ m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) }
280
+ m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) }
281
+ m_unsupported = metadata.unsupported
282
+ m_unsupported.each { |u| warn.call(meta_path, 0, 0, nil, "doesn't support: #{u}") }
283
+ @logger.info 'Metadata OK.' if m_errors.empty? && m_unsupported.empty?
284
+
285
+ # extract profile name
286
+ result[:summary][:profile] = metadata.params[:name]
287
+
288
+ # check if the profile is using the old test directory instead of the
289
+ # new controls directory
290
+ if @source_reader.tests.keys.any? { |x| x =~ %r{^test/$} }
291
+ warn.call(@target, 0, 0, nil, 'Profile uses deprecated `test` directory, rename it to `controls`.')
292
+ end
293
+
294
+ count = controls_count
295
+ result[:summary][:controls] = count
296
+ if count == 0
297
+ warn.call(nil, nil, nil, nil, 'No controls or tests were defined.')
298
+ else
299
+ @logger.info("Found #{count} controls.")
300
+ end
301
+
302
+ # iterate over hash of groups
303
+ params[:controls].each { |id, control|
304
+ sfile = control[:source_location][:ref]
305
+ sline = control[:source_location][:line]
306
+ error.call(sfile, sline, nil, id, 'Avoid controls with empty IDs') if id.nil? or id.empty?
307
+ next if id.start_with? '(generated '
308
+ warn.call(sfile, sline, nil, id, "Control #{id} has no title") if control[:title].to_s.empty?
309
+ warn.call(sfile, sline, nil, id, "Control #{id} has no description") if control[:desc].to_s.empty?
310
+ warn.call(sfile, sline, nil, id, "Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0
311
+ warn.call(sfile, sline, nil, id, "Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0
312
+ warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? or control[:checks].empty?
313
+ }
314
+
315
+ # profile is valid if we could not find any error
316
+ result[:summary][:valid] = result[:errors].empty?
317
+
318
+ @logger.info 'Control definitions OK.' if result[:warnings].empty?
319
+ result
320
+ end
321
+
322
+ def controls_count
323
+ params[:controls].values.length
324
+ end
325
+
326
+ # generates a archive of a folder profile
327
+ # assumes that the profile was checked before
328
+ def archive(opts)
329
+ # check if file exists otherwise overwrite the archive
330
+ dst = archive_name(opts)
331
+ if dst.exist? && !opts[:overwrite]
332
+ @logger.info "Archive #{dst} exists already. Use --overwrite."
333
+ return false
334
+ end
335
+
336
+ # remove existing archive
337
+ File.delete(dst) if dst.exist?
338
+ @logger.info "Generate archive #{dst}."
339
+
340
+ # filter files that should not be part of the profile
341
+ # TODO ignore all .files, but add the files to debug output
342
+
343
+ # display all files that will be part of the archive
344
+ @logger.debug 'Add the following files to archive:'
345
+ files.each { |f| @logger.debug ' ' + f }
346
+
347
+ if opts[:zip]
348
+ # generate zip archive
349
+ require 'inspec/archive/zip'
350
+ zag = Inspec::Archive::ZipArchiveGenerator.new
351
+ zag.archive(root_path, files, dst)
352
+ else
353
+ # generate tar archive
354
+ require 'inspec/archive/tar'
355
+ tag = Inspec::Archive::TarArchiveGenerator.new
356
+ tag.archive(root_path, files, dst)
357
+ end
358
+
359
+ @logger.info 'Finished archive generation.'
360
+ true
361
+ end
362
+
363
+ def locked_dependencies
364
+ @locked_dependencies ||= load_dependencies
365
+ end
366
+
367
+ def lockfile_exists?
368
+ @source_reader.target.files.include?('inspec.lock')
369
+ end
370
+
371
+ def lockfile_path
372
+ File.join(cwd, 'inspec.lock')
373
+ end
374
+
375
+ def root_path
376
+ @source_reader.target.prefix
377
+ end
378
+
379
+ def files
380
+ @source_reader.target.files
381
+ end
382
+
383
+ #
384
+ # TODO(ssd): Relative path handling really needs to be carefully
385
+ # thought through, especially with respect to relative paths in
386
+ # tarballs.
387
+ #
388
+ def cwd
389
+ @target.is_a?(String) && File.directory?(@target) ? @target : './'
390
+ end
391
+
392
+ def lockfile
393
+ @lockfile ||= if lockfile_exists?
394
+ Inspec::Lockfile.from_content(@source_reader.target.read('inspec.lock'))
395
+ else
396
+ generate_lockfile
397
+ end
398
+ end
399
+
400
+ #
401
+ # Generate an in-memory lockfile. This won't render the lock file
402
+ # to disk, it must be explicitly written to disk by the caller.
403
+ #
404
+ # @param vendor_path [String] Path to the on-disk vendor dir
405
+ # @return [Inspec::Lockfile]
406
+ #
407
+ def generate_lockfile
408
+ res = Inspec::DependencySet.new(cwd, @cache, nil, @backend)
409
+ res.vendor(metadata.dependencies)
410
+ Inspec::Lockfile.from_dependency_set(res)
411
+ end
412
+
413
+ def load_dependencies
414
+ Inspec::DependencySet.from_lockfile(lockfile, cwd, @cache, @backend, { attributes: @attr_values })
415
+ end
416
+
417
+ # Calculate this profile's SHA256 checksum. Includes metadata, dependencies,
418
+ # libraries, data files, and controls.
419
+ #
420
+ # @return [Type] description of returned object
421
+ def sha256
422
+ # get all dependency checksums
423
+ deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }]
424
+
425
+ res = OpenSSL::Digest::SHA256.new
426
+ files = source_reader.tests.to_a + source_reader.libraries.to_a +
427
+ source_reader.data_files.to_a +
428
+ [['inspec.yml', source_reader.metadata.content]] +
429
+ [['inspec.lock.deps', YAML.dump(deps)]]
430
+
431
+ files.sort_by { |a| a[0] }
432
+ .map { |f| res << f[0] << "\0" << f[1] << "\0" }
433
+
434
+ res.digest.unpack('H*')[0]
435
+ end
436
+
437
+ private
438
+
439
+ # Create an archive name for this profile and an additional options
440
+ # configuration. Either use :output or generate the name from metadata.
441
+ #
442
+ # @param [Hash] configuration options
443
+ # @return [Pathname] path for the archive
444
+ def archive_name(opts)
445
+ if (name = opts[:output])
446
+ return Pathname.new(name)
447
+ end
448
+
449
+ name = params[:name] ||
450
+ raise('Cannot create an archive without a profile name! Please '\
451
+ 'specify the name in metadata or use --output to create the archive.')
452
+ version = params[:version] ||
453
+ raise('Cannot create an archive without a profile version! Please '\
454
+ 'specify the version in metadata or use --output to create the archive.')
455
+ ext = opts[:zip] ? 'zip' : 'tar.gz'
456
+ slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_')
457
+ Pathname.new(Dir.pwd).join("#{slug}-#{version}.#{ext}")
458
+ end
459
+
460
+ def load_params
461
+ params = @source_reader.metadata.params
462
+ params[:name] = @profile_id unless @profile_id.nil?
463
+ load_checks_params(params)
464
+ @profile_id ||= params[:name]
465
+ params
466
+ end
467
+
468
+ def load_checks_params(params)
469
+ load_libraries
470
+ tests = collect_tests
471
+ params[:controls] = controls = {}
472
+ params[:groups] = groups = {}
473
+ prefix = @source_reader.target.prefix || ''
474
+ tests.each do |rule|
475
+ next if rule.nil?
476
+ f = load_rule_filepath(prefix, rule)
477
+ load_rule(rule, f, controls, groups)
478
+ end
479
+ params[:attributes] = @runner_context.attributes
480
+ params
481
+ end
482
+
483
+ def load_rule_filepath(prefix, rule)
484
+ file = rule.instance_variable_get(:@__file)
485
+ file = file[prefix.length..-1] if file.start_with?(prefix)
486
+ file
487
+ end
488
+
489
+ def load_rule(rule, file, controls, groups)
490
+ id = Inspec::Rule.rule_id(rule)
491
+ location = rule.instance_variable_get(:@__source_location)
492
+ controls[id] = {
493
+ title: rule.title,
494
+ desc: rule.desc,
495
+ impact: rule.impact,
496
+ refs: rule.ref,
497
+ tags: rule.tag,
498
+ checks: Inspec::Rule.checks(rule),
499
+ code: Inspec::MethodSource.code_at(location, source_reader),
500
+ source_location: location,
501
+ }
502
+
503
+ groups[file] ||= {
504
+ title: rule.instance_variable_get(:@__group_title),
505
+ controls: [],
506
+ }
507
+ groups[file][:controls].push(id)
508
+ end
509
+ end
510
+ end