inspec 2.1.81 → 2.1.83

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