inspec-core 2.1.67

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 (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3136 -0
  3. data/Gemfile +56 -0
  4. data/LICENSE +14 -0
  5. data/MAINTAINERS.md +33 -0
  6. data/MAINTAINERS.toml +52 -0
  7. data/README.md +453 -0
  8. data/bin/inspec +12 -0
  9. data/docs/.gitignore +2 -0
  10. data/docs/README.md +40 -0
  11. data/docs/dev/control-eval.md +62 -0
  12. data/docs/dsl_inspec.md +258 -0
  13. data/docs/dsl_resource.md +100 -0
  14. data/docs/glossary.md +99 -0
  15. data/docs/habitat.md +192 -0
  16. data/docs/inspec_and_friends.md +114 -0
  17. data/docs/matchers.md +169 -0
  18. data/docs/migration.md +293 -0
  19. data/docs/platforms.md +119 -0
  20. data/docs/plugin_kitchen_inspec.md +50 -0
  21. data/docs/profiles.md +378 -0
  22. data/docs/reporters.md +105 -0
  23. data/docs/resources/aide_conf.md.erb +76 -0
  24. data/docs/resources/apache.md.erb +67 -0
  25. data/docs/resources/apache_conf.md.erb +68 -0
  26. data/docs/resources/apt.md.erb +71 -0
  27. data/docs/resources/audit_policy.md.erb +47 -0
  28. data/docs/resources/auditd.md.erb +79 -0
  29. data/docs/resources/auditd_conf.md.erb +68 -0
  30. data/docs/resources/bash.md.erb +75 -0
  31. data/docs/resources/bond.md.erb +90 -0
  32. data/docs/resources/bridge.md.erb +57 -0
  33. data/docs/resources/bsd_service.md.erb +67 -0
  34. data/docs/resources/chocolatey_package.md.erb +58 -0
  35. data/docs/resources/command.md.erb +138 -0
  36. data/docs/resources/cpan.md.erb +79 -0
  37. data/docs/resources/cran.md.erb +64 -0
  38. data/docs/resources/crontab.md.erb +89 -0
  39. data/docs/resources/csv.md.erb +54 -0
  40. data/docs/resources/dh_params.md.erb +205 -0
  41. data/docs/resources/directory.md.erb +30 -0
  42. data/docs/resources/docker.md.erb +219 -0
  43. data/docs/resources/docker_container.md.erb +103 -0
  44. data/docs/resources/docker_image.md.erb +94 -0
  45. data/docs/resources/docker_service.md.erb +114 -0
  46. data/docs/resources/elasticsearch.md.erb +242 -0
  47. data/docs/resources/etc_fstab.md.erb +125 -0
  48. data/docs/resources/etc_group.md.erb +75 -0
  49. data/docs/resources/etc_hosts.md.erb +78 -0
  50. data/docs/resources/etc_hosts_allow.md.erb +74 -0
  51. data/docs/resources/etc_hosts_deny.md.erb +74 -0
  52. data/docs/resources/file.md.erb +526 -0
  53. data/docs/resources/filesystem.md.erb +41 -0
  54. data/docs/resources/firewalld.md.erb +107 -0
  55. data/docs/resources/gem.md.erb +79 -0
  56. data/docs/resources/group.md.erb +61 -0
  57. data/docs/resources/grub_conf.md.erb +101 -0
  58. data/docs/resources/host.md.erb +86 -0
  59. data/docs/resources/http.md.erb +197 -0
  60. data/docs/resources/iis_app.md.erb +122 -0
  61. data/docs/resources/iis_site.md.erb +135 -0
  62. data/docs/resources/inetd_conf.md.erb +94 -0
  63. data/docs/resources/ini.md.erb +76 -0
  64. data/docs/resources/interface.md.erb +58 -0
  65. data/docs/resources/iptables.md.erb +64 -0
  66. data/docs/resources/json.md.erb +63 -0
  67. data/docs/resources/kernel_module.md.erb +120 -0
  68. data/docs/resources/kernel_parameter.md.erb +53 -0
  69. data/docs/resources/key_rsa.md.erb +85 -0
  70. data/docs/resources/launchd_service.md.erb +57 -0
  71. data/docs/resources/limits_conf.md.erb +75 -0
  72. data/docs/resources/login_defs.md.erb +71 -0
  73. data/docs/resources/mount.md.erb +69 -0
  74. data/docs/resources/mssql_session.md.erb +60 -0
  75. data/docs/resources/mysql_conf.md.erb +99 -0
  76. data/docs/resources/mysql_session.md.erb +74 -0
  77. data/docs/resources/nginx.md.erb +79 -0
  78. data/docs/resources/nginx_conf.md.erb +138 -0
  79. data/docs/resources/npm.md.erb +60 -0
  80. data/docs/resources/ntp_conf.md.erb +60 -0
  81. data/docs/resources/oneget.md.erb +53 -0
  82. data/docs/resources/oracledb_session.md.erb +52 -0
  83. data/docs/resources/os.md.erb +141 -0
  84. data/docs/resources/os_env.md.erb +91 -0
  85. data/docs/resources/package.md.erb +120 -0
  86. data/docs/resources/packages.md.erb +67 -0
  87. data/docs/resources/parse_config.md.erb +103 -0
  88. data/docs/resources/parse_config_file.md.erb +138 -0
  89. data/docs/resources/passwd.md.erb +141 -0
  90. data/docs/resources/pip.md.erb +67 -0
  91. data/docs/resources/port.md.erb +137 -0
  92. data/docs/resources/postgres_conf.md.erb +79 -0
  93. data/docs/resources/postgres_hba_conf.md.erb +93 -0
  94. data/docs/resources/postgres_ident_conf.md.erb +76 -0
  95. data/docs/resources/postgres_session.md.erb +69 -0
  96. data/docs/resources/powershell.md.erb +102 -0
  97. data/docs/resources/processes.md.erb +109 -0
  98. data/docs/resources/rabbitmq_config.md.erb +41 -0
  99. data/docs/resources/registry_key.md.erb +158 -0
  100. data/docs/resources/runit_service.md.erb +57 -0
  101. data/docs/resources/security_policy.md.erb +47 -0
  102. data/docs/resources/service.md.erb +121 -0
  103. data/docs/resources/shadow.md.erb +146 -0
  104. data/docs/resources/ssh_config.md.erb +73 -0
  105. data/docs/resources/sshd_config.md.erb +83 -0
  106. data/docs/resources/ssl.md.erb +119 -0
  107. data/docs/resources/sys_info.md.erb +42 -0
  108. data/docs/resources/systemd_service.md.erb +57 -0
  109. data/docs/resources/sysv_service.md.erb +57 -0
  110. data/docs/resources/upstart_service.md.erb +57 -0
  111. data/docs/resources/user.md.erb +140 -0
  112. data/docs/resources/users.md.erb +127 -0
  113. data/docs/resources/vbscript.md.erb +55 -0
  114. data/docs/resources/virtualization.md.erb +57 -0
  115. data/docs/resources/windows_feature.md.erb +47 -0
  116. data/docs/resources/windows_hotfix.md.erb +53 -0
  117. data/docs/resources/windows_task.md.erb +95 -0
  118. data/docs/resources/wmi.md.erb +81 -0
  119. data/docs/resources/x509_certificate.md.erb +151 -0
  120. data/docs/resources/xinetd_conf.md.erb +156 -0
  121. data/docs/resources/xml.md.erb +85 -0
  122. data/docs/resources/yaml.md.erb +69 -0
  123. data/docs/resources/yum.md.erb +98 -0
  124. data/docs/resources/zfs_dataset.md.erb +53 -0
  125. data/docs/resources/zfs_pool.md.erb +47 -0
  126. data/docs/ruby_usage.md +203 -0
  127. data/docs/shared/matcher_be.md.erb +1 -0
  128. data/docs/shared/matcher_cmp.md.erb +43 -0
  129. data/docs/shared/matcher_eq.md.erb +3 -0
  130. data/docs/shared/matcher_include.md.erb +1 -0
  131. data/docs/shared/matcher_match.md.erb +1 -0
  132. data/docs/shell.md +217 -0
  133. data/examples/README.md +8 -0
  134. data/examples/inheritance/README.md +65 -0
  135. data/examples/inheritance/controls/example.rb +14 -0
  136. data/examples/inheritance/inspec.yml +15 -0
  137. data/examples/kitchen-ansible/.kitchen.yml +25 -0
  138. data/examples/kitchen-ansible/Gemfile +19 -0
  139. data/examples/kitchen-ansible/README.md +53 -0
  140. data/examples/kitchen-ansible/files/nginx.repo +6 -0
  141. data/examples/kitchen-ansible/tasks/main.yml +16 -0
  142. data/examples/kitchen-ansible/test/integration/default/default.yml +5 -0
  143. data/examples/kitchen-ansible/test/integration/default/web_spec.rb +28 -0
  144. data/examples/kitchen-chef/.kitchen.yml +20 -0
  145. data/examples/kitchen-chef/Berksfile +3 -0
  146. data/examples/kitchen-chef/Gemfile +19 -0
  147. data/examples/kitchen-chef/README.md +27 -0
  148. data/examples/kitchen-chef/metadata.rb +7 -0
  149. data/examples/kitchen-chef/recipes/default.rb +6 -0
  150. data/examples/kitchen-chef/recipes/nginx.rb +30 -0
  151. data/examples/kitchen-chef/test/integration/default/web_spec.rb +28 -0
  152. data/examples/kitchen-puppet/.kitchen.yml +23 -0
  153. data/examples/kitchen-puppet/Gemfile +20 -0
  154. data/examples/kitchen-puppet/Puppetfile +25 -0
  155. data/examples/kitchen-puppet/README.md +53 -0
  156. data/examples/kitchen-puppet/manifests/site.pp +33 -0
  157. data/examples/kitchen-puppet/metadata.json +11 -0
  158. data/examples/kitchen-puppet/modules/.gitkeep +0 -0
  159. data/examples/kitchen-puppet/test/integration/default/web_spec.rb +28 -0
  160. data/examples/meta-profile/README.md +37 -0
  161. data/examples/meta-profile/controls/example.rb +13 -0
  162. data/examples/meta-profile/inspec.yml +13 -0
  163. data/examples/profile-attribute.yml +2 -0
  164. data/examples/profile-attribute/README.md +14 -0
  165. data/examples/profile-attribute/controls/example.rb +11 -0
  166. data/examples/profile-attribute/inspec.yml +8 -0
  167. data/examples/profile-sensitive/README.md +29 -0
  168. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
  169. data/examples/profile-sensitive/controls/sensitive.rb +9 -0
  170. data/examples/profile-sensitive/inspec.yml +8 -0
  171. data/examples/profile/README.md +48 -0
  172. data/examples/profile/controls/example.rb +23 -0
  173. data/examples/profile/controls/gordon.rb +36 -0
  174. data/examples/profile/controls/meta.rb +34 -0
  175. data/examples/profile/inspec.yml +10 -0
  176. data/examples/profile/libraries/gordon_config.rb +59 -0
  177. data/inspec-core.gemspec +43 -0
  178. data/lib/bundles/README.md +3 -0
  179. data/lib/bundles/inspec-artifact.rb +7 -0
  180. data/lib/bundles/inspec-artifact/README.md +1 -0
  181. data/lib/bundles/inspec-artifact/cli.rb +277 -0
  182. data/lib/bundles/inspec-compliance.rb +16 -0
  183. data/lib/bundles/inspec-compliance/.kitchen.yml +20 -0
  184. data/lib/bundles/inspec-compliance/README.md +193 -0
  185. data/lib/bundles/inspec-compliance/api.rb +360 -0
  186. data/lib/bundles/inspec-compliance/api/login.rb +193 -0
  187. data/lib/bundles/inspec-compliance/bootstrap.sh +41 -0
  188. data/lib/bundles/inspec-compliance/cli.rb +260 -0
  189. data/lib/bundles/inspec-compliance/configuration.rb +103 -0
  190. data/lib/bundles/inspec-compliance/http.rb +125 -0
  191. data/lib/bundles/inspec-compliance/images/cc-token.png +0 -0
  192. data/lib/bundles/inspec-compliance/support.rb +36 -0
  193. data/lib/bundles/inspec-compliance/target.rb +106 -0
  194. data/lib/bundles/inspec-compliance/test/integration/default/cli.rb +93 -0
  195. data/lib/bundles/inspec-habitat.rb +12 -0
  196. data/lib/bundles/inspec-habitat/cli.rb +36 -0
  197. data/lib/bundles/inspec-habitat/log.rb +10 -0
  198. data/lib/bundles/inspec-habitat/profile.rb +391 -0
  199. data/lib/bundles/inspec-init.rb +8 -0
  200. data/lib/bundles/inspec-init/README.md +31 -0
  201. data/lib/bundles/inspec-init/cli.rb +97 -0
  202. data/lib/bundles/inspec-init/templates/profile/README.md +3 -0
  203. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +19 -0
  204. data/lib/bundles/inspec-init/templates/profile/inspec.yml +8 -0
  205. data/lib/bundles/inspec-init/templates/profile/libraries/.gitkeep +0 -0
  206. data/lib/bundles/inspec-supermarket.rb +13 -0
  207. data/lib/bundles/inspec-supermarket/README.md +45 -0
  208. data/lib/bundles/inspec-supermarket/api.rb +84 -0
  209. data/lib/bundles/inspec-supermarket/cli.rb +73 -0
  210. data/lib/bundles/inspec-supermarket/target.rb +34 -0
  211. data/lib/fetchers/git.rb +163 -0
  212. data/lib/fetchers/local.rb +74 -0
  213. data/lib/fetchers/mock.rb +35 -0
  214. data/lib/fetchers/url.rb +247 -0
  215. data/lib/inspec.rb +24 -0
  216. data/lib/inspec/archive/tar.rb +29 -0
  217. data/lib/inspec/archive/zip.rb +19 -0
  218. data/lib/inspec/backend.rb +93 -0
  219. data/lib/inspec/base_cli.rb +368 -0
  220. data/lib/inspec/cached_fetcher.rb +66 -0
  221. data/lib/inspec/cli.rb +292 -0
  222. data/lib/inspec/completions/bash.sh.erb +45 -0
  223. data/lib/inspec/completions/fish.sh.erb +34 -0
  224. data/lib/inspec/completions/zsh.sh.erb +61 -0
  225. data/lib/inspec/control_eval_context.rb +179 -0
  226. data/lib/inspec/dependencies/cache.rb +72 -0
  227. data/lib/inspec/dependencies/dependency_set.rb +92 -0
  228. data/lib/inspec/dependencies/lockfile.rb +115 -0
  229. data/lib/inspec/dependencies/requirement.rb +123 -0
  230. data/lib/inspec/dependencies/resolver.rb +86 -0
  231. data/lib/inspec/describe.rb +27 -0
  232. data/lib/inspec/dsl.rb +66 -0
  233. data/lib/inspec/dsl_shared.rb +33 -0
  234. data/lib/inspec/env_printer.rb +157 -0
  235. data/lib/inspec/errors.rb +14 -0
  236. data/lib/inspec/exceptions.rb +12 -0
  237. data/lib/inspec/expect.rb +45 -0
  238. data/lib/inspec/fetcher.rb +45 -0
  239. data/lib/inspec/file_provider.rb +275 -0
  240. data/lib/inspec/formatters.rb +3 -0
  241. data/lib/inspec/formatters/base.rb +259 -0
  242. data/lib/inspec/formatters/json_rspec.rb +20 -0
  243. data/lib/inspec/formatters/show_progress.rb +12 -0
  244. data/lib/inspec/library_eval_context.rb +58 -0
  245. data/lib/inspec/log.rb +11 -0
  246. data/lib/inspec/metadata.rb +247 -0
  247. data/lib/inspec/method_source.rb +24 -0
  248. data/lib/inspec/objects.rb +14 -0
  249. data/lib/inspec/objects/attribute.rb +75 -0
  250. data/lib/inspec/objects/control.rb +61 -0
  251. data/lib/inspec/objects/describe.rb +92 -0
  252. data/lib/inspec/objects/each_loop.rb +36 -0
  253. data/lib/inspec/objects/list.rb +15 -0
  254. data/lib/inspec/objects/or_test.rb +40 -0
  255. data/lib/inspec/objects/ruby_helper.rb +15 -0
  256. data/lib/inspec/objects/tag.rb +27 -0
  257. data/lib/inspec/objects/test.rb +87 -0
  258. data/lib/inspec/objects/value.rb +27 -0
  259. data/lib/inspec/plugins.rb +60 -0
  260. data/lib/inspec/plugins/cli.rb +24 -0
  261. data/lib/inspec/plugins/fetcher.rb +86 -0
  262. data/lib/inspec/plugins/resource.rb +135 -0
  263. data/lib/inspec/plugins/secret.rb +15 -0
  264. data/lib/inspec/plugins/source_reader.rb +40 -0
  265. data/lib/inspec/polyfill.rb +12 -0
  266. data/lib/inspec/profile.rb +513 -0
  267. data/lib/inspec/profile_context.rb +208 -0
  268. data/lib/inspec/profile_vendor.rb +66 -0
  269. data/lib/inspec/reporters.rb +60 -0
  270. data/lib/inspec/reporters/automate.rb +76 -0
  271. data/lib/inspec/reporters/base.rb +25 -0
  272. data/lib/inspec/reporters/cli.rb +356 -0
  273. data/lib/inspec/reporters/json.rb +116 -0
  274. data/lib/inspec/reporters/json_min.rb +48 -0
  275. data/lib/inspec/reporters/junit.rb +78 -0
  276. data/lib/inspec/require_loader.rb +33 -0
  277. data/lib/inspec/resource.rb +190 -0
  278. data/lib/inspec/rule.rb +280 -0
  279. data/lib/inspec/runner.rb +345 -0
  280. data/lib/inspec/runner_mock.rb +41 -0
  281. data/lib/inspec/runner_rspec.rb +175 -0
  282. data/lib/inspec/runtime_profile.rb +26 -0
  283. data/lib/inspec/schema.rb +213 -0
  284. data/lib/inspec/secrets.rb +19 -0
  285. data/lib/inspec/secrets/yaml.rb +30 -0
  286. data/lib/inspec/shell.rb +220 -0
  287. data/lib/inspec/shell_detector.rb +90 -0
  288. data/lib/inspec/source_reader.rb +29 -0
  289. data/lib/inspec/version.rb +8 -0
  290. data/lib/matchers/matchers.rb +339 -0
  291. data/lib/resources/aide_conf.rb +151 -0
  292. data/lib/resources/apache.rb +48 -0
  293. data/lib/resources/apache_conf.rb +149 -0
  294. data/lib/resources/apt.rb +149 -0
  295. data/lib/resources/audit_policy.rb +63 -0
  296. data/lib/resources/auditd.rb +231 -0
  297. data/lib/resources/auditd_conf.rb +46 -0
  298. data/lib/resources/bash.rb +35 -0
  299. data/lib/resources/bond.rb +69 -0
  300. data/lib/resources/bridge.rb +122 -0
  301. data/lib/resources/chocolatey_package.rb +78 -0
  302. data/lib/resources/command.rb +73 -0
  303. data/lib/resources/cpan.rb +58 -0
  304. data/lib/resources/cran.rb +64 -0
  305. data/lib/resources/crontab.rb +169 -0
  306. data/lib/resources/csv.rb +56 -0
  307. data/lib/resources/dh_params.rb +77 -0
  308. data/lib/resources/directory.rb +25 -0
  309. data/lib/resources/docker.rb +236 -0
  310. data/lib/resources/docker_container.rb +89 -0
  311. data/lib/resources/docker_image.rb +83 -0
  312. data/lib/resources/docker_object.rb +57 -0
  313. data/lib/resources/docker_service.rb +90 -0
  314. data/lib/resources/elasticsearch.rb +169 -0
  315. data/lib/resources/etc_fstab.rb +94 -0
  316. data/lib/resources/etc_group.rb +154 -0
  317. data/lib/resources/etc_hosts.rb +66 -0
  318. data/lib/resources/etc_hosts_allow_deny.rb +112 -0
  319. data/lib/resources/file.rb +298 -0
  320. data/lib/resources/filesystem.rb +31 -0
  321. data/lib/resources/firewalld.rb +143 -0
  322. data/lib/resources/gem.rb +70 -0
  323. data/lib/resources/groups.rb +215 -0
  324. data/lib/resources/grub_conf.rb +227 -0
  325. data/lib/resources/host.rb +306 -0
  326. data/lib/resources/http.rb +253 -0
  327. data/lib/resources/iis_app.rb +101 -0
  328. data/lib/resources/iis_site.rb +148 -0
  329. data/lib/resources/inetd_conf.rb +54 -0
  330. data/lib/resources/ini.rb +29 -0
  331. data/lib/resources/interface.rb +129 -0
  332. data/lib/resources/iptables.rb +80 -0
  333. data/lib/resources/json.rb +111 -0
  334. data/lib/resources/kernel_module.rb +107 -0
  335. data/lib/resources/kernel_parameter.rb +58 -0
  336. data/lib/resources/key_rsa.rb +63 -0
  337. data/lib/resources/limits_conf.rb +46 -0
  338. data/lib/resources/login_def.rb +57 -0
  339. data/lib/resources/mount.rb +88 -0
  340. data/lib/resources/mssql_session.rb +101 -0
  341. data/lib/resources/mysql.rb +82 -0
  342. data/lib/resources/mysql_conf.rb +127 -0
  343. data/lib/resources/mysql_session.rb +85 -0
  344. data/lib/resources/nginx.rb +96 -0
  345. data/lib/resources/nginx_conf.rb +226 -0
  346. data/lib/resources/npm.rb +48 -0
  347. data/lib/resources/ntp_conf.rb +51 -0
  348. data/lib/resources/oneget.rb +71 -0
  349. data/lib/resources/oracledb_session.rb +139 -0
  350. data/lib/resources/os.rb +36 -0
  351. data/lib/resources/os_env.rb +86 -0
  352. data/lib/resources/package.rb +370 -0
  353. data/lib/resources/packages.rb +111 -0
  354. data/lib/resources/parse_config.rb +112 -0
  355. data/lib/resources/passwd.rb +76 -0
  356. data/lib/resources/pip.rb +130 -0
  357. data/lib/resources/platform.rb +109 -0
  358. data/lib/resources/port.rb +771 -0
  359. data/lib/resources/postgres.rb +131 -0
  360. data/lib/resources/postgres_conf.rb +114 -0
  361. data/lib/resources/postgres_hba_conf.rb +90 -0
  362. data/lib/resources/postgres_ident_conf.rb +79 -0
  363. data/lib/resources/postgres_session.rb +71 -0
  364. data/lib/resources/powershell.rb +67 -0
  365. data/lib/resources/processes.rb +204 -0
  366. data/lib/resources/rabbitmq_conf.rb +51 -0
  367. data/lib/resources/registry_key.rb +297 -0
  368. data/lib/resources/security_policy.rb +180 -0
  369. data/lib/resources/service.rb +794 -0
  370. data/lib/resources/shadow.rb +159 -0
  371. data/lib/resources/ssh_conf.rb +97 -0
  372. data/lib/resources/ssl.rb +99 -0
  373. data/lib/resources/sys_info.rb +28 -0
  374. data/lib/resources/toml.rb +32 -0
  375. data/lib/resources/users.rb +654 -0
  376. data/lib/resources/vbscript.rb +68 -0
  377. data/lib/resources/virtualization.rb +247 -0
  378. data/lib/resources/windows_feature.rb +84 -0
  379. data/lib/resources/windows_hotfix.rb +35 -0
  380. data/lib/resources/windows_task.rb +102 -0
  381. data/lib/resources/wmi.rb +110 -0
  382. data/lib/resources/x509_certificate.rb +137 -0
  383. data/lib/resources/xinetd.rb +106 -0
  384. data/lib/resources/xml.rb +46 -0
  385. data/lib/resources/yaml.rb +43 -0
  386. data/lib/resources/yum.rb +180 -0
  387. data/lib/resources/zfs_dataset.rb +60 -0
  388. data/lib/resources/zfs_pool.rb +49 -0
  389. data/lib/source_readers/flat.rb +39 -0
  390. data/lib/source_readers/inspec.rb +75 -0
  391. data/lib/utils/command_wrapper.rb +27 -0
  392. data/lib/utils/convert.rb +12 -0
  393. data/lib/utils/database_helpers.rb +77 -0
  394. data/lib/utils/enumerable_delegation.rb +9 -0
  395. data/lib/utils/erlang_parser.rb +192 -0
  396. data/lib/utils/file_reader.rb +25 -0
  397. data/lib/utils/filter.rb +273 -0
  398. data/lib/utils/filter_array.rb +27 -0
  399. data/lib/utils/find_files.rb +47 -0
  400. data/lib/utils/hash.rb +41 -0
  401. data/lib/utils/json_log.rb +18 -0
  402. data/lib/utils/latest_version.rb +22 -0
  403. data/lib/utils/modulator.rb +12 -0
  404. data/lib/utils/nginx_parser.rb +105 -0
  405. data/lib/utils/object_traversal.rb +49 -0
  406. data/lib/utils/parser.rb +274 -0
  407. data/lib/utils/pkey_reader.rb +15 -0
  408. data/lib/utils/plugin_registry.rb +93 -0
  409. data/lib/utils/simpleconfig.rb +120 -0
  410. data/lib/utils/spdx.rb +13 -0
  411. data/lib/utils/spdx.txt +344 -0
  412. metadata +713 -0
@@ -0,0 +1,208 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ require 'inspec/log'
5
+ require 'inspec/rule'
6
+ require 'inspec/resource'
7
+ require 'inspec/library_eval_context'
8
+ require 'inspec/control_eval_context'
9
+ require 'inspec/require_loader'
10
+ require 'securerandom'
11
+ require 'inspec/objects/attribute'
12
+
13
+ module Inspec
14
+ class ProfileContext
15
+ def self.for_profile(profile, backend, attributes)
16
+ new(profile.name, backend, { 'profile' => profile,
17
+ 'attributes' => attributes,
18
+ 'check_mode' => profile.check_mode })
19
+ end
20
+
21
+ attr_reader :attributes, :profile_id, :resource_registry, :backend
22
+ attr_accessor :rules
23
+ def initialize(profile_id, backend, conf)
24
+ if backend.nil?
25
+ raise 'ProfileContext is initiated with a backend == nil. ' \
26
+ 'This is a backend error which must be fixed upstream.'
27
+ end
28
+ @profile_id = profile_id
29
+ @backend = backend
30
+ @conf = conf.dup
31
+ @skip_only_if_eval = @conf['check_mode']
32
+ @rules = {}
33
+ @control_subcontexts = []
34
+ @lib_subcontexts = []
35
+ @require_loader = ::Inspec::RequireLoader.new
36
+ @attributes = []
37
+ # A local resource registry that only contains resources defined
38
+ # in the transitive dependency tree of the loaded profile.
39
+ @resource_registry = Inspec::Resource.new_registry
40
+ @library_eval_context = Inspec::LibraryEvalContext.create(@resource_registry, @require_loader)
41
+ @current_load = nil
42
+ end
43
+
44
+ def dependencies
45
+ if @conf['profile'].nil?
46
+ {}
47
+ else
48
+ @conf['profile'].locked_dependencies
49
+ end
50
+ end
51
+
52
+ def to_resources_dsl
53
+ Inspec::Resource.create_dsl(self)
54
+ end
55
+
56
+ def control_eval_context
57
+ @control_eval_context ||= begin
58
+ ctx = Inspec::ControlEvalContext.create(self, to_resources_dsl)
59
+ ctx.new(@backend, @conf, dependencies, @require_loader, @skip_only_if_eval)
60
+ end
61
+ end
62
+
63
+ def reload_dsl
64
+ @control_eval_context = nil
65
+ end
66
+
67
+ def profile_supports_platform?
68
+ return true if @conf['profile'].nil?
69
+
70
+ @conf['profile'].supports_platform?
71
+ end
72
+
73
+ def profile_supports_inspec_version?
74
+ return true if @conf['profile'].nil?
75
+
76
+ @conf['profile'].supports_runtime?
77
+ end
78
+
79
+ def remove_rule(id)
80
+ @rules[id] = nil if @rules.key?(id)
81
+ @control_subcontexts.each do |c|
82
+ c.remove_rule(id)
83
+ end
84
+ end
85
+
86
+ def all_controls
87
+ ret = @rules.values
88
+ ret += @control_subcontexts.map(&:all_rules).flatten
89
+ ret
90
+ end
91
+ alias all_rules all_controls
92
+
93
+ def subcontext_by_name(name)
94
+ found = @lib_subcontexts.find { |c| c.profile_id == name }
95
+ if !found
96
+ @lib_subcontexts.each do |c|
97
+ found = c.subcontext_by_name(name)
98
+ break if found
99
+ end
100
+ end
101
+
102
+ found
103
+ end
104
+
105
+ def add_resources(context)
106
+ @resource_registry.merge!(context.resource_registry)
107
+ control_eval_context.add_resources(context)
108
+ @lib_subcontexts << context
109
+ reload_dsl
110
+ end
111
+
112
+ def add_subcontext(context)
113
+ @control_subcontexts << context
114
+ end
115
+
116
+ def load_libraries(libs)
117
+ lib_prefix = 'libraries' + File::SEPARATOR
118
+ autoloads = []
119
+
120
+ libs.sort_by! { |l| l[1] } # Sort on source path so load order is deterministic
121
+ libs.each do |content, source, line|
122
+ path = source
123
+ if source.start_with?(lib_prefix)
124
+ path = source.sub(lib_prefix, '')
125
+ autoloads.push(path) if File.dirname(path) == '.'
126
+ end
127
+
128
+ @require_loader.add(path, content, source, line)
129
+ end
130
+
131
+ # load all files directly that are flat inside the libraries folder
132
+ autoloads.each do |path|
133
+ next unless path.end_with?('.rb')
134
+ load_library_file(*@require_loader.load(path)) unless @require_loader.loaded?(path)
135
+ end
136
+ reload_dsl
137
+ end
138
+
139
+ def load_control_file(*args)
140
+ # Set `skip_file` to `false` between file loads to prevent skips from spanning multiple control files
141
+ control_eval_context.skip_file = false
142
+ load_with_context(control_eval_context, *args)
143
+ end
144
+ alias load load_control_file
145
+
146
+ def load_library_file(*args)
147
+ load_with_context(@library_eval_context, *args)
148
+ end
149
+
150
+ def load_with_context(context, content, source = nil, line = nil)
151
+ Inspec::Log.debug("Loading #{source || '<anonymous content>'} into #{self}")
152
+ @current_load = { file: source }
153
+ if content.is_a? Proc
154
+ context.instance_eval(&content)
155
+ elsif source.nil? && line.nil?
156
+ context.instance_eval(content)
157
+ else
158
+ context.instance_eval(content, source || 'unknown', line || 1)
159
+ end
160
+ end
161
+
162
+ def unregister_rule(id)
163
+ @rules.delete(full_id(@profile_id, id))
164
+ end
165
+
166
+ attr_reader :current_load
167
+
168
+ def register_rule(r)
169
+ # get the full ID
170
+ file = if @current_load.nil?
171
+ 'unknown'
172
+ else
173
+ @current_load[:file] || 'unknown'
174
+ end
175
+ r.instance_variable_set(:@__file, file)
176
+ r.instance_variable_set(:@__group_title, current_load[:title])
177
+
178
+ # add the rule to the registry
179
+ fid = full_id(Inspec::Rule.profile_id(r), Inspec::Rule.rule_id(r))
180
+ existing = @rules[fid]
181
+ if existing.nil?
182
+ @rules[fid] = r
183
+ else
184
+ Inspec::Rule.merge(existing, r)
185
+ end
186
+ end
187
+
188
+ def register_attribute(name, options = {})
189
+ # we need to return an attribute object, to allow dermination of default values
190
+ attr = Attribute.new(name, options)
191
+ # read value from given gived values
192
+ attr.value = @conf['attributes'][attr.name] unless @conf['attributes'].nil?
193
+ @attributes.push(attr)
194
+ attr.value
195
+ end
196
+
197
+ def set_header(field, val)
198
+ @current_load[field] = val
199
+ end
200
+
201
+ private
202
+
203
+ def full_id(pid, rid)
204
+ return rid.to_s if pid.to_s.empty?
205
+ pid.to_s + '/' + rid.to_s
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ # author: Adam Leff
3
+
4
+ require 'inspec/profile'
5
+
6
+ module Inspec
7
+ class ProfileVendor
8
+ attr_reader :profile_path
9
+
10
+ def initialize(path)
11
+ @profile_path = Pathname.new(path)
12
+ end
13
+
14
+ def vendor!
15
+ vendor_dependencies
16
+ end
17
+
18
+ # The URL fetcher uses a Tempfile to retrieve the vendored
19
+ # profile, which creates a file that is only readable by
20
+ # the current user. In most circumstances, this is likely OK.
21
+ # However, in environments like a Habitat package, these files
22
+ # need to be readable by all users or the Habitat Supervisor
23
+ # may not be able to start InSpec correctly.
24
+ #
25
+ # This method makes sure all vendored files are mode 644 for this
26
+ # use case. This method is not called by default - the caller
27
+ # vendoring the profile must make the decision as to whether this
28
+ # is necessary.
29
+ def make_readable
30
+ Dir.glob("#{cache_path}/**/*") do |e|
31
+ FileUtils.chmod(0644, e) if File.file?(e)
32
+ end
33
+ end
34
+
35
+ def cache_path
36
+ profile_path.join('vendor')
37
+ end
38
+
39
+ def lockfile
40
+ profile_path.join('inspec.lock')
41
+ end
42
+
43
+ private
44
+
45
+ def profile
46
+ @profile ||= Inspec::Profile.for_target(profile_path.to_s, profile_opts)
47
+ end
48
+
49
+ def profile_opts
50
+ {
51
+ vendor_cache: Inspec::Cache.new(cache_path.to_s),
52
+ backend: Inspec::Backend.create(target: 'mock://'),
53
+ }
54
+ end
55
+
56
+ def vendor_dependencies
57
+ delete_vendored_data
58
+ File.write(lockfile, profile.generate_lockfile.to_yaml)
59
+ end
60
+
61
+ def delete_vendored_data
62
+ FileUtils.rm_rf(cache_path) if cache_path.exist?
63
+ File.delete(lockfile) if lockfile.exist?
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,60 @@
1
+ require 'inspec/reporters/base'
2
+ require 'inspec/reporters/cli'
3
+ require 'inspec/reporters/json'
4
+ require 'inspec/reporters/json_min'
5
+ require 'inspec/reporters/junit'
6
+ require 'inspec/reporters/automate'
7
+
8
+ module Inspec::Reporters
9
+ def self.render(reporter, run_data)
10
+ name, config = reporter.dup
11
+ config[:run_data] = run_data
12
+ case name
13
+ when 'cli'
14
+ reporter = Inspec::Reporters::CLI.new(config)
15
+ when 'json'
16
+ reporter = Inspec::Reporters::Json.new(config)
17
+ when 'json-min'
18
+ reporter = Inspec::Reporters::JsonMin.new(config)
19
+ when 'junit'
20
+ reporter = Inspec::Reporters::Junit.new(config)
21
+ when 'automate'
22
+ reporter = Inspec::Reporters::Automate.new(config)
23
+ else
24
+ raise NotImplementedError, "'#{name}' is not a valid reporter type."
25
+ end
26
+
27
+ # optional send_report method on reporter
28
+ return reporter.send_report if defined?(reporter.send_report)
29
+
30
+ reporter.render
31
+ output = reporter.rendered_output
32
+
33
+ if config['file']
34
+ # create destination directory if it does not exist
35
+ dirname = File.dirname(config['file'])
36
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
37
+
38
+ File.write(config['file'], output)
39
+ elsif config['stdout'] == true
40
+ print output
41
+ STDOUT.flush
42
+ end
43
+ end
44
+
45
+ def self.report(reporter, run_data)
46
+ name, config = reporter.dup
47
+ config[:run_data] = run_data
48
+ case name
49
+ when 'json'
50
+ reporter = Inspec::Reporters::Json.new(config)
51
+ when 'json-min'
52
+ reporter = Inspec::Reporters::JsonMin.new(config)
53
+ else
54
+ # use base run_data hash for any other report
55
+ return run_data
56
+ end
57
+
58
+ reporter.report
59
+ end
60
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+
6
+ module Inspec::Reporters
7
+ class Automate < Json
8
+ def initialize(config)
9
+ super(config)
10
+
11
+ # default to not verifying ssl for sending reports
12
+ @config['verify_ssl'] = @config['verify_ssl'] || false
13
+ end
14
+
15
+ def enriched_report
16
+ # grab the report from the parent class
17
+ final_report = report
18
+
19
+ # Label this content as an inspec_report
20
+ final_report[:type] = 'inspec_report'
21
+
22
+ final_report[:end_time] = Time.now.utc.strftime('%FT%TZ')
23
+ final_report[:node_uuid] = @config['node_uuid'] || @run_data[:platform][:uuid]
24
+ raise Inspec::ReporterError, 'Cannot find a UUID for your node. Please specify one via json-config.' if final_report[:node_uuid].nil?
25
+
26
+ final_report[:report_uuid] = uuid_from_string(final_report[:end_time] + final_report[:node_uuid])
27
+
28
+ # optional json-config passthrough options
29
+ %w{node_name environment roles recipies}.each do |option|
30
+ final_report[option.to_sym] = @config[option] unless @config[option].nil?
31
+ end
32
+ final_report
33
+ end
34
+
35
+ def send_report
36
+ headers = { 'Content-Type' => 'application/json' }
37
+ headers['x-data-collector-token'] = @config['token']
38
+ headers['x-data-collector-auth'] = 'version=1.0'
39
+
40
+ uri = URI(@config['url'])
41
+ req = Net::HTTP::Post.new(uri.path, headers)
42
+ req.body = enriched_report.to_json
43
+ begin
44
+ Inspec::Log.debug "Posting report to Chef Automate: #{uri.path}"
45
+ http = Net::HTTP.new(uri.hostname, uri.port)
46
+ http.use_ssl = uri.scheme == 'https'
47
+ if @config['verify_ssl'] == true
48
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
49
+ else
50
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
51
+ end
52
+
53
+ http.request(req)
54
+ return true
55
+ rescue => e
56
+ Inspec::Log.error "send_report: POST to #{uri.path} returned: #{e.message}"
57
+ return false
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # This hashes the passed string into SHA1.
64
+ # Then it downgrades the 160bit SHA1 to a 128bit
65
+ # then we format it as a valid UUIDv5.
66
+ def uuid_from_string(string)
67
+ hash = Digest::SHA1.new
68
+ hash.update(string)
69
+ ary = hash.digest.unpack('NnnnnN')
70
+ ary[2] = (ary[2] & 0x0FFF) | (5 << 12)
71
+ ary[3] = (ary[3] & 0x3FFF) | 0x8000
72
+ # rubocop:disable Style/FormatString
73
+ '%08x-%04x-%04x-%04x-%04x%08x' % ary
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,25 @@
1
+ module Inspec::Reporters
2
+ class Base
3
+ attr_reader :run_data
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ @run_data = config[:run_data]
8
+ @output = ''
9
+ end
10
+
11
+ def output(str, newline = true)
12
+ @output << str
13
+ @output << "\n" if newline
14
+ end
15
+
16
+ def rendered_output
17
+ @output
18
+ end
19
+
20
+ # each reporter must implement #render
21
+ def render
22
+ raise NotImplementedError, "#{self.class} must implement a `#render` method to format its output."
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,356 @@
1
+ # encoding: utf-8
2
+
3
+ module Inspec::Reporters
4
+ class CLI < Base
5
+ case RUBY_PLATFORM
6
+ when /windows|mswin|msys|mingw|cygwin/
7
+ # Most currently available Windows terminals have poor support
8
+ # for ANSI extended colors
9
+ COLORS = {
10
+ 'failed' => "\033[0;1;31m",
11
+ 'passed' => "\033[0;1;32m",
12
+ 'skipped' => "\033[0;37m",
13
+ 'reset' => "\033[0m",
14
+ }.freeze
15
+
16
+ # Most currently available Windows terminals have poor support
17
+ # for UTF-8 characters so use these boring indicators
18
+ INDICATORS = {
19
+ 'failed' => '[FAIL]',
20
+ 'skipped' => '[SKIP]',
21
+ 'passed' => '[PASS]',
22
+ 'unknown' => '[UNKN]',
23
+ }.freeze
24
+ else
25
+ # Extended colors for everyone else
26
+ COLORS = {
27
+ 'failed' => "\033[38;5;9m",
28
+ 'passed' => "\033[38;5;41m",
29
+ 'skipped' => "\033[38;5;247m",
30
+ 'reset' => "\033[0m",
31
+ }.freeze
32
+
33
+ # Groovy UTF-8 characters for everyone else...
34
+ # ...even though they probably only work on Mac
35
+ INDICATORS = {
36
+ 'failed' => '×',
37
+ 'skipped' => '↺',
38
+ 'passed' => '✔',
39
+ 'unknown' => '?',
40
+ }.freeze
41
+ end
42
+
43
+ MULTI_TEST_CONTROL_SUMMARY_MAX_LEN = 60
44
+
45
+ def render
46
+ run_data[:profiles].each do |profile|
47
+ @control_count = 0
48
+ output('')
49
+ print_profile_header(profile)
50
+ print_standard_control_results(profile)
51
+ print_anonymous_control_results(profile)
52
+ output(format_message(
53
+ indentation: 5,
54
+ message: 'No tests executed.',
55
+ )) if @control_count == 0
56
+ end
57
+
58
+ output('')
59
+ print_profile_summary
60
+ print_tests_summary
61
+ end
62
+
63
+ private
64
+
65
+ def print_profile_header(profile)
66
+ output("Profile: #{format_profile_name(profile)}")
67
+ output("Version: #{profile[:version] || '(not specified)'}")
68
+ output("Target: #{run_data[:platform][:target]}") unless run_data[:platform][:target].nil?
69
+ output('')
70
+ end
71
+
72
+ def print_standard_control_results(profile)
73
+ standard_controls_from_profile(profile).each do |control_from_profile|
74
+ control = Control.new(control_from_profile)
75
+ next if control.results.nil?
76
+ output(format_control_header(control))
77
+ control.results.each do |result|
78
+ output(format_result(control, result, :standard))
79
+ @control_count += 1
80
+ end
81
+ end
82
+ output('') if @control_count > 0
83
+ end
84
+
85
+ def print_anonymous_control_results(profile)
86
+ anonymous_controls_from_profile(profile).each do |control_from_profile|
87
+ control = Control.new(control_from_profile)
88
+ next if control.results.nil?
89
+ output(format_control_header(control))
90
+ control.results.each do |result|
91
+ output(format_result(control, result, :anonymous))
92
+ @control_count += 1
93
+ end
94
+ end
95
+ end
96
+
97
+ def format_profile_name(profile)
98
+ if profile[:title].nil?
99
+ (profile[:name] || 'unknown').to_s
100
+ else
101
+ "#{profile[:title]} (#{profile[:name] || 'unknown'})"
102
+ end
103
+ end
104
+
105
+ def format_control_header(control)
106
+ impact = control.impact_string
107
+ format_message(
108
+ color: impact,
109
+ indicator: impact,
110
+ message: control.title_for_report,
111
+ )
112
+ end
113
+
114
+ def format_result(control, result, type)
115
+ impact = control.impact_string_for_result(result)
116
+
117
+ message = if result[:status] == 'skipped'
118
+ result[:skip_message]
119
+ elsif type == :anonymous
120
+ result[:expectation_message]
121
+ else
122
+ result[:code_desc]
123
+ end
124
+
125
+ # append any failure details to the message if they exist
126
+ message += "\n#{result[:message]}" if result[:message]
127
+
128
+ format_message(
129
+ color: impact,
130
+ indicator: impact,
131
+ indentation: 5,
132
+ message: message,
133
+ )
134
+ end
135
+
136
+ def format_message(message_info)
137
+ indicator = message_info[:indicator]
138
+ color = message_info[:color]
139
+ indentation = message_info.fetch(:indentation, 2)
140
+ message = message_info[:message]
141
+
142
+ message_to_format = ''
143
+ message_to_format += "#{INDICATORS[indicator]} " unless indicator.nil?
144
+ message_to_format += message.to_s.lstrip
145
+
146
+ format_with_color(color, indent_lines(message_to_format, indentation))
147
+ end
148
+
149
+ def format_with_color(color_name, text)
150
+ return text if defined?(RSpec.configuration) && !RSpec.configuration.color
151
+ return text unless COLORS.key?(color_name)
152
+
153
+ "#{COLORS[color_name]}#{text}#{COLORS['reset']}"
154
+ end
155
+
156
+ def all_unique_controls
157
+ return @unique_controls unless @unique_controls.nil?
158
+
159
+ @unique_controls = Set.new
160
+ run_data[:profiles].each do |profile|
161
+ profile[:controls].map { |control| @unique_controls.add(control) }
162
+ end
163
+
164
+ @unique_controls
165
+ end
166
+
167
+ def profile_summary
168
+ failed = 0
169
+ skipped = 0
170
+ passed = 0
171
+
172
+ all_unique_controls.each do |control|
173
+ next if control[:id].start_with? '(generated from '
174
+ next unless control[:results]
175
+ if control[:results].any? { |r| r[:status] == 'failed' }
176
+ failed += 1
177
+ elsif control[:results].any? { |r| r[:status] == 'skipped' }
178
+ skipped += 1
179
+ else
180
+ passed += 1
181
+ end
182
+ end
183
+
184
+ total = failed + passed + skipped
185
+
186
+ {
187
+ 'total' => total,
188
+ 'failed' => failed,
189
+ 'skipped' => skipped,
190
+ 'passed' => passed,
191
+ }
192
+ end
193
+
194
+ def tests_summary
195
+ total = 0
196
+ failed = 0
197
+ skipped = 0
198
+ passed = 0
199
+
200
+ all_unique_controls.each do |control|
201
+ next unless control[:results]
202
+ control[:results].each do |result|
203
+ if result[:status] == 'failed'
204
+ failed += 1
205
+ elsif result[:status] == 'skipped'
206
+ skipped += 1
207
+ else
208
+ passed += 1
209
+ end
210
+ end
211
+ end
212
+
213
+ {
214
+ 'total' => total,
215
+ 'failed' => failed,
216
+ 'skipped' => skipped,
217
+ 'passed' => passed,
218
+ }
219
+ end
220
+
221
+ def print_profile_summary
222
+ summary = profile_summary
223
+ return unless summary['total'] > 0
224
+
225
+ success_str = summary['passed'] == 1 ? '1 successful control' : "#{summary['passed']} successful controls"
226
+ failed_str = summary['failed'] == 1 ? '1 control failure' : "#{summary['failed']} control failures"
227
+ skipped_str = summary['skipped'] == 1 ? '1 control skipped' : "#{summary['skipped']} controls skipped"
228
+
229
+ success_color = summary['passed'] > 0 ? 'passed' : 'no_color'
230
+ failed_color = summary['failed'] > 0 ? 'failed' : 'no_color'
231
+ skipped_color = summary['skipped'] > 0 ? 'skipped' : 'no_color'
232
+
233
+ s = format(
234
+ 'Profile Summary: %s, %s, %s',
235
+ format_with_color(success_color, success_str),
236
+ format_with_color(failed_color, failed_str),
237
+ format_with_color(skipped_color, skipped_str),
238
+ )
239
+ output(s) if summary['total'] > 0
240
+ end
241
+
242
+ def print_tests_summary
243
+ summary = tests_summary
244
+
245
+ failed_str = summary['failed'] == 1 ? '1 failure' : "#{summary['failed']} failures"
246
+
247
+ success_color = summary['passed'] > 0 ? 'passed' : 'no_color'
248
+ failed_color = summary['failed'] > 0 ? 'failed' : 'no_color'
249
+ skipped_color = summary['skipped'] > 0 ? 'skipped' : 'no_color'
250
+
251
+ s = format(
252
+ 'Test Summary: %s, %s, %s',
253
+ format_with_color(success_color, "#{summary['passed']} successful"),
254
+ format_with_color(failed_color, failed_str),
255
+ format_with_color(skipped_color, "#{summary['skipped']} skipped"),
256
+ )
257
+
258
+ output(s)
259
+ end
260
+
261
+ def standard_controls_from_profile(profile)
262
+ profile[:controls].reject { |c| is_anonymous_control?(c) }
263
+ end
264
+
265
+ def anonymous_controls_from_profile(profile)
266
+ profile[:controls].select { |c| is_anonymous_control?(c) && !c[:results].nil? }
267
+ end
268
+
269
+ def is_anonymous_control?(control)
270
+ control[:id].start_with?('(generated from ')
271
+ end
272
+
273
+ def indent_lines(message, indentation)
274
+ message.lines.map { |line| ' ' * indentation + line }.join
275
+ end
276
+
277
+ class Control
278
+ attr_reader :data
279
+
280
+ def initialize(control_hash)
281
+ @data = control_hash
282
+ end
283
+
284
+ def id
285
+ data[:id]
286
+ end
287
+
288
+ def title
289
+ data[:title]
290
+ end
291
+
292
+ def results
293
+ data[:results]
294
+ end
295
+
296
+ def impact
297
+ data[:impact]
298
+ end
299
+
300
+ def anonymous?
301
+ id.start_with?('(generated from ')
302
+ end
303
+
304
+ def title_for_report
305
+ # if this is an anonymous control, just grab the resource title from any result entry
306
+ return results.first[:resource_title] if anonymous?
307
+
308
+ title_for_report = "#{id}: #{title || results.first[:resource_title]}"
309
+
310
+ # we will not add any additional data to the title if there's only
311
+ # zero or one test for this control.
312
+ return title_for_report if results.nil? || results.size <= 1
313
+
314
+ # append a failure summary if appropriate.
315
+ title_for_report += " (#{failure_count} failed)" if failure_count > 0
316
+ title_for_report += " (#{skipped_count} skipped)" if skipped_count > 0
317
+
318
+ title_for_report
319
+ end
320
+
321
+ def impact_string
322
+ if anonymous?
323
+ nil
324
+ elsif impact.nil?
325
+ 'unknown'
326
+ elsif results&.find { |r| r[:status] == 'skipped' }
327
+ 'skipped'
328
+ elsif results.nil? || results.empty? || results.all? { |r| r[:status] == 'passed' }
329
+ 'passed'
330
+ else
331
+ 'failed'
332
+ end
333
+ end
334
+
335
+ def impact_string_for_result(result)
336
+ if result[:status] == 'skipped'
337
+ 'skipped'
338
+ elsif result[:status] == 'passed'
339
+ 'passed'
340
+ elsif impact.nil?
341
+ 'unknown'
342
+ else
343
+ 'failed'
344
+ end
345
+ end
346
+
347
+ def failure_count
348
+ results.select { |r| r[:status] == 'failed' }.size
349
+ end
350
+
351
+ def skipped_count
352
+ results.select { |r| r[:status] == 'skipped' }.size
353
+ end
354
+ end
355
+ end
356
+ end