inspec 2.1.81 → 2.1.83

Sign up to get free protection for your applications and to get access to all the features.
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
data/lib/resources/ssl.rb CHANGED
@@ -1,99 +1,99 @@
1
- # encoding: utf-8
2
- # copyright: 2015, Chef Software Inc.
3
-
4
- require 'sslshake'
5
- require 'utils/filter'
6
- require 'uri'
7
- require 'parallel'
8
-
9
- # Custom resource based on the InSpec resource DSL
10
- class SSL < Inspec.resource(1)
11
- name 'ssl'
12
- supports platform: 'unix'
13
- supports platform: 'windows'
14
-
15
- desc "
16
- SSL test resource
17
- "
18
-
19
- example "
20
- describe ssl(port: 443) do
21
- it { should be_enabled }
22
- end
23
-
24
- # protocols: ssl2, ssl3, tls1.0, tls1.1, tls1.2
25
- describe ssl(port: 443).protocols('ssl2') do
26
- it { should_not be_enabled }
27
- end
28
-
29
- # any ciphers, filter by name or regex
30
- describe ssl(port: 443).ciphers(/rc4/i) do
31
- it { should_not be_enabled }
32
- end
33
- "
34
-
35
- VERSIONS = [
36
- 'ssl2',
37
- 'ssl3',
38
- 'tls1.0',
39
- 'tls1.1',
40
- 'tls1.2',
41
- ].freeze
42
-
43
- attr_reader :host, :port, :timeout, :retries
44
-
45
- def initialize(opts = {})
46
- @host = opts[:host]
47
- if @host.nil?
48
- # Transports like SSH and WinRM will provide a hostname
49
- if inspec.backend.respond_to?('hostname')
50
- @host = inspec.backend.hostname
51
- elsif inspec.backend.class.to_s == 'Train::Transports::Local::Connection'
52
- @host = 'localhost'
53
- end
54
- end
55
- @port = opts[:port] || 443
56
- @timeout = opts[:timeout]
57
- @retries = opts[:retries]
58
- end
59
-
60
- filter = FilterTable.create
61
- filter.add(:enabled?) do |x|
62
- raise 'Cannot determine host for SSL test. Please specify it or use a different target.' if x.resource.host.nil?
63
- x.handshake.values.any? { |i| i['success'] }
64
- end
65
- filter.add_accessor(:where)
66
- .add_accessor(:entries)
67
- .add(:ciphers, field: 'cipher')
68
- .add(:protocols, field: 'protocol')
69
- .add(:handshake) { |x|
70
- groups = x.entries.group_by(&:protocol)
71
- res = Parallel.map(groups, in_threads: 8) do |proto, e|
72
- [proto, SSLShake.hello(x.resource.host, port: x.resource.port,
73
- protocol: proto, ciphers: e.map(&:cipher),
74
- timeout: x.resource.timeout, retries: x.resource.retries, servername: x.resource.host)]
75
- end
76
- Hash[res]
77
- }
78
- .connect(self, :scan_config)
79
-
80
- def to_s
81
- "SSL/TLS on #{@host}:#{@port}"
82
- end
83
-
84
- private
85
-
86
- def scan_config
87
- [
88
- { 'protocol' => 'ssl2', 'ciphers' => SSLShake::SSLv2::CIPHERS.keys },
89
- { 'protocol' => 'ssl3', 'ciphers' => SSLShake::TLS::SSL3_CIPHERS.keys },
90
- { 'protocol' => 'tls1.0', 'ciphers' => SSLShake::TLS::TLS10_CIPHERS.keys },
91
- { 'protocol' => 'tls1.1', 'ciphers' => SSLShake::TLS::TLS10_CIPHERS.keys },
92
- { 'protocol' => 'tls1.2', 'ciphers' => SSLShake::TLS::TLS_CIPHERS.keys },
93
- ].map do |line|
94
- line['ciphers'].map do |cipher|
95
- { 'protocol' => line['protocol'], 'cipher' => cipher }
96
- end
97
- end.flatten
98
- end
99
- end
1
+ # encoding: utf-8
2
+ # copyright: 2015, Chef Software Inc.
3
+
4
+ require 'sslshake'
5
+ require 'utils/filter'
6
+ require 'uri'
7
+ require 'parallel'
8
+
9
+ # Custom resource based on the InSpec resource DSL
10
+ class SSL < Inspec.resource(1)
11
+ name 'ssl'
12
+ supports platform: 'unix'
13
+ supports platform: 'windows'
14
+
15
+ desc "
16
+ SSL test resource
17
+ "
18
+
19
+ example "
20
+ describe ssl(port: 443) do
21
+ it { should be_enabled }
22
+ end
23
+
24
+ # protocols: ssl2, ssl3, tls1.0, tls1.1, tls1.2
25
+ describe ssl(port: 443).protocols('ssl2') do
26
+ it { should_not be_enabled }
27
+ end
28
+
29
+ # any ciphers, filter by name or regex
30
+ describe ssl(port: 443).ciphers(/rc4/i) do
31
+ it { should_not be_enabled }
32
+ end
33
+ "
34
+
35
+ VERSIONS = [
36
+ 'ssl2',
37
+ 'ssl3',
38
+ 'tls1.0',
39
+ 'tls1.1',
40
+ 'tls1.2',
41
+ ].freeze
42
+
43
+ attr_reader :host, :port, :timeout, :retries
44
+
45
+ def initialize(opts = {})
46
+ @host = opts[:host]
47
+ if @host.nil?
48
+ # Transports like SSH and WinRM will provide a hostname
49
+ if inspec.backend.respond_to?('hostname')
50
+ @host = inspec.backend.hostname
51
+ elsif inspec.backend.class.to_s == 'Train::Transports::Local::Connection'
52
+ @host = 'localhost'
53
+ end
54
+ end
55
+ @port = opts[:port] || 443
56
+ @timeout = opts[:timeout]
57
+ @retries = opts[:retries]
58
+ end
59
+
60
+ filter = FilterTable.create
61
+ filter.add(:enabled?) do |x|
62
+ raise 'Cannot determine host for SSL test. Please specify it or use a different target.' if x.resource.host.nil?
63
+ x.handshake.values.any? { |i| i['success'] }
64
+ end
65
+ filter.add_accessor(:where)
66
+ .add_accessor(:entries)
67
+ .add(:ciphers, field: 'cipher')
68
+ .add(:protocols, field: 'protocol')
69
+ .add(:handshake) { |x|
70
+ groups = x.entries.group_by(&:protocol)
71
+ res = Parallel.map(groups, in_threads: 8) do |proto, e|
72
+ [proto, SSLShake.hello(x.resource.host, port: x.resource.port,
73
+ protocol: proto, ciphers: e.map(&:cipher),
74
+ timeout: x.resource.timeout, retries: x.resource.retries, servername: x.resource.host)]
75
+ end
76
+ Hash[res]
77
+ }
78
+ .connect(self, :scan_config)
79
+
80
+ def to_s
81
+ "SSL/TLS on #{@host}:#{@port}"
82
+ end
83
+
84
+ private
85
+
86
+ def scan_config
87
+ [
88
+ { 'protocol' => 'ssl2', 'ciphers' => SSLShake::SSLv2::CIPHERS.keys },
89
+ { 'protocol' => 'ssl3', 'ciphers' => SSLShake::TLS::SSL3_CIPHERS.keys },
90
+ { 'protocol' => 'tls1.0', 'ciphers' => SSLShake::TLS::TLS10_CIPHERS.keys },
91
+ { 'protocol' => 'tls1.1', 'ciphers' => SSLShake::TLS::TLS10_CIPHERS.keys },
92
+ { 'protocol' => 'tls1.2', 'ciphers' => SSLShake::TLS::TLS_CIPHERS.keys },
93
+ ].map do |line|
94
+ line['ciphers'].map do |cipher|
95
+ { 'protocol' => line['protocol'], 'cipher' => cipher }
96
+ end
97
+ end.flatten
98
+ end
99
+ end
@@ -1,28 +1,28 @@
1
- # encoding: utf-8
2
- module Inspec::Resources
3
- # this resource returns additional system informatio
4
- class System < Inspec.resource(1)
5
- name 'sys_info'
6
- supports platform: 'unix'
7
- supports platform: 'windows'
8
-
9
- desc 'Use the user InSpec system resource to test for operating system properties.'
10
- example "
11
- describe sys_info do
12
- its('hostname') { should eq 'example.com' }
13
- end
14
- "
15
-
16
- # returns the hostname of the local system
17
- def hostname
18
- os = inspec.os
19
- if os.linux? || os.darwin?
20
- inspec.command('hostname').stdout.chomp
21
- elsif os.windows?
22
- inspec.powershell('$env:computername').stdout.chomp
23
- else
24
- skip_resource 'The `sys_info.hostname` resource is not supported on your OS yet.'
25
- end
26
- end
27
- end
28
- end
1
+ # encoding: utf-8
2
+ module Inspec::Resources
3
+ # this resource returns additional system informatio
4
+ class System < Inspec.resource(1)
5
+ name 'sys_info'
6
+ supports platform: 'unix'
7
+ supports platform: 'windows'
8
+
9
+ desc 'Use the user InSpec system resource to test for operating system properties.'
10
+ example "
11
+ describe sys_info do
12
+ its('hostname') { should eq 'example.com' }
13
+ end
14
+ "
15
+
16
+ # returns the hostname of the local system
17
+ def hostname
18
+ os = inspec.os
19
+ if os.linux? || os.darwin?
20
+ inspec.command('hostname').stdout.chomp
21
+ elsif os.windows?
22
+ inspec.powershell('$env:computername').stdout.chomp
23
+ else
24
+ skip_resource 'The `sys_info.hostname` resource is not supported on your OS yet.'
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,32 +1,32 @@
1
- # encoding: utf-8
2
- # author: Nolan Davidson
3
-
4
- require 'tomlrb'
5
-
6
- module Inspec::Resources
7
- class TomlConfig < JsonConfig
8
- name 'toml'
9
- desc 'Use the toml InSpec resource to test configuration data in a TOML file'
10
- example "
11
- describe toml('default.toml') do
12
- its('key') { should eq('value') }
13
- its (['arr', 1]) { should eq 2 }
14
- its (['mytable', 'key1']) { should eq 'value1' }
15
- end
16
- "
17
-
18
- def parse(content)
19
- Tomlrb.parse(content)
20
- rescue => e
21
- raise Inspec::Exceptions::ResourceFailed, "Unable to parse TOML: #{e.message}"
22
- end
23
-
24
- private
25
-
26
- # used by JsonConfig to build up a full to_s method
27
- # based on whether a file path, content, or command was supplied.
28
- def resource_base_name
29
- 'TOML'
30
- end
31
- end
32
- end
1
+ # encoding: utf-8
2
+ # author: Nolan Davidson
3
+
4
+ require 'tomlrb'
5
+
6
+ module Inspec::Resources
7
+ class TomlConfig < JsonConfig
8
+ name 'toml'
9
+ desc 'Use the toml InSpec resource to test configuration data in a TOML file'
10
+ example "
11
+ describe toml('default.toml') do
12
+ its('key') { should eq('value') }
13
+ its (['arr', 1]) { should eq 2 }
14
+ its (['mytable', 'key1']) { should eq 'value1' }
15
+ end
16
+ "
17
+
18
+ def parse(content)
19
+ Tomlrb.parse(content)
20
+ rescue => e
21
+ raise Inspec::Exceptions::ResourceFailed, "Unable to parse TOML: #{e.message}"
22
+ end
23
+
24
+ private
25
+
26
+ # used by JsonConfig to build up a full to_s method
27
+ # based on whether a file path, content, or command was supplied.
28
+ def resource_base_name
29
+ 'TOML'
30
+ end
31
+ end
32
+ end
@@ -1,654 +1,654 @@
1
- # encoding: utf-8
2
-
3
- require 'utils/parser'
4
- require 'utils/convert'
5
- require 'utils/filter'
6
-
7
- module Inspec::Resources
8
- # This file contains two resources, the `user` and `users` resource.
9
- # The `user` resource is optimized for requests that verify specific users
10
- # that you know upfront for testing. If you need to query all users or search
11
- # specific users with certain properties, use the `users` resource.
12
- module UserManagementSelector
13
- # select user provider based on the operating system
14
- # returns nil, if no user manager was found for the operating system
15
- def select_user_manager(os)
16
- if os.linux?
17
- LinuxUser.new(inspec)
18
- elsif os.windows?
19
- WindowsUser.new(inspec)
20
- elsif ['darwin'].include?(os[:family])
21
- DarwinUser.new(inspec)
22
- elsif ['freebsd'].include?(os[:family])
23
- FreeBSDUser.new(inspec)
24
- elsif ['aix'].include?(os[:family])
25
- AixUser.new(inspec)
26
- elsif os.solaris?
27
- SolarisUser.new(inspec)
28
- elsif ['hpux'].include?(os[:family])
29
- HpuxUser.new(inspec)
30
- end
31
- end
32
- end
33
-
34
- # The InSpec users resources looksup all local users available on a system.
35
- # TODO: the current version of the users resource will use eg. /etc/passwd
36
- # on Linux to parse all usernames. Therefore the resource may not return
37
- # users managed on other systems like LDAP/ActiveDirectory. Please open
38
- # a feature request at https://github.com/chef/inspec if you need that
39
- # functionality
40
- #
41
- # This resource allows complex filter mechanisms
42
- #
43
- # describe users.where(uid: 0).entries do
44
- # it { should eq ['root'] }
45
- # its('uids') { should eq [1234] }
46
- # its('gids') { should eq [1234] }
47
- # end
48
- #
49
- # describe users.where { uid =~ /S\-1\-5\-21\-\d+\-\d+\-\d+\-500/ } do
50
- # it { should exist }
51
- # end
52
- class Users < Inspec.resource(1)
53
- include UserManagementSelector
54
-
55
- name 'users'
56
- supports platform: 'unix'
57
- supports platform: 'windows'
58
- desc 'Use the users InSpec audit resource to test local user profiles. Users can be filtered by groups to which they belong, the frequency of required password changes, the directory paths to home and shell.'
59
- example "
60
- describe users.where { uid == 0 }.entries do
61
- it { should eq ['root'] }
62
- its('uids') { should eq [1234] }
63
- its('gids') { should eq [1234] }
64
- end
65
- "
66
- def initialize
67
- # select user provider
68
- @user_provider = select_user_manager(inspec.os)
69
- return skip_resource 'The `users` resource is not supported on your OS yet.' if @user_provider.nil?
70
- end
71
-
72
- filter = FilterTable.create
73
- filter.add_accessor(:where)
74
- .add_accessor(:entries)
75
- .add(:usernames, field: :username)
76
- .add(:uids, field: :uid)
77
- .add(:gids, field: :gid)
78
- .add(:groupnames, field: :groupname)
79
- .add(:groups, field: :groups)
80
- .add(:homes, field: :home)
81
- .add(:shells, field: :shell)
82
- .add(:mindays, field: :mindays)
83
- .add(:maxdays, field: :maxdays)
84
- .add(:warndays, field: :warndays)
85
- .add(:disabled, field: :disabled)
86
- .add(:exists?) { |x| !x.entries.empty? }
87
- .add(:disabled?) { |x| x.where { disabled == false }.entries.empty? }
88
- .add(:enabled?) { |x| x.where { disabled == true }.entries.empty? }
89
- filter.connect(self, :collect_user_details)
90
-
91
- def to_s
92
- 'Users'
93
- end
94
-
95
- private
96
-
97
- # method to get all available users
98
- def list_users
99
- @username_cache ||= @user_provider.list_users unless @user_provider.nil?
100
- end
101
-
102
- # collects information about every user
103
- def collect_user_details
104
- @users_cache ||= @user_provider.collect_user_details unless @user_provider.nil?
105
- end
106
- end
107
-
108
- # The `user` resource handles the special case where only one resource is required
109
- #
110
- # describe user('root') do
111
- # it { should exist }
112
- # its('uid') { should eq 0 }
113
- # its('gid') { should eq 0 }
114
- # its('group') { should eq 'root' }
115
- # its('groups') { should eq ['root', 'wheel']}
116
- # its('home') { should eq '/root' }
117
- # its('shell') { should eq '/bin/bash' }
118
- # its('mindays') { should eq 0 }
119
- # its('maxdays') { should eq 99 }
120
- # its('warndays') { should eq 5 }
121
- # end
122
- #
123
- # The following Serverspec matchers are deprecated in favor for direct value access
124
- #
125
- # describe user('root') do
126
- # it { should belong_to_group 'root' }
127
- # it { should have_uid 0 }
128
- # it { should have_home_directory '/root' }
129
- # it { should have_login_shell '/bin/bash' }
130
- # its('minimum_days_between_password_change') { should eq 0 }
131
- # its('maximum_days_between_password_change') { should eq 99 }
132
- # end
133
- #
134
- # ServerSpec tests that are not supported:
135
- #
136
- # describe user('root') do
137
- # it { should have_authorized_key 'ssh-rsa ADg54...3434 user@example.local' }
138
- # its(:encrypted_password) { should eq 1234 }
139
- # end
140
- class User < Inspec.resource(1)
141
- include UserManagementSelector
142
- name 'user'
143
- supports platform: 'unix'
144
- supports platform: 'windows'
145
- desc 'Use the user InSpec audit resource to test user profiles, including the groups to which they belong, the frequency of required password changes, the directory paths to home and shell.'
146
- example "
147
- describe user('root') do
148
- it { should exist }
149
- its('uid') { should eq 1234 }
150
- its('gid') { should eq 1234 }
151
- end
152
- "
153
- def initialize(username = nil)
154
- @username = username
155
- # select user provider
156
- @user_provider = select_user_manager(inspec.os)
157
- return skip_resource 'The `user` resource is not supported on your OS yet.' if @user_provider.nil?
158
- end
159
-
160
- def exists?
161
- !identity.nil? && !identity[:username].nil?
162
- end
163
-
164
- def disabled?
165
- identity[:disabled] == true unless identity.nil?
166
- end
167
-
168
- def enabled?
169
- identity[:disabled] == false unless identity.nil?
170
- end
171
-
172
- def username
173
- identity[:username] unless identity.nil?
174
- end
175
-
176
- def uid
177
- identity[:uid] unless identity.nil?
178
- end
179
-
180
- def gid
181
- identity[:gid] unless identity.nil?
182
- end
183
-
184
- def groupname
185
- identity[:groupname] unless identity.nil?
186
- end
187
- alias group groupname
188
-
189
- def groups
190
- identity[:groups] unless identity.nil?
191
- end
192
-
193
- def home
194
- meta_info[:home] unless meta_info.nil?
195
- end
196
-
197
- def shell
198
- meta_info[:shell] unless meta_info.nil?
199
- end
200
-
201
- # returns the minimum days between password changes
202
- def mindays
203
- credentials[:mindays] unless credentials.nil?
204
- end
205
-
206
- # returns the maximum days between password changes
207
- def maxdays
208
- credentials[:maxdays] unless credentials.nil?
209
- end
210
-
211
- # returns the days for password change warning
212
- def warndays
213
- credentials[:warndays] unless credentials.nil?
214
- end
215
-
216
- # implement 'mindays' method to be compatible with serverspec
217
- def minimum_days_between_password_change
218
- deprecated('minimum_days_between_password_change', "Please use: its('mindays')")
219
- mindays
220
- end
221
-
222
- # implement 'maxdays' method to be compatible with serverspec
223
- def maximum_days_between_password_change
224
- deprecated('maximum_days_between_password_change', "Please use: its('maxdays')")
225
- maxdays
226
- end
227
-
228
- # implements rspec has matcher, to be compatible with serverspec
229
- # @see: https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/matchers/built_in/has.rb
230
- def has_uid?(compare_uid)
231
- deprecated('has_uid?')
232
- uid == compare_uid
233
- end
234
-
235
- def has_home_directory?(compare_home)
236
- deprecated('has_home_directory?', "Please use: its('home')")
237
- home == compare_home
238
- end
239
-
240
- def has_login_shell?(compare_shell)
241
- deprecated('has_login_shell?', "Please use: its('shell')")
242
- shell == compare_shell
243
- end
244
-
245
- def has_authorized_key?(_compare_key)
246
- deprecated('has_authorized_key?')
247
- raise NotImplementedError
248
- end
249
-
250
- def deprecated(name, alternative = nil)
251
- warn "[DEPRECATION] #{name} is deprecated. #{alternative}"
252
- end
253
-
254
- def to_s
255
- "User #{@username}"
256
- end
257
-
258
- private
259
-
260
- # returns the iden
261
- def identity
262
- return @id_cache if defined?(@id_cache)
263
- @id_cache = @user_provider.identity(@username) if !@user_provider.nil?
264
- end
265
-
266
- def meta_info
267
- return @meta_cache if defined?(@meta_cache)
268
- @meta_cache = @user_provider.meta_info(@username) if !@user_provider.nil?
269
- end
270
-
271
- def credentials
272
- return @cred_cache if defined?(@cred_cache)
273
- @cred_cache = @user_provider.credentials(@username) if !@user_provider.nil?
274
- end
275
- end
276
-
277
- # This is an abstract class that every user provoider has to implement.
278
- # A user provider implements a system abstracts and helps the InSpec resource
279
- # hand-over system specific behavior to those providers
280
- class UserInfo
281
- include Converter
282
-
283
- attr_reader :inspec
284
- def initialize(inspec)
285
- @inspec = inspec
286
- end
287
-
288
- # returns a hash with user-specific values:
289
- # {
290
- # uid: '',
291
- # user: '',
292
- # gid: '',
293
- # group: '',
294
- # groups: '',
295
- # }
296
- def identity(_username)
297
- raise 'user provider must implement the `identity` method'
298
- end
299
-
300
- # returns optional information about a user, eg shell
301
- def meta_info(_username)
302
- nil
303
- end
304
-
305
- # returns a hash with meta-data about user credentials
306
- # {
307
- # mindays: 1,
308
- # maxdays: 1,
309
- # warndays: 1,
310
- # }
311
- # this method is optional and may not be implemented by each provider
312
- def credentials(_username)
313
- nil
314
- end
315
-
316
- # returns an array with users
317
- def list_users
318
- raise 'user provider must implement the `list_users` method'
319
- end
320
-
321
- # retuns all aspects of the user as one hash
322
- def user_details(username)
323
- item = {}
324
- id = identity(username)
325
- item.merge!(id) unless id.nil?
326
- meta = meta_info(username)
327
- item.merge!(meta) unless meta.nil?
328
- cred = credentials(username)
329
- item.merge!(cred) unless cred.nil?
330
- item
331
- end
332
-
333
- # returns the full information list for a user
334
- def collect_user_details
335
- list_users.map { |username|
336
- user_details(username.chomp)
337
- }
338
- end
339
- end
340
-
341
- # implements generic unix id handling
342
- class UnixUser < UserInfo
343
- attr_reader :inspec, :id_cmd, :list_users_cmd
344
- def initialize(inspec)
345
- @inspec = inspec
346
- @id_cmd ||= 'id'
347
- @list_users_cmd ||= 'cut -d: -f1 /etc/passwd | grep -v "^#"'
348
- super
349
- end
350
-
351
- # returns a list of all local users on a system
352
- def list_users
353
- cmd = inspec.command(list_users_cmd)
354
- return [] if cmd.exit_status != 0
355
- cmd.stdout.chomp.lines
356
- end
357
-
358
- # parse one id entry like '0(wheel)''
359
- def parse_value(line)
360
- SimpleConfig.new(
361
- line,
362
- line_separator: ',',
363
- assignment_regex: /^\s*([^\(]*?)\s*\(\s*(.*?)\)*$/,
364
- group_re: nil,
365
- multiple_values: false,
366
- ).params
367
- end
368
-
369
- # extracts the identity
370
- def identity(username)
371
- cmd = inspec.command("#{id_cmd} #{username}")
372
- return nil if cmd.exit_status != 0
373
-
374
- # parse words
375
- params = SimpleConfig.new(
376
- parse_id_entries(cmd.stdout.chomp),
377
- assignment_regex: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
378
- group_re: nil,
379
- multiple_values: false,
380
- ).params
381
-
382
- {
383
- uid: convert_to_i(parse_value(params['uid']).keys[0]),
384
- username: parse_value(params['uid']).values[0],
385
- gid: convert_to_i(parse_value(params['gid']).keys[0]),
386
- groupname: parse_value(params['gid']).values[0],
387
- groups: parse_value(params['groups']).values,
388
- }
389
- end
390
-
391
- # splits the results of id into seperate lines
392
- def parse_id_entries(raw)
393
- data = []
394
- until (index = raw.index(/\)\s{1}/)).nil?
395
- data.push(raw[0, index+1]) # inclue closing )
396
- raw = raw[index+2, raw.length-index-2]
397
- end
398
- data.push(raw) if !raw.nil?
399
- data.join("\n")
400
- end
401
- end
402
-
403
- class LinuxUser < UnixUser
404
- include PasswdParser
405
- include CommentParser
406
-
407
- def meta_info(username)
408
- cmd = inspec.command("getent passwd #{username}")
409
- return nil if cmd.exit_status != 0
410
- # returns: root:x:0:0:root:/root:/bin/bash
411
- passwd = parse_passwd_line(cmd.stdout.chomp)
412
- {
413
- home: passwd['home'],
414
- shell: passwd['shell'],
415
- }
416
- end
417
-
418
- def credentials(username)
419
- cmd = inspec.command("chage -l #{username}")
420
- return nil if cmd.exit_status != 0
421
-
422
- params = SimpleConfig.new(
423
- cmd.stdout.chomp,
424
- assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
425
- group_re: nil,
426
- multiple_values: false,
427
- ).params
428
-
429
- {
430
- mindays: convert_to_i(params['Minimum number of days between password change']),
431
- maxdays: convert_to_i(params['Maximum number of days between password change']),
432
- warndays: convert_to_i(params['Number of days of warning before password expires']),
433
- }
434
- end
435
- end
436
-
437
- class SolarisUser < LinuxUser
438
- def initialize(inspec)
439
- @inspec = inspec
440
- @id_cmd ||= 'id -a'
441
- super
442
- end
443
- end
444
-
445
- class AixUser < UnixUser
446
- def identity(username)
447
- id = super(username)
448
- return nil if id.nil?
449
- # AIX 'id' command doesn't include the primary group in the supplementary
450
- # yet it can be somewhere in the supplementary list if someone added root
451
- # to a groups list in /etc/group
452
- # we rearrange to expected list if that is the case
453
- if id[:groups].first != id[:group]
454
- id[:groups].reject! { |i| i == id[:group] } if id[:groups].include?(id[:group])
455
- id[:groups].unshift(id[:group])
456
- end
457
-
458
- id
459
- end
460
-
461
- def meta_info(username)
462
- lsuser = inspec.command("lsuser -C -a home shell #{username}")
463
- return nil if lsuser.exit_status != 0
464
-
465
- user = lsuser.stdout.chomp.split("\n").last.split(':')
466
- {
467
- home: user[1],
468
- shell: user[2],
469
- }
470
- end
471
-
472
- def credentials(username)
473
- cmd = inspec.command(
474
- "lssec -c -f /etc/security/user -s #{username} -a minage -a maxage -a pwdwarntime",
475
- )
476
- return nil if cmd.exit_status != 0
477
-
478
- user_sec = cmd.stdout.chomp.split("\n").last.split(':')
479
-
480
- {
481
- mindays: user_sec[1].to_i * 7,
482
- maxdays: user_sec[2].to_i * 7,
483
- warndays: user_sec[3].to_i,
484
- }
485
- end
486
- end
487
-
488
- class HpuxUser < UnixUser
489
- def meta_info(username)
490
- hpuxuser = inspec.command("logins -x -l #{username}")
491
- return nil if hpuxuser.exit_status != 0
492
- user = hpuxuser.stdout.chomp.split(' ')
493
- {
494
- home: user[4],
495
- shell: user[5],
496
- }
497
- end
498
- end
499
-
500
- # we do not use 'finger' for MacOS, because it is harder to parse data with it
501
- # @see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/fingerd.8.html
502
- # instead we use 'dscl' to request user data
503
- # @see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/dscl.1.html
504
- # @see http://superuser.com/questions/592921/mac-osx-users-vs-dscl-command-to-list-user
505
- class DarwinUser < UnixUser
506
- def initialize(inspec)
507
- @list_users_cmd ||= 'dscl . list /Users'
508
- super
509
- end
510
-
511
- def meta_info(username)
512
- cmd = inspec.command("dscl -q . -read /Users/#{username} NFSHomeDirectory PrimaryGroupID RecordName UniqueID UserShell")
513
- return nil if cmd.exit_status != 0
514
-
515
- params = SimpleConfig.new(
516
- cmd.stdout.chomp,
517
- assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
518
- group_re: nil,
519
- multiple_values: false,
520
- ).params
521
-
522
- {
523
- home: params['NFSHomeDirectory'],
524
- shell: params['UserShell'],
525
- }
526
- end
527
- end
528
-
529
- # FreeBSD recommends to use the 'pw' command for user management
530
- # @see: https://www.freebsd.org/doc/handbook/users-synopsis.html
531
- # @see: https://www.freebsd.org/cgi/man.cgi?pw(8)
532
- # It offers the following commands:
533
- # - adduser(8) The recommended command-line application for adding new users.
534
- # - rmuser(8) The recommended command-line application for removing users.
535
- # - chpass(1) A flexible tool for changing user database information.
536
- # - passwd(1) The command-line tool to change user passwords.
537
- class FreeBSDUser < UnixUser
538
- include PasswdParser
539
-
540
- def meta_info(username)
541
- cmd = inspec.command("pw usershow #{username} -7")
542
- return nil if cmd.exit_status != 0
543
- # returns: root:*:0:0:Charlie &:/root:/bin/csh
544
- passwd = parse_passwd_line(cmd.stdout.chomp)
545
- {
546
- home: passwd['home'],
547
- shell: passwd['shell'],
548
- }
549
- end
550
- end
551
-
552
- # This optimization was inspired by
553
- # @see https://mcpmag.com/articles/2015/04/15/reporting-on-local-accounts.aspx
554
- # Alternative solutions are WMI Win32_UserAccount
555
- # @see https://msdn.microsoft.com/en-us/library/aa394507(v=vs.85).aspx
556
- # @see https://msdn.microsoft.com/en-us/library/aa394153(v=vs.85).aspx
557
- class WindowsUser < UserInfo
558
- def parse_windows_account(username)
559
- account = username.split('\\')
560
- name = account.pop
561
- domain = account.pop if !account.empty?
562
- [name, domain]
563
- end
564
-
565
- def identity(username)
566
- # TODO: we look for local users only at this point
567
- name, _domain = parse_windows_account(username)
568
- return if collect_user_details.nil?
569
- res = collect_user_details.select { |user| user[:username] == name }
570
- res[0] if !res.empty?
571
- end
572
-
573
- def list_users
574
- collect_user_details.map { |user| user[:username] }
575
- end
576
-
577
- # https://msdn.microsoft.com/en-us/library/aa746340(v=vs.85).aspx
578
- def collect_user_details # rubocop:disable Metrics/MethodLength
579
- return @users_cache if defined?(@users_cache)
580
- script = <<~EOH
581
- Function ConvertTo-SID { Param([byte[]]$BinarySID)
582
- (New-Object System.Security.Principal.SecurityIdentifier($BinarySID,0)).Value
583
- }
584
-
585
- Function Convert-UserFlag { Param ($UserFlag)
586
- $List = @()
587
- Switch ($UserFlag) {
588
- ($UserFlag -BOR 0x0001) { $List += 'SCRIPT' }
589
- ($UserFlag -BOR 0x0002) { $List += 'ACCOUNTDISABLE' }
590
- ($UserFlag -BOR 0x0008) { $List += 'HOMEDIR_REQUIRED' }
591
- ($UserFlag -BOR 0x0010) { $List += 'LOCKOUT' }
592
- ($UserFlag -BOR 0x0020) { $List += 'PASSWD_NOTREQD' }
593
- ($UserFlag -BOR 0x0040) { $List += 'PASSWD_CANT_CHANGE' }
594
- ($UserFlag -BOR 0x0080) { $List += 'ENCRYPTED_TEXT_PWD_ALLOWED' }
595
- ($UserFlag -BOR 0x0100) { $List += 'TEMP_DUPLICATE_ACCOUNT' }
596
- ($UserFlag -BOR 0x0200) { $List += 'NORMAL_ACCOUNT' }
597
- ($UserFlag -BOR 0x0800) { $List += 'INTERDOMAIN_TRUST_ACCOUNT' }
598
- ($UserFlag -BOR 0x1000) { $List += 'WORKSTATION_TRUST_ACCOUNT' }
599
- ($UserFlag -BOR 0x2000) { $List += 'SERVER_TRUST_ACCOUNT' }
600
- ($UserFlag -BOR 0x10000) { $List += 'DONT_EXPIRE_PASSWORD' }
601
- ($UserFlag -BOR 0x20000) { $List += 'MNS_LOGON_ACCOUNT' }
602
- ($UserFlag -BOR 0x40000) { $List += 'SMARTCARD_REQUIRED' }
603
- ($UserFlag -BOR 0x80000) { $List += 'TRUSTED_FOR_DELEGATION' }
604
- ($UserFlag -BOR 0x100000) { $List += 'NOT_DELEGATED' }
605
- ($UserFlag -BOR 0x200000) { $List += 'USE_DES_KEY_ONLY' }
606
- ($UserFlag -BOR 0x400000) { $List += 'DONT_REQ_PREAUTH' }
607
- ($UserFlag -BOR 0x800000) { $List += 'PASSWORD_EXPIRED' }
608
- ($UserFlag -BOR 0x1000000) { $List += 'TRUSTED_TO_AUTH_FOR_DELEGATION' }
609
- ($UserFlag -BOR 0x04000000) { $List += 'PARTIAL_SECRETS_ACCOUNT' }
610
- }
611
- $List
612
- }
613
-
614
- $Computername = $Env:Computername
615
- $adsi = [ADSI]"WinNT://$Computername"
616
- $adsi.Children | where {$_.SchemaClassName -eq 'user'} | ForEach {
617
- New-Object PSObject -property @{
618
- uid = ConvertTo-SID -BinarySID $_.ObjectSID[0]
619
- username = $_.Name[0]
620
- description = $_.Description[0]
621
- disabled = $_.AccountDisabled[0]
622
- userflags = Convert-UserFlag -UserFlag $_.UserFlags[0]
623
- passwordage = [math]::Round($_.PasswordAge[0]/86400)
624
- minpasswordlength = $_.MinPasswordLength[0]
625
- mindays = [math]::Round($_.MinPasswordAge[0]/86400)
626
- maxdays = [math]::Round($_.MaxPasswordAge[0]/86400)
627
- warndays = $null
628
- badpasswordattempts = $_.BadPasswordAttempts[0]
629
- maxbadpasswords = $_.MaxBadPasswordsAllowed[0]
630
- gid = $null
631
- group = $null
632
- groups = @($_.Groups() | Foreach-Object { $_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null) })
633
- home = $_.HomeDirectory[0]
634
- shell = $null
635
- domain = $Computername
636
- }
637
- } | ConvertTo-Json
638
- EOH
639
- cmd = inspec.powershell(script)
640
- # cannot rely on exit code for now, successful command returns exit code 1
641
- # return nil if cmd.exit_status != 0, try to parse json
642
- begin
643
- users = JSON.parse(cmd.stdout)
644
- rescue JSON::ParserError => _e
645
- return nil
646
- end
647
-
648
- # ensure we have an array of groups
649
- users = [users] if !users.is_a?(Array)
650
- # convert keys to symbols
651
- @users_cache = users.map { |user| user.each_with_object({}) { |(k, v), h| h[k.to_sym] = v } }
652
- end
653
- end
654
- end
1
+ # encoding: utf-8
2
+
3
+ require 'utils/parser'
4
+ require 'utils/convert'
5
+ require 'utils/filter'
6
+
7
+ module Inspec::Resources
8
+ # This file contains two resources, the `user` and `users` resource.
9
+ # The `user` resource is optimized for requests that verify specific users
10
+ # that you know upfront for testing. If you need to query all users or search
11
+ # specific users with certain properties, use the `users` resource.
12
+ module UserManagementSelector
13
+ # select user provider based on the operating system
14
+ # returns nil, if no user manager was found for the operating system
15
+ def select_user_manager(os)
16
+ if os.linux?
17
+ LinuxUser.new(inspec)
18
+ elsif os.windows?
19
+ WindowsUser.new(inspec)
20
+ elsif ['darwin'].include?(os[:family])
21
+ DarwinUser.new(inspec)
22
+ elsif ['freebsd'].include?(os[:family])
23
+ FreeBSDUser.new(inspec)
24
+ elsif ['aix'].include?(os[:family])
25
+ AixUser.new(inspec)
26
+ elsif os.solaris?
27
+ SolarisUser.new(inspec)
28
+ elsif ['hpux'].include?(os[:family])
29
+ HpuxUser.new(inspec)
30
+ end
31
+ end
32
+ end
33
+
34
+ # The InSpec users resources looksup all local users available on a system.
35
+ # TODO: the current version of the users resource will use eg. /etc/passwd
36
+ # on Linux to parse all usernames. Therefore the resource may not return
37
+ # users managed on other systems like LDAP/ActiveDirectory. Please open
38
+ # a feature request at https://github.com/chef/inspec if you need that
39
+ # functionality
40
+ #
41
+ # This resource allows complex filter mechanisms
42
+ #
43
+ # describe users.where(uid: 0).entries do
44
+ # it { should eq ['root'] }
45
+ # its('uids') { should eq [1234] }
46
+ # its('gids') { should eq [1234] }
47
+ # end
48
+ #
49
+ # describe users.where { uid =~ /S\-1\-5\-21\-\d+\-\d+\-\d+\-500/ } do
50
+ # it { should exist }
51
+ # end
52
+ class Users < Inspec.resource(1)
53
+ include UserManagementSelector
54
+
55
+ name 'users'
56
+ supports platform: 'unix'
57
+ supports platform: 'windows'
58
+ desc 'Use the users InSpec audit resource to test local user profiles. Users can be filtered by groups to which they belong, the frequency of required password changes, the directory paths to home and shell.'
59
+ example "
60
+ describe users.where { uid == 0 }.entries do
61
+ it { should eq ['root'] }
62
+ its('uids') { should eq [1234] }
63
+ its('gids') { should eq [1234] }
64
+ end
65
+ "
66
+ def initialize
67
+ # select user provider
68
+ @user_provider = select_user_manager(inspec.os)
69
+ return skip_resource 'The `users` resource is not supported on your OS yet.' if @user_provider.nil?
70
+ end
71
+
72
+ filter = FilterTable.create
73
+ filter.add_accessor(:where)
74
+ .add_accessor(:entries)
75
+ .add(:usernames, field: :username)
76
+ .add(:uids, field: :uid)
77
+ .add(:gids, field: :gid)
78
+ .add(:groupnames, field: :groupname)
79
+ .add(:groups, field: :groups)
80
+ .add(:homes, field: :home)
81
+ .add(:shells, field: :shell)
82
+ .add(:mindays, field: :mindays)
83
+ .add(:maxdays, field: :maxdays)
84
+ .add(:warndays, field: :warndays)
85
+ .add(:disabled, field: :disabled)
86
+ .add(:exists?) { |x| !x.entries.empty? }
87
+ .add(:disabled?) { |x| x.where { disabled == false }.entries.empty? }
88
+ .add(:enabled?) { |x| x.where { disabled == true }.entries.empty? }
89
+ filter.connect(self, :collect_user_details)
90
+
91
+ def to_s
92
+ 'Users'
93
+ end
94
+
95
+ private
96
+
97
+ # method to get all available users
98
+ def list_users
99
+ @username_cache ||= @user_provider.list_users unless @user_provider.nil?
100
+ end
101
+
102
+ # collects information about every user
103
+ def collect_user_details
104
+ @users_cache ||= @user_provider.collect_user_details unless @user_provider.nil?
105
+ end
106
+ end
107
+
108
+ # The `user` resource handles the special case where only one resource is required
109
+ #
110
+ # describe user('root') do
111
+ # it { should exist }
112
+ # its('uid') { should eq 0 }
113
+ # its('gid') { should eq 0 }
114
+ # its('group') { should eq 'root' }
115
+ # its('groups') { should eq ['root', 'wheel']}
116
+ # its('home') { should eq '/root' }
117
+ # its('shell') { should eq '/bin/bash' }
118
+ # its('mindays') { should eq 0 }
119
+ # its('maxdays') { should eq 99 }
120
+ # its('warndays') { should eq 5 }
121
+ # end
122
+ #
123
+ # The following Serverspec matchers are deprecated in favor for direct value access
124
+ #
125
+ # describe user('root') do
126
+ # it { should belong_to_group 'root' }
127
+ # it { should have_uid 0 }
128
+ # it { should have_home_directory '/root' }
129
+ # it { should have_login_shell '/bin/bash' }
130
+ # its('minimum_days_between_password_change') { should eq 0 }
131
+ # its('maximum_days_between_password_change') { should eq 99 }
132
+ # end
133
+ #
134
+ # ServerSpec tests that are not supported:
135
+ #
136
+ # describe user('root') do
137
+ # it { should have_authorized_key 'ssh-rsa ADg54...3434 user@example.local' }
138
+ # its(:encrypted_password) { should eq 1234 }
139
+ # end
140
+ class User < Inspec.resource(1)
141
+ include UserManagementSelector
142
+ name 'user'
143
+ supports platform: 'unix'
144
+ supports platform: 'windows'
145
+ desc 'Use the user InSpec audit resource to test user profiles, including the groups to which they belong, the frequency of required password changes, the directory paths to home and shell.'
146
+ example "
147
+ describe user('root') do
148
+ it { should exist }
149
+ its('uid') { should eq 1234 }
150
+ its('gid') { should eq 1234 }
151
+ end
152
+ "
153
+ def initialize(username = nil)
154
+ @username = username
155
+ # select user provider
156
+ @user_provider = select_user_manager(inspec.os)
157
+ return skip_resource 'The `user` resource is not supported on your OS yet.' if @user_provider.nil?
158
+ end
159
+
160
+ def exists?
161
+ !identity.nil? && !identity[:username].nil?
162
+ end
163
+
164
+ def disabled?
165
+ identity[:disabled] == true unless identity.nil?
166
+ end
167
+
168
+ def enabled?
169
+ identity[:disabled] == false unless identity.nil?
170
+ end
171
+
172
+ def username
173
+ identity[:username] unless identity.nil?
174
+ end
175
+
176
+ def uid
177
+ identity[:uid] unless identity.nil?
178
+ end
179
+
180
+ def gid
181
+ identity[:gid] unless identity.nil?
182
+ end
183
+
184
+ def groupname
185
+ identity[:groupname] unless identity.nil?
186
+ end
187
+ alias group groupname
188
+
189
+ def groups
190
+ identity[:groups] unless identity.nil?
191
+ end
192
+
193
+ def home
194
+ meta_info[:home] unless meta_info.nil?
195
+ end
196
+
197
+ def shell
198
+ meta_info[:shell] unless meta_info.nil?
199
+ end
200
+
201
+ # returns the minimum days between password changes
202
+ def mindays
203
+ credentials[:mindays] unless credentials.nil?
204
+ end
205
+
206
+ # returns the maximum days between password changes
207
+ def maxdays
208
+ credentials[:maxdays] unless credentials.nil?
209
+ end
210
+
211
+ # returns the days for password change warning
212
+ def warndays
213
+ credentials[:warndays] unless credentials.nil?
214
+ end
215
+
216
+ # implement 'mindays' method to be compatible with serverspec
217
+ def minimum_days_between_password_change
218
+ deprecated('minimum_days_between_password_change', "Please use: its('mindays')")
219
+ mindays
220
+ end
221
+
222
+ # implement 'maxdays' method to be compatible with serverspec
223
+ def maximum_days_between_password_change
224
+ deprecated('maximum_days_between_password_change', "Please use: its('maxdays')")
225
+ maxdays
226
+ end
227
+
228
+ # implements rspec has matcher, to be compatible with serverspec
229
+ # @see: https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/matchers/built_in/has.rb
230
+ def has_uid?(compare_uid)
231
+ deprecated('has_uid?')
232
+ uid == compare_uid
233
+ end
234
+
235
+ def has_home_directory?(compare_home)
236
+ deprecated('has_home_directory?', "Please use: its('home')")
237
+ home == compare_home
238
+ end
239
+
240
+ def has_login_shell?(compare_shell)
241
+ deprecated('has_login_shell?', "Please use: its('shell')")
242
+ shell == compare_shell
243
+ end
244
+
245
+ def has_authorized_key?(_compare_key)
246
+ deprecated('has_authorized_key?')
247
+ raise NotImplementedError
248
+ end
249
+
250
+ def deprecated(name, alternative = nil)
251
+ warn "[DEPRECATION] #{name} is deprecated. #{alternative}"
252
+ end
253
+
254
+ def to_s
255
+ "User #{@username}"
256
+ end
257
+
258
+ private
259
+
260
+ # returns the iden
261
+ def identity
262
+ return @id_cache if defined?(@id_cache)
263
+ @id_cache = @user_provider.identity(@username) if !@user_provider.nil?
264
+ end
265
+
266
+ def meta_info
267
+ return @meta_cache if defined?(@meta_cache)
268
+ @meta_cache = @user_provider.meta_info(@username) if !@user_provider.nil?
269
+ end
270
+
271
+ def credentials
272
+ return @cred_cache if defined?(@cred_cache)
273
+ @cred_cache = @user_provider.credentials(@username) if !@user_provider.nil?
274
+ end
275
+ end
276
+
277
+ # This is an abstract class that every user provoider has to implement.
278
+ # A user provider implements a system abstracts and helps the InSpec resource
279
+ # hand-over system specific behavior to those providers
280
+ class UserInfo
281
+ include Converter
282
+
283
+ attr_reader :inspec
284
+ def initialize(inspec)
285
+ @inspec = inspec
286
+ end
287
+
288
+ # returns a hash with user-specific values:
289
+ # {
290
+ # uid: '',
291
+ # user: '',
292
+ # gid: '',
293
+ # group: '',
294
+ # groups: '',
295
+ # }
296
+ def identity(_username)
297
+ raise 'user provider must implement the `identity` method'
298
+ end
299
+
300
+ # returns optional information about a user, eg shell
301
+ def meta_info(_username)
302
+ nil
303
+ end
304
+
305
+ # returns a hash with meta-data about user credentials
306
+ # {
307
+ # mindays: 1,
308
+ # maxdays: 1,
309
+ # warndays: 1,
310
+ # }
311
+ # this method is optional and may not be implemented by each provider
312
+ def credentials(_username)
313
+ nil
314
+ end
315
+
316
+ # returns an array with users
317
+ def list_users
318
+ raise 'user provider must implement the `list_users` method'
319
+ end
320
+
321
+ # retuns all aspects of the user as one hash
322
+ def user_details(username)
323
+ item = {}
324
+ id = identity(username)
325
+ item.merge!(id) unless id.nil?
326
+ meta = meta_info(username)
327
+ item.merge!(meta) unless meta.nil?
328
+ cred = credentials(username)
329
+ item.merge!(cred) unless cred.nil?
330
+ item
331
+ end
332
+
333
+ # returns the full information list for a user
334
+ def collect_user_details
335
+ list_users.map { |username|
336
+ user_details(username.chomp)
337
+ }
338
+ end
339
+ end
340
+
341
+ # implements generic unix id handling
342
+ class UnixUser < UserInfo
343
+ attr_reader :inspec, :id_cmd, :list_users_cmd
344
+ def initialize(inspec)
345
+ @inspec = inspec
346
+ @id_cmd ||= 'id'
347
+ @list_users_cmd ||= 'cut -d: -f1 /etc/passwd | grep -v "^#"'
348
+ super
349
+ end
350
+
351
+ # returns a list of all local users on a system
352
+ def list_users
353
+ cmd = inspec.command(list_users_cmd)
354
+ return [] if cmd.exit_status != 0
355
+ cmd.stdout.chomp.lines
356
+ end
357
+
358
+ # parse one id entry like '0(wheel)''
359
+ def parse_value(line)
360
+ SimpleConfig.new(
361
+ line,
362
+ line_separator: ',',
363
+ assignment_regex: /^\s*([^\(]*?)\s*\(\s*(.*?)\)*$/,
364
+ group_re: nil,
365
+ multiple_values: false,
366
+ ).params
367
+ end
368
+
369
+ # extracts the identity
370
+ def identity(username)
371
+ cmd = inspec.command("#{id_cmd} #{username}")
372
+ return nil if cmd.exit_status != 0
373
+
374
+ # parse words
375
+ params = SimpleConfig.new(
376
+ parse_id_entries(cmd.stdout.chomp),
377
+ assignment_regex: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
378
+ group_re: nil,
379
+ multiple_values: false,
380
+ ).params
381
+
382
+ {
383
+ uid: convert_to_i(parse_value(params['uid']).keys[0]),
384
+ username: parse_value(params['uid']).values[0],
385
+ gid: convert_to_i(parse_value(params['gid']).keys[0]),
386
+ groupname: parse_value(params['gid']).values[0],
387
+ groups: parse_value(params['groups']).values,
388
+ }
389
+ end
390
+
391
+ # splits the results of id into seperate lines
392
+ def parse_id_entries(raw)
393
+ data = []
394
+ until (index = raw.index(/\)\s{1}/)).nil?
395
+ data.push(raw[0, index+1]) # inclue closing )
396
+ raw = raw[index+2, raw.length-index-2]
397
+ end
398
+ data.push(raw) if !raw.nil?
399
+ data.join("\n")
400
+ end
401
+ end
402
+
403
+ class LinuxUser < UnixUser
404
+ include PasswdParser
405
+ include CommentParser
406
+
407
+ def meta_info(username)
408
+ cmd = inspec.command("getent passwd #{username}")
409
+ return nil if cmd.exit_status != 0
410
+ # returns: root:x:0:0:root:/root:/bin/bash
411
+ passwd = parse_passwd_line(cmd.stdout.chomp)
412
+ {
413
+ home: passwd['home'],
414
+ shell: passwd['shell'],
415
+ }
416
+ end
417
+
418
+ def credentials(username)
419
+ cmd = inspec.command("chage -l #{username}")
420
+ return nil if cmd.exit_status != 0
421
+
422
+ params = SimpleConfig.new(
423
+ cmd.stdout.chomp,
424
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
425
+ group_re: nil,
426
+ multiple_values: false,
427
+ ).params
428
+
429
+ {
430
+ mindays: convert_to_i(params['Minimum number of days between password change']),
431
+ maxdays: convert_to_i(params['Maximum number of days between password change']),
432
+ warndays: convert_to_i(params['Number of days of warning before password expires']),
433
+ }
434
+ end
435
+ end
436
+
437
+ class SolarisUser < LinuxUser
438
+ def initialize(inspec)
439
+ @inspec = inspec
440
+ @id_cmd ||= 'id -a'
441
+ super
442
+ end
443
+ end
444
+
445
+ class AixUser < UnixUser
446
+ def identity(username)
447
+ id = super(username)
448
+ return nil if id.nil?
449
+ # AIX 'id' command doesn't include the primary group in the supplementary
450
+ # yet it can be somewhere in the supplementary list if someone added root
451
+ # to a groups list in /etc/group
452
+ # we rearrange to expected list if that is the case
453
+ if id[:groups].first != id[:group]
454
+ id[:groups].reject! { |i| i == id[:group] } if id[:groups].include?(id[:group])
455
+ id[:groups].unshift(id[:group])
456
+ end
457
+
458
+ id
459
+ end
460
+
461
+ def meta_info(username)
462
+ lsuser = inspec.command("lsuser -C -a home shell #{username}")
463
+ return nil if lsuser.exit_status != 0
464
+
465
+ user = lsuser.stdout.chomp.split("\n").last.split(':')
466
+ {
467
+ home: user[1],
468
+ shell: user[2],
469
+ }
470
+ end
471
+
472
+ def credentials(username)
473
+ cmd = inspec.command(
474
+ "lssec -c -f /etc/security/user -s #{username} -a minage -a maxage -a pwdwarntime",
475
+ )
476
+ return nil if cmd.exit_status != 0
477
+
478
+ user_sec = cmd.stdout.chomp.split("\n").last.split(':')
479
+
480
+ {
481
+ mindays: user_sec[1].to_i * 7,
482
+ maxdays: user_sec[2].to_i * 7,
483
+ warndays: user_sec[3].to_i,
484
+ }
485
+ end
486
+ end
487
+
488
+ class HpuxUser < UnixUser
489
+ def meta_info(username)
490
+ hpuxuser = inspec.command("logins -x -l #{username}")
491
+ return nil if hpuxuser.exit_status != 0
492
+ user = hpuxuser.stdout.chomp.split(' ')
493
+ {
494
+ home: user[4],
495
+ shell: user[5],
496
+ }
497
+ end
498
+ end
499
+
500
+ # we do not use 'finger' for MacOS, because it is harder to parse data with it
501
+ # @see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/fingerd.8.html
502
+ # instead we use 'dscl' to request user data
503
+ # @see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/dscl.1.html
504
+ # @see http://superuser.com/questions/592921/mac-osx-users-vs-dscl-command-to-list-user
505
+ class DarwinUser < UnixUser
506
+ def initialize(inspec)
507
+ @list_users_cmd ||= 'dscl . list /Users'
508
+ super
509
+ end
510
+
511
+ def meta_info(username)
512
+ cmd = inspec.command("dscl -q . -read /Users/#{username} NFSHomeDirectory PrimaryGroupID RecordName UniqueID UserShell")
513
+ return nil if cmd.exit_status != 0
514
+
515
+ params = SimpleConfig.new(
516
+ cmd.stdout.chomp,
517
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
518
+ group_re: nil,
519
+ multiple_values: false,
520
+ ).params
521
+
522
+ {
523
+ home: params['NFSHomeDirectory'],
524
+ shell: params['UserShell'],
525
+ }
526
+ end
527
+ end
528
+
529
+ # FreeBSD recommends to use the 'pw' command for user management
530
+ # @see: https://www.freebsd.org/doc/handbook/users-synopsis.html
531
+ # @see: https://www.freebsd.org/cgi/man.cgi?pw(8)
532
+ # It offers the following commands:
533
+ # - adduser(8) The recommended command-line application for adding new users.
534
+ # - rmuser(8) The recommended command-line application for removing users.
535
+ # - chpass(1) A flexible tool for changing user database information.
536
+ # - passwd(1) The command-line tool to change user passwords.
537
+ class FreeBSDUser < UnixUser
538
+ include PasswdParser
539
+
540
+ def meta_info(username)
541
+ cmd = inspec.command("pw usershow #{username} -7")
542
+ return nil if cmd.exit_status != 0
543
+ # returns: root:*:0:0:Charlie &:/root:/bin/csh
544
+ passwd = parse_passwd_line(cmd.stdout.chomp)
545
+ {
546
+ home: passwd['home'],
547
+ shell: passwd['shell'],
548
+ }
549
+ end
550
+ end
551
+
552
+ # This optimization was inspired by
553
+ # @see https://mcpmag.com/articles/2015/04/15/reporting-on-local-accounts.aspx
554
+ # Alternative solutions are WMI Win32_UserAccount
555
+ # @see https://msdn.microsoft.com/en-us/library/aa394507(v=vs.85).aspx
556
+ # @see https://msdn.microsoft.com/en-us/library/aa394153(v=vs.85).aspx
557
+ class WindowsUser < UserInfo
558
+ def parse_windows_account(username)
559
+ account = username.split('\\')
560
+ name = account.pop
561
+ domain = account.pop if !account.empty?
562
+ [name, domain]
563
+ end
564
+
565
+ def identity(username)
566
+ # TODO: we look for local users only at this point
567
+ name, _domain = parse_windows_account(username)
568
+ return if collect_user_details.nil?
569
+ res = collect_user_details.select { |user| user[:username] == name }
570
+ res[0] if !res.empty?
571
+ end
572
+
573
+ def list_users
574
+ collect_user_details.map { |user| user[:username] }
575
+ end
576
+
577
+ # https://msdn.microsoft.com/en-us/library/aa746340(v=vs.85).aspx
578
+ def collect_user_details # rubocop:disable Metrics/MethodLength
579
+ return @users_cache if defined?(@users_cache)
580
+ script = <<~EOH
581
+ Function ConvertTo-SID { Param([byte[]]$BinarySID)
582
+ (New-Object System.Security.Principal.SecurityIdentifier($BinarySID,0)).Value
583
+ }
584
+
585
+ Function Convert-UserFlag { Param ($UserFlag)
586
+ $List = @()
587
+ Switch ($UserFlag) {
588
+ ($UserFlag -BOR 0x0001) { $List += 'SCRIPT' }
589
+ ($UserFlag -BOR 0x0002) { $List += 'ACCOUNTDISABLE' }
590
+ ($UserFlag -BOR 0x0008) { $List += 'HOMEDIR_REQUIRED' }
591
+ ($UserFlag -BOR 0x0010) { $List += 'LOCKOUT' }
592
+ ($UserFlag -BOR 0x0020) { $List += 'PASSWD_NOTREQD' }
593
+ ($UserFlag -BOR 0x0040) { $List += 'PASSWD_CANT_CHANGE' }
594
+ ($UserFlag -BOR 0x0080) { $List += 'ENCRYPTED_TEXT_PWD_ALLOWED' }
595
+ ($UserFlag -BOR 0x0100) { $List += 'TEMP_DUPLICATE_ACCOUNT' }
596
+ ($UserFlag -BOR 0x0200) { $List += 'NORMAL_ACCOUNT' }
597
+ ($UserFlag -BOR 0x0800) { $List += 'INTERDOMAIN_TRUST_ACCOUNT' }
598
+ ($UserFlag -BOR 0x1000) { $List += 'WORKSTATION_TRUST_ACCOUNT' }
599
+ ($UserFlag -BOR 0x2000) { $List += 'SERVER_TRUST_ACCOUNT' }
600
+ ($UserFlag -BOR 0x10000) { $List += 'DONT_EXPIRE_PASSWORD' }
601
+ ($UserFlag -BOR 0x20000) { $List += 'MNS_LOGON_ACCOUNT' }
602
+ ($UserFlag -BOR 0x40000) { $List += 'SMARTCARD_REQUIRED' }
603
+ ($UserFlag -BOR 0x80000) { $List += 'TRUSTED_FOR_DELEGATION' }
604
+ ($UserFlag -BOR 0x100000) { $List += 'NOT_DELEGATED' }
605
+ ($UserFlag -BOR 0x200000) { $List += 'USE_DES_KEY_ONLY' }
606
+ ($UserFlag -BOR 0x400000) { $List += 'DONT_REQ_PREAUTH' }
607
+ ($UserFlag -BOR 0x800000) { $List += 'PASSWORD_EXPIRED' }
608
+ ($UserFlag -BOR 0x1000000) { $List += 'TRUSTED_TO_AUTH_FOR_DELEGATION' }
609
+ ($UserFlag -BOR 0x04000000) { $List += 'PARTIAL_SECRETS_ACCOUNT' }
610
+ }
611
+ $List
612
+ }
613
+
614
+ $Computername = $Env:Computername
615
+ $adsi = [ADSI]"WinNT://$Computername"
616
+ $adsi.Children | where {$_.SchemaClassName -eq 'user'} | ForEach {
617
+ New-Object PSObject -property @{
618
+ uid = ConvertTo-SID -BinarySID $_.ObjectSID[0]
619
+ username = $_.Name[0]
620
+ description = $_.Description[0]
621
+ disabled = $_.AccountDisabled[0]
622
+ userflags = Convert-UserFlag -UserFlag $_.UserFlags[0]
623
+ passwordage = [math]::Round($_.PasswordAge[0]/86400)
624
+ minpasswordlength = $_.MinPasswordLength[0]
625
+ mindays = [math]::Round($_.MinPasswordAge[0]/86400)
626
+ maxdays = [math]::Round($_.MaxPasswordAge[0]/86400)
627
+ warndays = $null
628
+ badpasswordattempts = $_.BadPasswordAttempts[0]
629
+ maxbadpasswords = $_.MaxBadPasswordsAllowed[0]
630
+ gid = $null
631
+ group = $null
632
+ groups = @($_.Groups() | Foreach-Object { $_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null) })
633
+ home = $_.HomeDirectory[0]
634
+ shell = $null
635
+ domain = $Computername
636
+ }
637
+ } | ConvertTo-Json
638
+ EOH
639
+ cmd = inspec.powershell(script)
640
+ # cannot rely on exit code for now, successful command returns exit code 1
641
+ # return nil if cmd.exit_status != 0, try to parse json
642
+ begin
643
+ users = JSON.parse(cmd.stdout)
644
+ rescue JSON::ParserError => _e
645
+ return nil
646
+ end
647
+
648
+ # ensure we have an array of groups
649
+ users = [users] if !users.is_a?(Array)
650
+ # convert keys to symbols
651
+ @users_cache = users.map { |user| user.each_with_object({}) { |(k, v), h| h[k.to_sym] = v } }
652
+ end
653
+ end
654
+ end