inspec 2.1.21 → 2.1.30

Sign up to get free protection for your applications and to get access to all the features.
Files changed (502) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +101 -101
  3. data/CHANGELOG.md +3062 -3045
  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 +447 -447
  9. data/Rakefile +322 -322
  10. data/bin/inspec +12 -12
  11. data/docs/.gitignore +2 -2
  12. data/docs/README.md +40 -40
  13. data/docs/dsl_inspec.md +258 -258
  14. data/docs/dsl_resource.md +100 -100
  15. data/docs/glossary.md +99 -99
  16. data/docs/habitat.md +191 -191
  17. data/docs/inspec_and_friends.md +114 -114
  18. data/docs/matchers.md +169 -169
  19. data/docs/migration.md +293 -293
  20. data/docs/platforms.md +118 -118
  21. data/docs/plugin_kitchen_inspec.md +50 -50
  22. data/docs/profiles.md +376 -376
  23. data/docs/reporters.md +105 -105
  24. data/docs/resources/aide_conf.md.erb +75 -75
  25. data/docs/resources/apache.md.erb +67 -67
  26. data/docs/resources/apache_conf.md.erb +68 -68
  27. data/docs/resources/apt.md.erb +71 -71
  28. data/docs/resources/audit_policy.md.erb +47 -47
  29. data/docs/resources/auditd.md.erb +79 -79
  30. data/docs/resources/auditd_conf.md.erb +68 -68
  31. data/docs/resources/aws_cloudtrail_trail.md.erb +140 -140
  32. data/docs/resources/aws_cloudtrail_trails.md.erb +81 -81
  33. data/docs/resources/aws_cloudwatch_alarm.md.erb +86 -86
  34. data/docs/resources/aws_cloudwatch_log_metric_filter.md.erb +151 -151
  35. data/docs/resources/aws_config_delivery_channel.md +79 -79
  36. data/docs/resources/aws_config_recorder.md.erb +71 -71
  37. data/docs/resources/aws_ec2_instance.md.erb +106 -106
  38. data/docs/resources/aws_iam_access_key.md.erb +123 -123
  39. data/docs/resources/aws_iam_access_keys.md.erb +198 -198
  40. data/docs/resources/aws_iam_group.md.erb +46 -46
  41. data/docs/resources/aws_iam_groups.md.erb +43 -43
  42. data/docs/resources/aws_iam_password_policy.md.erb +76 -76
  43. data/docs/resources/aws_iam_policies.md.erb +82 -82
  44. data/docs/resources/aws_iam_policy.md.erb +144 -144
  45. data/docs/resources/aws_iam_role.md.erb +63 -63
  46. data/docs/resources/aws_iam_root_user.md.erb +70 -58
  47. data/docs/resources/aws_iam_user.md.erb +64 -64
  48. data/docs/resources/aws_iam_users.md.erb +89 -89
  49. data/docs/resources/aws_kms_key.md.erb +171 -171
  50. data/docs/resources/aws_kms_keys.md.erb +84 -84
  51. data/docs/resources/aws_rds_instance.md.erb +60 -60
  52. data/docs/resources/aws_route_table.md.erb +47 -47
  53. data/docs/resources/aws_route_tables.md.erb +49 -0
  54. data/docs/resources/aws_s3_bucket.md.erb +134 -134
  55. data/docs/resources/aws_s3_bucket_object.md.erb +83 -83
  56. data/docs/resources/aws_s3_buckets.md.erb +53 -0
  57. data/docs/resources/aws_security_group.md.erb +151 -151
  58. data/docs/resources/aws_security_groups.md.erb +91 -91
  59. data/docs/resources/aws_sns_subscription.md.erb +124 -124
  60. data/docs/resources/aws_sns_topic.md.erb +63 -63
  61. data/docs/resources/aws_sns_topics.md.erb +52 -52
  62. data/docs/resources/aws_subnet.md.erb +134 -134
  63. data/docs/resources/aws_subnets.md.erb +126 -126
  64. data/docs/resources/aws_vpc.md.erb +120 -120
  65. data/docs/resources/aws_vpcs.md.erb +48 -48
  66. data/docs/resources/azure_generic_resource.md.erb +171 -171
  67. data/docs/resources/azure_resource_group.md.erb +284 -284
  68. data/docs/resources/azure_virtual_machine.md.erb +347 -347
  69. data/docs/resources/azure_virtual_machine_data_disk.md.erb +224 -224
  70. data/docs/resources/bash.md.erb +75 -75
  71. data/docs/resources/bond.md.erb +90 -90
  72. data/docs/resources/bridge.md.erb +57 -57
  73. data/docs/resources/bsd_service.md.erb +67 -67
  74. data/docs/resources/chocolatey_package.md.erb +58 -0
  75. data/docs/resources/command.md.erb +138 -138
  76. data/docs/resources/cpan.md.erb +79 -79
  77. data/docs/resources/cran.md.erb +64 -64
  78. data/docs/resources/crontab.md.erb +89 -89
  79. data/docs/resources/csv.md.erb +54 -54
  80. data/docs/resources/dh_params.md.erb +205 -205
  81. data/docs/resources/directory.md.erb +30 -30
  82. data/docs/resources/docker.md.erb +219 -219
  83. data/docs/resources/docker_container.md.erb +103 -103
  84. data/docs/resources/docker_image.md.erb +94 -94
  85. data/docs/resources/docker_service.md.erb +114 -114
  86. data/docs/resources/elasticsearch.md.erb +242 -242
  87. data/docs/resources/etc_fstab.md.erb +125 -125
  88. data/docs/resources/etc_group.md.erb +75 -75
  89. data/docs/resources/etc_hosts.md.erb +78 -78
  90. data/docs/resources/etc_hosts_allow.md.erb +74 -74
  91. data/docs/resources/etc_hosts_deny.md.erb +74 -74
  92. data/docs/resources/file.md.erb +526 -526
  93. data/docs/resources/filesystem.md.erb +41 -41
  94. data/docs/resources/firewalld.md.erb +107 -107
  95. data/docs/resources/gem.md.erb +79 -79
  96. data/docs/resources/group.md.erb +61 -61
  97. data/docs/resources/grub_conf.md.erb +101 -101
  98. data/docs/resources/host.md.erb +86 -86
  99. data/docs/resources/http.md.erb +196 -196
  100. data/docs/resources/iis_app.md.erb +122 -122
  101. data/docs/resources/iis_site.md.erb +135 -135
  102. data/docs/resources/inetd_conf.md.erb +94 -94
  103. data/docs/resources/ini.md.erb +76 -76
  104. data/docs/resources/interface.md.erb +58 -58
  105. data/docs/resources/iptables.md.erb +64 -64
  106. data/docs/resources/json.md.erb +63 -63
  107. data/docs/resources/kernel_module.md.erb +120 -120
  108. data/docs/resources/kernel_parameter.md.erb +53 -53
  109. data/docs/resources/key_rsa.md.erb +85 -85
  110. data/docs/resources/launchd_service.md.erb +57 -57
  111. data/docs/resources/limits_conf.md.erb +75 -75
  112. data/docs/resources/login_defs.md.erb +71 -71
  113. data/docs/resources/mount.md.erb +69 -69
  114. data/docs/resources/mssql_session.md.erb +60 -60
  115. data/docs/resources/mysql_conf.md.erb +99 -99
  116. data/docs/resources/mysql_session.md.erb +74 -74
  117. data/docs/resources/nginx.md.erb +79 -79
  118. data/docs/resources/nginx_conf.md.erb +138 -138
  119. data/docs/resources/npm.md.erb +60 -60
  120. data/docs/resources/ntp_conf.md.erb +60 -60
  121. data/docs/resources/oneget.md.erb +53 -53
  122. data/docs/resources/oracledb_session.md.erb +52 -52
  123. data/docs/resources/os.md.erb +141 -141
  124. data/docs/resources/os_env.md.erb +78 -78
  125. data/docs/resources/package.md.erb +120 -120
  126. data/docs/resources/packages.md.erb +67 -67
  127. data/docs/resources/parse_config.md.erb +103 -103
  128. data/docs/resources/parse_config_file.md.erb +138 -138
  129. data/docs/resources/passwd.md.erb +141 -141
  130. data/docs/resources/pip.md.erb +67 -67
  131. data/docs/resources/port.md.erb +137 -137
  132. data/docs/resources/postgres_conf.md.erb +79 -79
  133. data/docs/resources/postgres_hba_conf.md.erb +93 -93
  134. data/docs/resources/postgres_ident_conf.md.erb +76 -76
  135. data/docs/resources/postgres_session.md.erb +69 -69
  136. data/docs/resources/powershell.md.erb +102 -102
  137. data/docs/resources/processes.md.erb +109 -109
  138. data/docs/resources/rabbitmq_config.md.erb +41 -41
  139. data/docs/resources/registry_key.md.erb +158 -158
  140. data/docs/resources/runit_service.md.erb +57 -57
  141. data/docs/resources/security_policy.md.erb +47 -47
  142. data/docs/resources/service.md.erb +121 -121
  143. data/docs/resources/shadow.md.erb +146 -146
  144. data/docs/resources/ssh_config.md.erb +73 -73
  145. data/docs/resources/sshd_config.md.erb +83 -83
  146. data/docs/resources/ssl.md.erb +119 -119
  147. data/docs/resources/sys_info.md.erb +42 -42
  148. data/docs/resources/systemd_service.md.erb +57 -57
  149. data/docs/resources/sysv_service.md.erb +57 -57
  150. data/docs/resources/upstart_service.md.erb +57 -57
  151. data/docs/resources/user.md.erb +140 -140
  152. data/docs/resources/users.md.erb +127 -127
  153. data/docs/resources/vbscript.md.erb +55 -55
  154. data/docs/resources/virtualization.md.erb +57 -57
  155. data/docs/resources/windows_feature.md.erb +47 -47
  156. data/docs/resources/windows_hotfix.md.erb +53 -53
  157. data/docs/resources/windows_task.md.erb +95 -95
  158. data/docs/resources/wmi.md.erb +81 -81
  159. data/docs/resources/x509_certificate.md.erb +151 -151
  160. data/docs/resources/xinetd_conf.md.erb +156 -156
  161. data/docs/resources/xml.md.erb +85 -85
  162. data/docs/resources/yaml.md.erb +69 -69
  163. data/docs/resources/yum.md.erb +98 -98
  164. data/docs/resources/zfs_dataset.md.erb +53 -53
  165. data/docs/resources/zfs_pool.md.erb +47 -47
  166. data/docs/ruby_usage.md +203 -203
  167. data/docs/shared/matcher_be.md.erb +1 -1
  168. data/docs/shared/matcher_cmp.md.erb +43 -43
  169. data/docs/shared/matcher_eq.md.erb +3 -3
  170. data/docs/shared/matcher_include.md.erb +1 -1
  171. data/docs/shared/matcher_match.md.erb +1 -1
  172. data/docs/shell.md +217 -217
  173. data/examples/README.md +8 -8
  174. data/examples/inheritance/README.md +65 -65
  175. data/examples/inheritance/controls/example.rb +14 -14
  176. data/examples/inheritance/inspec.yml +15 -15
  177. data/examples/kitchen-ansible/.kitchen.yml +25 -25
  178. data/examples/kitchen-ansible/Gemfile +19 -19
  179. data/examples/kitchen-ansible/README.md +53 -53
  180. data/examples/kitchen-ansible/files/nginx.repo +6 -6
  181. data/examples/kitchen-ansible/tasks/main.yml +16 -16
  182. data/examples/kitchen-ansible/test/integration/default/default.yml +5 -5
  183. data/examples/kitchen-ansible/test/integration/default/web_spec.rb +28 -28
  184. data/examples/kitchen-chef/.kitchen.yml +20 -20
  185. data/examples/kitchen-chef/Berksfile +3 -3
  186. data/examples/kitchen-chef/Gemfile +19 -19
  187. data/examples/kitchen-chef/README.md +27 -27
  188. data/examples/kitchen-chef/metadata.rb +7 -7
  189. data/examples/kitchen-chef/recipes/default.rb +6 -6
  190. data/examples/kitchen-chef/recipes/nginx.rb +30 -30
  191. data/examples/kitchen-chef/test/integration/default/web_spec.rb +28 -28
  192. data/examples/kitchen-puppet/.kitchen.yml +22 -22
  193. data/examples/kitchen-puppet/Gemfile +20 -20
  194. data/examples/kitchen-puppet/Puppetfile +25 -25
  195. data/examples/kitchen-puppet/README.md +53 -53
  196. data/examples/kitchen-puppet/manifests/site.pp +33 -33
  197. data/examples/kitchen-puppet/metadata.json +11 -11
  198. data/examples/kitchen-puppet/test/integration/default/web_spec.rb +28 -28
  199. data/examples/meta-profile/README.md +37 -37
  200. data/examples/meta-profile/controls/example.rb +13 -13
  201. data/examples/meta-profile/inspec.yml +13 -13
  202. data/examples/profile-attribute.yml +2 -2
  203. data/examples/profile-attribute/README.md +14 -14
  204. data/examples/profile-attribute/controls/example.rb +11 -11
  205. data/examples/profile-attribute/inspec.yml +8 -8
  206. data/examples/profile-aws/controls/iam_password_policy_expiration.rb +8 -8
  207. data/examples/profile-aws/controls/iam_password_policy_max_age.rb +8 -8
  208. data/examples/profile-aws/controls/iam_root_user_mfa.rb +8 -8
  209. data/examples/profile-aws/controls/iam_users_access_key_age.rb +8 -8
  210. data/examples/profile-aws/controls/iam_users_console_users_mfa.rb +8 -8
  211. data/examples/profile-aws/inspec.yml +11 -11
  212. data/examples/profile-azure/controls/azure_resource_group_example.rb +24 -24
  213. data/examples/profile-azure/controls/azure_vm_example.rb +29 -29
  214. data/examples/profile-azure/inspec.yml +11 -11
  215. data/examples/profile-sensitive/README.md +29 -29
  216. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -9
  217. data/examples/profile-sensitive/controls/sensitive.rb +9 -9
  218. data/examples/profile-sensitive/inspec.yml +8 -8
  219. data/examples/profile/README.md +48 -48
  220. data/examples/profile/controls/example.rb +23 -23
  221. data/examples/profile/controls/gordon.rb +36 -36
  222. data/examples/profile/controls/meta.rb +34 -34
  223. data/examples/profile/inspec.yml +10 -10
  224. data/examples/profile/libraries/gordon_config.rb +59 -53
  225. data/inspec.gemspec +47 -47
  226. data/lib/bundles/README.md +3 -3
  227. data/lib/bundles/inspec-artifact.rb +7 -7
  228. data/lib/bundles/inspec-artifact/README.md +1 -1
  229. data/lib/bundles/inspec-artifact/cli.rb +277 -277
  230. data/lib/bundles/inspec-compliance.rb +16 -16
  231. data/lib/bundles/inspec-compliance/.kitchen.yml +20 -20
  232. data/lib/bundles/inspec-compliance/README.md +185 -185
  233. data/lib/bundles/inspec-compliance/api.rb +316 -316
  234. data/lib/bundles/inspec-compliance/api/login.rb +152 -152
  235. data/lib/bundles/inspec-compliance/bootstrap.sh +41 -41
  236. data/lib/bundles/inspec-compliance/cli.rb +254 -254
  237. data/lib/bundles/inspec-compliance/configuration.rb +103 -103
  238. data/lib/bundles/inspec-compliance/http.rb +86 -86
  239. data/lib/bundles/inspec-compliance/support.rb +36 -36
  240. data/lib/bundles/inspec-compliance/target.rb +98 -98
  241. data/lib/bundles/inspec-compliance/test/integration/default/cli.rb +93 -93
  242. data/lib/bundles/inspec-habitat.rb +12 -12
  243. data/lib/bundles/inspec-habitat/cli.rb +36 -36
  244. data/lib/bundles/inspec-habitat/log.rb +10 -10
  245. data/lib/bundles/inspec-habitat/profile.rb +391 -391
  246. data/lib/bundles/inspec-init.rb +8 -8
  247. data/lib/bundles/inspec-init/README.md +31 -31
  248. data/lib/bundles/inspec-init/cli.rb +97 -97
  249. data/lib/bundles/inspec-init/templates/profile/README.md +3 -3
  250. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +19 -19
  251. data/lib/bundles/inspec-init/templates/profile/inspec.yml +8 -8
  252. data/lib/bundles/inspec-supermarket.rb +13 -13
  253. data/lib/bundles/inspec-supermarket/README.md +45 -45
  254. data/lib/bundles/inspec-supermarket/api.rb +84 -84
  255. data/lib/bundles/inspec-supermarket/cli.rb +73 -73
  256. data/lib/bundles/inspec-supermarket/target.rb +34 -34
  257. data/lib/fetchers/git.rb +163 -163
  258. data/lib/fetchers/local.rb +74 -74
  259. data/lib/fetchers/mock.rb +35 -35
  260. data/lib/fetchers/url.rb +204 -204
  261. data/lib/inspec.rb +24 -24
  262. data/lib/inspec/archive/tar.rb +29 -29
  263. data/lib/inspec/archive/zip.rb +19 -19
  264. data/lib/inspec/backend.rb +93 -93
  265. data/lib/inspec/base_cli.rb +363 -357
  266. data/lib/inspec/cached_fetcher.rb +66 -66
  267. data/lib/inspec/cli.rb +292 -292
  268. data/lib/inspec/completions/bash.sh.erb +45 -45
  269. data/lib/inspec/completions/fish.sh.erb +34 -34
  270. data/lib/inspec/completions/zsh.sh.erb +61 -61
  271. data/lib/inspec/control_eval_context.rb +179 -179
  272. data/lib/inspec/dependencies/cache.rb +72 -72
  273. data/lib/inspec/dependencies/dependency_set.rb +92 -92
  274. data/lib/inspec/dependencies/lockfile.rb +115 -115
  275. data/lib/inspec/dependencies/requirement.rb +123 -123
  276. data/lib/inspec/dependencies/resolver.rb +86 -86
  277. data/lib/inspec/describe.rb +27 -27
  278. data/lib/inspec/dsl.rb +66 -66
  279. data/lib/inspec/dsl_shared.rb +33 -33
  280. data/lib/inspec/env_printer.rb +157 -157
  281. data/lib/inspec/errors.rb +14 -13
  282. data/lib/inspec/exceptions.rb +12 -12
  283. data/lib/inspec/expect.rb +45 -45
  284. data/lib/inspec/fetcher.rb +45 -45
  285. data/lib/inspec/file_provider.rb +275 -275
  286. data/lib/inspec/formatters.rb +3 -3
  287. data/lib/inspec/formatters/base.rb +259 -250
  288. data/lib/inspec/formatters/json_rspec.rb +20 -20
  289. data/lib/inspec/formatters/show_progress.rb +12 -12
  290. data/lib/inspec/library_eval_context.rb +58 -58
  291. data/lib/inspec/log.rb +11 -11
  292. data/lib/inspec/metadata.rb +247 -247
  293. data/lib/inspec/method_source.rb +24 -24
  294. data/lib/inspec/objects.rb +14 -14
  295. data/lib/inspec/objects/attribute.rb +65 -65
  296. data/lib/inspec/objects/control.rb +61 -61
  297. data/lib/inspec/objects/describe.rb +92 -92
  298. data/lib/inspec/objects/each_loop.rb +36 -36
  299. data/lib/inspec/objects/list.rb +15 -15
  300. data/lib/inspec/objects/or_test.rb +40 -40
  301. data/lib/inspec/objects/ruby_helper.rb +15 -15
  302. data/lib/inspec/objects/tag.rb +27 -27
  303. data/lib/inspec/objects/test.rb +87 -87
  304. data/lib/inspec/objects/value.rb +27 -27
  305. data/lib/inspec/plugins.rb +60 -60
  306. data/lib/inspec/plugins/cli.rb +24 -24
  307. data/lib/inspec/plugins/fetcher.rb +86 -86
  308. data/lib/inspec/plugins/resource.rb +135 -135
  309. data/lib/inspec/plugins/secret.rb +15 -15
  310. data/lib/inspec/plugins/source_reader.rb +40 -40
  311. data/lib/inspec/polyfill.rb +12 -12
  312. data/lib/inspec/profile.rb +513 -513
  313. data/lib/inspec/profile_context.rb +208 -208
  314. data/lib/inspec/profile_vendor.rb +66 -66
  315. data/lib/inspec/reporters.rb +60 -54
  316. data/lib/inspec/reporters/automate.rb +76 -0
  317. data/lib/inspec/reporters/base.rb +25 -24
  318. data/lib/inspec/reporters/cli.rb +356 -356
  319. data/lib/inspec/reporters/json.rb +116 -116
  320. data/lib/inspec/reporters/json_min.rb +48 -48
  321. data/lib/inspec/reporters/junit.rb +77 -77
  322. data/lib/inspec/require_loader.rb +33 -33
  323. data/lib/inspec/resource.rb +187 -186
  324. data/lib/inspec/rule.rb +266 -266
  325. data/lib/inspec/runner.rb +345 -345
  326. data/lib/inspec/runner_mock.rb +41 -41
  327. data/lib/inspec/runner_rspec.rb +175 -175
  328. data/lib/inspec/runtime_profile.rb +26 -26
  329. data/lib/inspec/schema.rb +213 -213
  330. data/lib/inspec/secrets.rb +19 -19
  331. data/lib/inspec/secrets/yaml.rb +30 -30
  332. data/lib/inspec/shell.rb +220 -220
  333. data/lib/inspec/shell_detector.rb +90 -90
  334. data/lib/inspec/source_reader.rb +29 -29
  335. data/lib/inspec/version.rb +8 -8
  336. data/lib/matchers/matchers.rb +339 -339
  337. data/lib/resource_support/aws.rb +49 -47
  338. data/lib/resource_support/aws/aws_backend_base.rb +12 -12
  339. data/lib/resource_support/aws/aws_backend_factory_mixin.rb +12 -12
  340. data/lib/resource_support/aws/aws_plural_resource_mixin.rb +21 -21
  341. data/lib/resource_support/aws/aws_resource_mixin.rb +66 -66
  342. data/lib/resource_support/aws/aws_singular_resource_mixin.rb +24 -24
  343. data/lib/resources/aide_conf.rb +151 -151
  344. data/lib/resources/apache.rb +48 -48
  345. data/lib/resources/apache_conf.rb +149 -149
  346. data/lib/resources/apt.rb +149 -149
  347. data/lib/resources/audit_policy.rb +63 -63
  348. data/lib/resources/auditd.rb +231 -231
  349. data/lib/resources/auditd_conf.rb +46 -46
  350. data/lib/resources/aws/aws_cloudtrail_trail.rb +77 -77
  351. data/lib/resources/aws/aws_cloudtrail_trails.rb +47 -47
  352. data/lib/resources/aws/aws_cloudwatch_alarm.rb +62 -62
  353. data/lib/resources/aws/aws_cloudwatch_log_metric_filter.rb +100 -100
  354. data/lib/resources/aws/aws_config_delivery_channel.rb +76 -76
  355. data/lib/resources/aws/aws_config_recorder.rb +98 -98
  356. data/lib/resources/aws/aws_ec2_instance.rb +157 -157
  357. data/lib/resources/aws/aws_iam_access_key.rb +106 -106
  358. data/lib/resources/aws/aws_iam_access_keys.rb +149 -149
  359. data/lib/resources/aws/aws_iam_group.rb +56 -56
  360. data/lib/resources/aws/aws_iam_groups.rb +52 -52
  361. data/lib/resources/aws/aws_iam_password_policy.rb +116 -116
  362. data/lib/resources/aws/aws_iam_policies.rb +53 -53
  363. data/lib/resources/aws/aws_iam_policy.rb +125 -125
  364. data/lib/resources/aws/aws_iam_role.rb +51 -51
  365. data/lib/resources/aws/aws_iam_root_user.rb +78 -60
  366. data/lib/resources/aws/aws_iam_user.rb +111 -111
  367. data/lib/resources/aws/aws_iam_users.rb +108 -108
  368. data/lib/resources/aws/aws_kms_key.rb +96 -96
  369. data/lib/resources/aws/aws_kms_keys.rb +53 -53
  370. data/lib/resources/aws/aws_rds_instance.rb +71 -71
  371. data/lib/resources/aws/aws_route_table.rb +63 -63
  372. data/lib/resources/aws/aws_route_tables.rb +60 -0
  373. data/lib/resources/aws/aws_s3_bucket.rb +115 -115
  374. data/lib/resources/aws/aws_s3_bucket_object.rb +82 -82
  375. data/lib/resources/aws/aws_s3_buckets.rb +51 -0
  376. data/lib/resources/aws/aws_security_group.rb +93 -93
  377. data/lib/resources/aws/aws_security_groups.rb +68 -68
  378. data/lib/resources/aws/aws_sns_subscription.rb +78 -78
  379. data/lib/resources/aws/aws_sns_topic.rb +53 -53
  380. data/lib/resources/aws/aws_sns_topics.rb +56 -56
  381. data/lib/resources/aws/aws_subnet.rb +88 -88
  382. data/lib/resources/aws/aws_subnets.rb +53 -53
  383. data/lib/resources/aws/aws_vpc.rb +69 -69
  384. data/lib/resources/aws/aws_vpcs.rb +45 -45
  385. data/lib/resources/azure/azure_backend.rb +377 -377
  386. data/lib/resources/azure/azure_generic_resource.rb +59 -59
  387. data/lib/resources/azure/azure_resource_group.rb +152 -152
  388. data/lib/resources/azure/azure_virtual_machine.rb +264 -264
  389. data/lib/resources/azure/azure_virtual_machine_data_disk.rb +134 -134
  390. data/lib/resources/bash.rb +35 -35
  391. data/lib/resources/bond.rb +69 -69
  392. data/lib/resources/bridge.rb +122 -122
  393. data/lib/resources/chocolatey_package.rb +78 -0
  394. data/lib/resources/command.rb +73 -73
  395. data/lib/resources/cpan.rb +58 -58
  396. data/lib/resources/cran.rb +64 -64
  397. data/lib/resources/crontab.rb +169 -169
  398. data/lib/resources/csv.rb +56 -56
  399. data/lib/resources/dh_params.rb +77 -77
  400. data/lib/resources/directory.rb +25 -25
  401. data/lib/resources/docker.rb +236 -236
  402. data/lib/resources/docker_container.rb +89 -89
  403. data/lib/resources/docker_image.rb +83 -83
  404. data/lib/resources/docker_object.rb +57 -57
  405. data/lib/resources/docker_service.rb +90 -90
  406. data/lib/resources/elasticsearch.rb +169 -169
  407. data/lib/resources/etc_fstab.rb +94 -94
  408. data/lib/resources/etc_group.rb +152 -152
  409. data/lib/resources/etc_hosts.rb +66 -66
  410. data/lib/resources/etc_hosts_allow_deny.rb +112 -112
  411. data/lib/resources/file.rb +298 -298
  412. data/lib/resources/filesystem.rb +31 -31
  413. data/lib/resources/firewalld.rb +143 -143
  414. data/lib/resources/gem.rb +70 -70
  415. data/lib/resources/groups.rb +215 -215
  416. data/lib/resources/grub_conf.rb +227 -227
  417. data/lib/resources/host.rb +306 -306
  418. data/lib/resources/http.rb +253 -253
  419. data/lib/resources/iis_app.rb +101 -101
  420. data/lib/resources/iis_site.rb +148 -148
  421. data/lib/resources/inetd_conf.rb +54 -54
  422. data/lib/resources/ini.rb +29 -29
  423. data/lib/resources/interface.rb +129 -129
  424. data/lib/resources/iptables.rb +80 -80
  425. data/lib/resources/json.rb +107 -107
  426. data/lib/resources/kernel_module.rb +107 -107
  427. data/lib/resources/kernel_parameter.rb +58 -58
  428. data/lib/resources/key_rsa.rb +61 -61
  429. data/lib/resources/limits_conf.rb +46 -46
  430. data/lib/resources/login_def.rb +57 -57
  431. data/lib/resources/mount.rb +88 -88
  432. data/lib/resources/mssql_session.rb +101 -101
  433. data/lib/resources/mysql.rb +82 -81
  434. data/lib/resources/mysql_conf.rb +127 -127
  435. data/lib/resources/mysql_session.rb +85 -85
  436. data/lib/resources/nginx.rb +96 -96
  437. data/lib/resources/nginx_conf.rb +226 -226
  438. data/lib/resources/npm.rb +48 -48
  439. data/lib/resources/ntp_conf.rb +51 -51
  440. data/lib/resources/oneget.rb +71 -71
  441. data/lib/resources/oracledb_session.rb +139 -139
  442. data/lib/resources/os.rb +36 -36
  443. data/lib/resources/os_env.rb +75 -75
  444. data/lib/resources/package.rb +370 -370
  445. data/lib/resources/packages.rb +111 -111
  446. data/lib/resources/parse_config.rb +112 -112
  447. data/lib/resources/passwd.rb +76 -76
  448. data/lib/resources/pip.rb +130 -130
  449. data/lib/resources/platform.rb +109 -109
  450. data/lib/resources/port.rb +771 -771
  451. data/lib/resources/postgres.rb +131 -130
  452. data/lib/resources/postgres_conf.rb +114 -114
  453. data/lib/resources/postgres_hba_conf.rb +90 -90
  454. data/lib/resources/postgres_ident_conf.rb +79 -79
  455. data/lib/resources/postgres_session.rb +71 -71
  456. data/lib/resources/powershell.rb +66 -66
  457. data/lib/resources/processes.rb +204 -204
  458. data/lib/resources/rabbitmq_conf.rb +51 -51
  459. data/lib/resources/registry_key.rb +297 -297
  460. data/lib/resources/security_policy.rb +180 -180
  461. data/lib/resources/service.rb +794 -790
  462. data/lib/resources/shadow.rb +149 -149
  463. data/lib/resources/ssh_conf.rb +97 -97
  464. data/lib/resources/ssl.rb +99 -99
  465. data/lib/resources/sys_info.rb +28 -28
  466. data/lib/resources/toml.rb +32 -32
  467. data/lib/resources/users.rb +654 -654
  468. data/lib/resources/vbscript.rb +68 -68
  469. data/lib/resources/virtualization.rb +247 -247
  470. data/lib/resources/windows_feature.rb +84 -84
  471. data/lib/resources/windows_hotfix.rb +35 -35
  472. data/lib/resources/windows_task.rb +102 -102
  473. data/lib/resources/wmi.rb +110 -110
  474. data/lib/resources/x509_certificate.rb +137 -137
  475. data/lib/resources/xinetd.rb +106 -106
  476. data/lib/resources/xml.rb +46 -46
  477. data/lib/resources/yaml.rb +43 -43
  478. data/lib/resources/yum.rb +180 -180
  479. data/lib/resources/zfs_dataset.rb +60 -60
  480. data/lib/resources/zfs_pool.rb +49 -49
  481. data/lib/source_readers/flat.rb +39 -39
  482. data/lib/source_readers/inspec.rb +75 -75
  483. data/lib/utils/command_wrapper.rb +27 -27
  484. data/lib/utils/convert.rb +12 -12
  485. data/lib/utils/database_helpers.rb +77 -77
  486. data/lib/utils/erlang_parser.rb +192 -192
  487. data/lib/utils/file_reader.rb +25 -25
  488. data/lib/utils/filter.rb +273 -273
  489. data/lib/utils/filter_array.rb +27 -27
  490. data/lib/utils/find_files.rb +44 -44
  491. data/lib/utils/hash.rb +41 -41
  492. data/lib/utils/json_log.rb +18 -18
  493. data/lib/utils/latest_version.rb +22 -22
  494. data/lib/utils/modulator.rb +12 -12
  495. data/lib/utils/nginx_parser.rb +85 -85
  496. data/lib/utils/object_traversal.rb +49 -49
  497. data/lib/utils/parser.rb +274 -274
  498. data/lib/utils/plugin_registry.rb +93 -93
  499. data/lib/utils/simpleconfig.rb +120 -120
  500. data/lib/utils/spdx.rb +13 -13
  501. data/lib/utils/spdx.txt +343 -343
  502. metadata +9 -2
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