inspec-core 2.1.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3136 -0
  3. data/Gemfile +56 -0
  4. data/LICENSE +14 -0
  5. data/MAINTAINERS.md +33 -0
  6. data/MAINTAINERS.toml +52 -0
  7. data/README.md +453 -0
  8. data/bin/inspec +12 -0
  9. data/docs/.gitignore +2 -0
  10. data/docs/README.md +40 -0
  11. data/docs/dev/control-eval.md +62 -0
  12. data/docs/dsl_inspec.md +258 -0
  13. data/docs/dsl_resource.md +100 -0
  14. data/docs/glossary.md +99 -0
  15. data/docs/habitat.md +192 -0
  16. data/docs/inspec_and_friends.md +114 -0
  17. data/docs/matchers.md +169 -0
  18. data/docs/migration.md +293 -0
  19. data/docs/platforms.md +119 -0
  20. data/docs/plugin_kitchen_inspec.md +50 -0
  21. data/docs/profiles.md +378 -0
  22. data/docs/reporters.md +105 -0
  23. data/docs/resources/aide_conf.md.erb +76 -0
  24. data/docs/resources/apache.md.erb +67 -0
  25. data/docs/resources/apache_conf.md.erb +68 -0
  26. data/docs/resources/apt.md.erb +71 -0
  27. data/docs/resources/audit_policy.md.erb +47 -0
  28. data/docs/resources/auditd.md.erb +79 -0
  29. data/docs/resources/auditd_conf.md.erb +68 -0
  30. data/docs/resources/bash.md.erb +75 -0
  31. data/docs/resources/bond.md.erb +90 -0
  32. data/docs/resources/bridge.md.erb +57 -0
  33. data/docs/resources/bsd_service.md.erb +67 -0
  34. data/docs/resources/chocolatey_package.md.erb +58 -0
  35. data/docs/resources/command.md.erb +138 -0
  36. data/docs/resources/cpan.md.erb +79 -0
  37. data/docs/resources/cran.md.erb +64 -0
  38. data/docs/resources/crontab.md.erb +89 -0
  39. data/docs/resources/csv.md.erb +54 -0
  40. data/docs/resources/dh_params.md.erb +205 -0
  41. data/docs/resources/directory.md.erb +30 -0
  42. data/docs/resources/docker.md.erb +219 -0
  43. data/docs/resources/docker_container.md.erb +103 -0
  44. data/docs/resources/docker_image.md.erb +94 -0
  45. data/docs/resources/docker_service.md.erb +114 -0
  46. data/docs/resources/elasticsearch.md.erb +242 -0
  47. data/docs/resources/etc_fstab.md.erb +125 -0
  48. data/docs/resources/etc_group.md.erb +75 -0
  49. data/docs/resources/etc_hosts.md.erb +78 -0
  50. data/docs/resources/etc_hosts_allow.md.erb +74 -0
  51. data/docs/resources/etc_hosts_deny.md.erb +74 -0
  52. data/docs/resources/file.md.erb +526 -0
  53. data/docs/resources/filesystem.md.erb +41 -0
  54. data/docs/resources/firewalld.md.erb +107 -0
  55. data/docs/resources/gem.md.erb +79 -0
  56. data/docs/resources/group.md.erb +61 -0
  57. data/docs/resources/grub_conf.md.erb +101 -0
  58. data/docs/resources/host.md.erb +86 -0
  59. data/docs/resources/http.md.erb +197 -0
  60. data/docs/resources/iis_app.md.erb +122 -0
  61. data/docs/resources/iis_site.md.erb +135 -0
  62. data/docs/resources/inetd_conf.md.erb +94 -0
  63. data/docs/resources/ini.md.erb +76 -0
  64. data/docs/resources/interface.md.erb +58 -0
  65. data/docs/resources/iptables.md.erb +64 -0
  66. data/docs/resources/json.md.erb +63 -0
  67. data/docs/resources/kernel_module.md.erb +120 -0
  68. data/docs/resources/kernel_parameter.md.erb +53 -0
  69. data/docs/resources/key_rsa.md.erb +85 -0
  70. data/docs/resources/launchd_service.md.erb +57 -0
  71. data/docs/resources/limits_conf.md.erb +75 -0
  72. data/docs/resources/login_defs.md.erb +71 -0
  73. data/docs/resources/mount.md.erb +69 -0
  74. data/docs/resources/mssql_session.md.erb +60 -0
  75. data/docs/resources/mysql_conf.md.erb +99 -0
  76. data/docs/resources/mysql_session.md.erb +74 -0
  77. data/docs/resources/nginx.md.erb +79 -0
  78. data/docs/resources/nginx_conf.md.erb +138 -0
  79. data/docs/resources/npm.md.erb +60 -0
  80. data/docs/resources/ntp_conf.md.erb +60 -0
  81. data/docs/resources/oneget.md.erb +53 -0
  82. data/docs/resources/oracledb_session.md.erb +52 -0
  83. data/docs/resources/os.md.erb +141 -0
  84. data/docs/resources/os_env.md.erb +91 -0
  85. data/docs/resources/package.md.erb +120 -0
  86. data/docs/resources/packages.md.erb +67 -0
  87. data/docs/resources/parse_config.md.erb +103 -0
  88. data/docs/resources/parse_config_file.md.erb +138 -0
  89. data/docs/resources/passwd.md.erb +141 -0
  90. data/docs/resources/pip.md.erb +67 -0
  91. data/docs/resources/port.md.erb +137 -0
  92. data/docs/resources/postgres_conf.md.erb +79 -0
  93. data/docs/resources/postgres_hba_conf.md.erb +93 -0
  94. data/docs/resources/postgres_ident_conf.md.erb +76 -0
  95. data/docs/resources/postgres_session.md.erb +69 -0
  96. data/docs/resources/powershell.md.erb +102 -0
  97. data/docs/resources/processes.md.erb +109 -0
  98. data/docs/resources/rabbitmq_config.md.erb +41 -0
  99. data/docs/resources/registry_key.md.erb +158 -0
  100. data/docs/resources/runit_service.md.erb +57 -0
  101. data/docs/resources/security_policy.md.erb +47 -0
  102. data/docs/resources/service.md.erb +121 -0
  103. data/docs/resources/shadow.md.erb +146 -0
  104. data/docs/resources/ssh_config.md.erb +73 -0
  105. data/docs/resources/sshd_config.md.erb +83 -0
  106. data/docs/resources/ssl.md.erb +119 -0
  107. data/docs/resources/sys_info.md.erb +42 -0
  108. data/docs/resources/systemd_service.md.erb +57 -0
  109. data/docs/resources/sysv_service.md.erb +57 -0
  110. data/docs/resources/upstart_service.md.erb +57 -0
  111. data/docs/resources/user.md.erb +140 -0
  112. data/docs/resources/users.md.erb +127 -0
  113. data/docs/resources/vbscript.md.erb +55 -0
  114. data/docs/resources/virtualization.md.erb +57 -0
  115. data/docs/resources/windows_feature.md.erb +47 -0
  116. data/docs/resources/windows_hotfix.md.erb +53 -0
  117. data/docs/resources/windows_task.md.erb +95 -0
  118. data/docs/resources/wmi.md.erb +81 -0
  119. data/docs/resources/x509_certificate.md.erb +151 -0
  120. data/docs/resources/xinetd_conf.md.erb +156 -0
  121. data/docs/resources/xml.md.erb +85 -0
  122. data/docs/resources/yaml.md.erb +69 -0
  123. data/docs/resources/yum.md.erb +98 -0
  124. data/docs/resources/zfs_dataset.md.erb +53 -0
  125. data/docs/resources/zfs_pool.md.erb +47 -0
  126. data/docs/ruby_usage.md +203 -0
  127. data/docs/shared/matcher_be.md.erb +1 -0
  128. data/docs/shared/matcher_cmp.md.erb +43 -0
  129. data/docs/shared/matcher_eq.md.erb +3 -0
  130. data/docs/shared/matcher_include.md.erb +1 -0
  131. data/docs/shared/matcher_match.md.erb +1 -0
  132. data/docs/shell.md +217 -0
  133. data/examples/README.md +8 -0
  134. data/examples/inheritance/README.md +65 -0
  135. data/examples/inheritance/controls/example.rb +14 -0
  136. data/examples/inheritance/inspec.yml +15 -0
  137. data/examples/kitchen-ansible/.kitchen.yml +25 -0
  138. data/examples/kitchen-ansible/Gemfile +19 -0
  139. data/examples/kitchen-ansible/README.md +53 -0
  140. data/examples/kitchen-ansible/files/nginx.repo +6 -0
  141. data/examples/kitchen-ansible/tasks/main.yml +16 -0
  142. data/examples/kitchen-ansible/test/integration/default/default.yml +5 -0
  143. data/examples/kitchen-ansible/test/integration/default/web_spec.rb +28 -0
  144. data/examples/kitchen-chef/.kitchen.yml +20 -0
  145. data/examples/kitchen-chef/Berksfile +3 -0
  146. data/examples/kitchen-chef/Gemfile +19 -0
  147. data/examples/kitchen-chef/README.md +27 -0
  148. data/examples/kitchen-chef/metadata.rb +7 -0
  149. data/examples/kitchen-chef/recipes/default.rb +6 -0
  150. data/examples/kitchen-chef/recipes/nginx.rb +30 -0
  151. data/examples/kitchen-chef/test/integration/default/web_spec.rb +28 -0
  152. data/examples/kitchen-puppet/.kitchen.yml +23 -0
  153. data/examples/kitchen-puppet/Gemfile +20 -0
  154. data/examples/kitchen-puppet/Puppetfile +25 -0
  155. data/examples/kitchen-puppet/README.md +53 -0
  156. data/examples/kitchen-puppet/manifests/site.pp +33 -0
  157. data/examples/kitchen-puppet/metadata.json +11 -0
  158. data/examples/kitchen-puppet/modules/.gitkeep +0 -0
  159. data/examples/kitchen-puppet/test/integration/default/web_spec.rb +28 -0
  160. data/examples/meta-profile/README.md +37 -0
  161. data/examples/meta-profile/controls/example.rb +13 -0
  162. data/examples/meta-profile/inspec.yml +13 -0
  163. data/examples/profile-attribute.yml +2 -0
  164. data/examples/profile-attribute/README.md +14 -0
  165. data/examples/profile-attribute/controls/example.rb +11 -0
  166. data/examples/profile-attribute/inspec.yml +8 -0
  167. data/examples/profile-sensitive/README.md +29 -0
  168. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
  169. data/examples/profile-sensitive/controls/sensitive.rb +9 -0
  170. data/examples/profile-sensitive/inspec.yml +8 -0
  171. data/examples/profile/README.md +48 -0
  172. data/examples/profile/controls/example.rb +23 -0
  173. data/examples/profile/controls/gordon.rb +36 -0
  174. data/examples/profile/controls/meta.rb +34 -0
  175. data/examples/profile/inspec.yml +10 -0
  176. data/examples/profile/libraries/gordon_config.rb +59 -0
  177. data/inspec-core.gemspec +43 -0
  178. data/lib/bundles/README.md +3 -0
  179. data/lib/bundles/inspec-artifact.rb +7 -0
  180. data/lib/bundles/inspec-artifact/README.md +1 -0
  181. data/lib/bundles/inspec-artifact/cli.rb +277 -0
  182. data/lib/bundles/inspec-compliance.rb +16 -0
  183. data/lib/bundles/inspec-compliance/.kitchen.yml +20 -0
  184. data/lib/bundles/inspec-compliance/README.md +193 -0
  185. data/lib/bundles/inspec-compliance/api.rb +360 -0
  186. data/lib/bundles/inspec-compliance/api/login.rb +193 -0
  187. data/lib/bundles/inspec-compliance/bootstrap.sh +41 -0
  188. data/lib/bundles/inspec-compliance/cli.rb +260 -0
  189. data/lib/bundles/inspec-compliance/configuration.rb +103 -0
  190. data/lib/bundles/inspec-compliance/http.rb +125 -0
  191. data/lib/bundles/inspec-compliance/images/cc-token.png +0 -0
  192. data/lib/bundles/inspec-compliance/support.rb +36 -0
  193. data/lib/bundles/inspec-compliance/target.rb +106 -0
  194. data/lib/bundles/inspec-compliance/test/integration/default/cli.rb +93 -0
  195. data/lib/bundles/inspec-habitat.rb +12 -0
  196. data/lib/bundles/inspec-habitat/cli.rb +36 -0
  197. data/lib/bundles/inspec-habitat/log.rb +10 -0
  198. data/lib/bundles/inspec-habitat/profile.rb +391 -0
  199. data/lib/bundles/inspec-init.rb +8 -0
  200. data/lib/bundles/inspec-init/README.md +31 -0
  201. data/lib/bundles/inspec-init/cli.rb +97 -0
  202. data/lib/bundles/inspec-init/templates/profile/README.md +3 -0
  203. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +19 -0
  204. data/lib/bundles/inspec-init/templates/profile/inspec.yml +8 -0
  205. data/lib/bundles/inspec-init/templates/profile/libraries/.gitkeep +0 -0
  206. data/lib/bundles/inspec-supermarket.rb +13 -0
  207. data/lib/bundles/inspec-supermarket/README.md +45 -0
  208. data/lib/bundles/inspec-supermarket/api.rb +84 -0
  209. data/lib/bundles/inspec-supermarket/cli.rb +73 -0
  210. data/lib/bundles/inspec-supermarket/target.rb +34 -0
  211. data/lib/fetchers/git.rb +163 -0
  212. data/lib/fetchers/local.rb +74 -0
  213. data/lib/fetchers/mock.rb +35 -0
  214. data/lib/fetchers/url.rb +247 -0
  215. data/lib/inspec.rb +24 -0
  216. data/lib/inspec/archive/tar.rb +29 -0
  217. data/lib/inspec/archive/zip.rb +19 -0
  218. data/lib/inspec/backend.rb +93 -0
  219. data/lib/inspec/base_cli.rb +368 -0
  220. data/lib/inspec/cached_fetcher.rb +66 -0
  221. data/lib/inspec/cli.rb +292 -0
  222. data/lib/inspec/completions/bash.sh.erb +45 -0
  223. data/lib/inspec/completions/fish.sh.erb +34 -0
  224. data/lib/inspec/completions/zsh.sh.erb +61 -0
  225. data/lib/inspec/control_eval_context.rb +179 -0
  226. data/lib/inspec/dependencies/cache.rb +72 -0
  227. data/lib/inspec/dependencies/dependency_set.rb +92 -0
  228. data/lib/inspec/dependencies/lockfile.rb +115 -0
  229. data/lib/inspec/dependencies/requirement.rb +123 -0
  230. data/lib/inspec/dependencies/resolver.rb +86 -0
  231. data/lib/inspec/describe.rb +27 -0
  232. data/lib/inspec/dsl.rb +66 -0
  233. data/lib/inspec/dsl_shared.rb +33 -0
  234. data/lib/inspec/env_printer.rb +157 -0
  235. data/lib/inspec/errors.rb +14 -0
  236. data/lib/inspec/exceptions.rb +12 -0
  237. data/lib/inspec/expect.rb +45 -0
  238. data/lib/inspec/fetcher.rb +45 -0
  239. data/lib/inspec/file_provider.rb +275 -0
  240. data/lib/inspec/formatters.rb +3 -0
  241. data/lib/inspec/formatters/base.rb +259 -0
  242. data/lib/inspec/formatters/json_rspec.rb +20 -0
  243. data/lib/inspec/formatters/show_progress.rb +12 -0
  244. data/lib/inspec/library_eval_context.rb +58 -0
  245. data/lib/inspec/log.rb +11 -0
  246. data/lib/inspec/metadata.rb +247 -0
  247. data/lib/inspec/method_source.rb +24 -0
  248. data/lib/inspec/objects.rb +14 -0
  249. data/lib/inspec/objects/attribute.rb +75 -0
  250. data/lib/inspec/objects/control.rb +61 -0
  251. data/lib/inspec/objects/describe.rb +92 -0
  252. data/lib/inspec/objects/each_loop.rb +36 -0
  253. data/lib/inspec/objects/list.rb +15 -0
  254. data/lib/inspec/objects/or_test.rb +40 -0
  255. data/lib/inspec/objects/ruby_helper.rb +15 -0
  256. data/lib/inspec/objects/tag.rb +27 -0
  257. data/lib/inspec/objects/test.rb +87 -0
  258. data/lib/inspec/objects/value.rb +27 -0
  259. data/lib/inspec/plugins.rb +60 -0
  260. data/lib/inspec/plugins/cli.rb +24 -0
  261. data/lib/inspec/plugins/fetcher.rb +86 -0
  262. data/lib/inspec/plugins/resource.rb +135 -0
  263. data/lib/inspec/plugins/secret.rb +15 -0
  264. data/lib/inspec/plugins/source_reader.rb +40 -0
  265. data/lib/inspec/polyfill.rb +12 -0
  266. data/lib/inspec/profile.rb +513 -0
  267. data/lib/inspec/profile_context.rb +208 -0
  268. data/lib/inspec/profile_vendor.rb +66 -0
  269. data/lib/inspec/reporters.rb +60 -0
  270. data/lib/inspec/reporters/automate.rb +76 -0
  271. data/lib/inspec/reporters/base.rb +25 -0
  272. data/lib/inspec/reporters/cli.rb +356 -0
  273. data/lib/inspec/reporters/json.rb +116 -0
  274. data/lib/inspec/reporters/json_min.rb +48 -0
  275. data/lib/inspec/reporters/junit.rb +78 -0
  276. data/lib/inspec/require_loader.rb +33 -0
  277. data/lib/inspec/resource.rb +190 -0
  278. data/lib/inspec/rule.rb +280 -0
  279. data/lib/inspec/runner.rb +345 -0
  280. data/lib/inspec/runner_mock.rb +41 -0
  281. data/lib/inspec/runner_rspec.rb +175 -0
  282. data/lib/inspec/runtime_profile.rb +26 -0
  283. data/lib/inspec/schema.rb +213 -0
  284. data/lib/inspec/secrets.rb +19 -0
  285. data/lib/inspec/secrets/yaml.rb +30 -0
  286. data/lib/inspec/shell.rb +220 -0
  287. data/lib/inspec/shell_detector.rb +90 -0
  288. data/lib/inspec/source_reader.rb +29 -0
  289. data/lib/inspec/version.rb +8 -0
  290. data/lib/matchers/matchers.rb +339 -0
  291. data/lib/resources/aide_conf.rb +151 -0
  292. data/lib/resources/apache.rb +48 -0
  293. data/lib/resources/apache_conf.rb +149 -0
  294. data/lib/resources/apt.rb +149 -0
  295. data/lib/resources/audit_policy.rb +63 -0
  296. data/lib/resources/auditd.rb +231 -0
  297. data/lib/resources/auditd_conf.rb +46 -0
  298. data/lib/resources/bash.rb +35 -0
  299. data/lib/resources/bond.rb +69 -0
  300. data/lib/resources/bridge.rb +122 -0
  301. data/lib/resources/chocolatey_package.rb +78 -0
  302. data/lib/resources/command.rb +73 -0
  303. data/lib/resources/cpan.rb +58 -0
  304. data/lib/resources/cran.rb +64 -0
  305. data/lib/resources/crontab.rb +169 -0
  306. data/lib/resources/csv.rb +56 -0
  307. data/lib/resources/dh_params.rb +77 -0
  308. data/lib/resources/directory.rb +25 -0
  309. data/lib/resources/docker.rb +236 -0
  310. data/lib/resources/docker_container.rb +89 -0
  311. data/lib/resources/docker_image.rb +83 -0
  312. data/lib/resources/docker_object.rb +57 -0
  313. data/lib/resources/docker_service.rb +90 -0
  314. data/lib/resources/elasticsearch.rb +169 -0
  315. data/lib/resources/etc_fstab.rb +94 -0
  316. data/lib/resources/etc_group.rb +154 -0
  317. data/lib/resources/etc_hosts.rb +66 -0
  318. data/lib/resources/etc_hosts_allow_deny.rb +112 -0
  319. data/lib/resources/file.rb +298 -0
  320. data/lib/resources/filesystem.rb +31 -0
  321. data/lib/resources/firewalld.rb +143 -0
  322. data/lib/resources/gem.rb +70 -0
  323. data/lib/resources/groups.rb +215 -0
  324. data/lib/resources/grub_conf.rb +227 -0
  325. data/lib/resources/host.rb +306 -0
  326. data/lib/resources/http.rb +253 -0
  327. data/lib/resources/iis_app.rb +101 -0
  328. data/lib/resources/iis_site.rb +148 -0
  329. data/lib/resources/inetd_conf.rb +54 -0
  330. data/lib/resources/ini.rb +29 -0
  331. data/lib/resources/interface.rb +129 -0
  332. data/lib/resources/iptables.rb +80 -0
  333. data/lib/resources/json.rb +111 -0
  334. data/lib/resources/kernel_module.rb +107 -0
  335. data/lib/resources/kernel_parameter.rb +58 -0
  336. data/lib/resources/key_rsa.rb +63 -0
  337. data/lib/resources/limits_conf.rb +46 -0
  338. data/lib/resources/login_def.rb +57 -0
  339. data/lib/resources/mount.rb +88 -0
  340. data/lib/resources/mssql_session.rb +101 -0
  341. data/lib/resources/mysql.rb +82 -0
  342. data/lib/resources/mysql_conf.rb +127 -0
  343. data/lib/resources/mysql_session.rb +85 -0
  344. data/lib/resources/nginx.rb +96 -0
  345. data/lib/resources/nginx_conf.rb +226 -0
  346. data/lib/resources/npm.rb +48 -0
  347. data/lib/resources/ntp_conf.rb +51 -0
  348. data/lib/resources/oneget.rb +71 -0
  349. data/lib/resources/oracledb_session.rb +139 -0
  350. data/lib/resources/os.rb +36 -0
  351. data/lib/resources/os_env.rb +86 -0
  352. data/lib/resources/package.rb +370 -0
  353. data/lib/resources/packages.rb +111 -0
  354. data/lib/resources/parse_config.rb +112 -0
  355. data/lib/resources/passwd.rb +76 -0
  356. data/lib/resources/pip.rb +130 -0
  357. data/lib/resources/platform.rb +109 -0
  358. data/lib/resources/port.rb +771 -0
  359. data/lib/resources/postgres.rb +131 -0
  360. data/lib/resources/postgres_conf.rb +114 -0
  361. data/lib/resources/postgres_hba_conf.rb +90 -0
  362. data/lib/resources/postgres_ident_conf.rb +79 -0
  363. data/lib/resources/postgres_session.rb +71 -0
  364. data/lib/resources/powershell.rb +67 -0
  365. data/lib/resources/processes.rb +204 -0
  366. data/lib/resources/rabbitmq_conf.rb +51 -0
  367. data/lib/resources/registry_key.rb +297 -0
  368. data/lib/resources/security_policy.rb +180 -0
  369. data/lib/resources/service.rb +794 -0
  370. data/lib/resources/shadow.rb +159 -0
  371. data/lib/resources/ssh_conf.rb +97 -0
  372. data/lib/resources/ssl.rb +99 -0
  373. data/lib/resources/sys_info.rb +28 -0
  374. data/lib/resources/toml.rb +32 -0
  375. data/lib/resources/users.rb +654 -0
  376. data/lib/resources/vbscript.rb +68 -0
  377. data/lib/resources/virtualization.rb +247 -0
  378. data/lib/resources/windows_feature.rb +84 -0
  379. data/lib/resources/windows_hotfix.rb +35 -0
  380. data/lib/resources/windows_task.rb +102 -0
  381. data/lib/resources/wmi.rb +110 -0
  382. data/lib/resources/x509_certificate.rb +137 -0
  383. data/lib/resources/xinetd.rb +106 -0
  384. data/lib/resources/xml.rb +46 -0
  385. data/lib/resources/yaml.rb +43 -0
  386. data/lib/resources/yum.rb +180 -0
  387. data/lib/resources/zfs_dataset.rb +60 -0
  388. data/lib/resources/zfs_pool.rb +49 -0
  389. data/lib/source_readers/flat.rb +39 -0
  390. data/lib/source_readers/inspec.rb +75 -0
  391. data/lib/utils/command_wrapper.rb +27 -0
  392. data/lib/utils/convert.rb +12 -0
  393. data/lib/utils/database_helpers.rb +77 -0
  394. data/lib/utils/enumerable_delegation.rb +9 -0
  395. data/lib/utils/erlang_parser.rb +192 -0
  396. data/lib/utils/file_reader.rb +25 -0
  397. data/lib/utils/filter.rb +273 -0
  398. data/lib/utils/filter_array.rb +27 -0
  399. data/lib/utils/find_files.rb +47 -0
  400. data/lib/utils/hash.rb +41 -0
  401. data/lib/utils/json_log.rb +18 -0
  402. data/lib/utils/latest_version.rb +22 -0
  403. data/lib/utils/modulator.rb +12 -0
  404. data/lib/utils/nginx_parser.rb +105 -0
  405. data/lib/utils/object_traversal.rb +49 -0
  406. data/lib/utils/parser.rb +274 -0
  407. data/lib/utils/pkey_reader.rb +15 -0
  408. data/lib/utils/plugin_registry.rb +93 -0
  409. data/lib/utils/simpleconfig.rb +120 -0
  410. data/lib/utils/spdx.rb +13 -0
  411. data/lib/utils/spdx.txt +344 -0
  412. metadata +713 -0
@@ -0,0 +1,771 @@
1
+ # encoding: utf-8
2
+
3
+ require 'utils/parser'
4
+ require 'utils/filter'
5
+ require 'ipaddr'
6
+
7
+ # TODO: currently we return local ip only
8
+ # TODO: improve handling of same port on multiple interfaces
9
+ module Inspec::Resources
10
+ class Port < Inspec.resource(1)
11
+ name 'port'
12
+ supports platform: 'unix'
13
+ supports platform: 'windows'
14
+ desc "Use the port InSpec audit resource to test basic port properties, such as port, process, if it's listening."
15
+ example "
16
+ describe port(80) do
17
+ it { should be_listening }
18
+ its('protocols') {should eq ['tcp']}
19
+ its('addresses') {should eq ['127.0.0.1']}
20
+ end
21
+
22
+ describe port.where { protocol =~ /tcp/ && port > 80 } do
23
+ it { should_not be_listening }
24
+ end
25
+ "
26
+
27
+ def initialize(*args)
28
+ args.unshift(nil) if args.length <= 1 # add the ip address to the front
29
+ @ip = args[0]
30
+ @port = if args[1].nil?
31
+ nil
32
+ else
33
+ args[1].to_i
34
+ end
35
+
36
+ @cache = nil
37
+ @port_manager = port_manager_for_os
38
+ return skip_resource 'The `port` resource is not supported on your OS yet.' if @port_manager.nil?
39
+ end
40
+
41
+ filter = FilterTable.create
42
+ filter.add_accessor(:where)
43
+ .add_accessor(:entries)
44
+ .add(:ports, field: 'port', style: :simple)
45
+ .add(:addresses, field: 'address', style: :simple)
46
+ .add(:protocols, field: 'protocol', style: :simple)
47
+ .add(:processes, field: 'process', style: :simple)
48
+ .add(:pids, field: 'pid', style: :simple)
49
+ .add(:listening?) { |x| !x.entries.empty? }
50
+ filter.connect(self, :info)
51
+
52
+ def to_s
53
+ "Port #{@port}"
54
+ end
55
+
56
+ private
57
+
58
+ def port_manager_for_os
59
+ os = inspec.os
60
+ if os.linux?
61
+ LinuxPorts.new(inspec)
62
+ elsif os.aix?
63
+ # AIX: see http://www.ibm.com/developerworks/aix/library/au-lsof.html#resources
64
+ # and https://www-01.ibm.com/marketing/iwm/iwm/web/reg/pick.do?source=aixbp
65
+ AixPorts.new(inspec)
66
+ elsif os.darwin?
67
+ # Darwin: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/lsof.8.html
68
+ LsofPorts.new(inspec)
69
+ elsif os.windows?
70
+ WindowsPorts.new(inspec)
71
+ elsif ['freebsd'].include?(os[:family])
72
+ FreeBsdPorts.new(inspec)
73
+ elsif os.solaris?
74
+ SolarisPorts.new(inspec)
75
+ elsif os.hpux?
76
+ HpuxPorts.new(inspec)
77
+ end
78
+ end
79
+
80
+ def info
81
+ return @cache if !@cache.nil?
82
+ # abort if os detection has not worked
83
+ return @cache = [] if @port_manager.nil?
84
+ # query ports
85
+ cache = @port_manager.info || []
86
+ cache.select! { |x| x['port'] == @port } unless @port.nil?
87
+ cache.select! { |x| x['address'] == @ip } unless @ip.nil?
88
+ @cache = cache
89
+ end
90
+ end
91
+
92
+ # implements an info method and returns all ip adresses and protocols for
93
+ # each port
94
+ # [{
95
+ # 'port' => 22,
96
+ # 'address' => '0.0.0.0'
97
+ # 'protocol' => 'tcp'
98
+ # },
99
+ # {
100
+ # 'port' => 22,
101
+ # 'address' => '::'
102
+ # 'protocol' => 'tcp6'
103
+ # }]
104
+ class PortsInfo
105
+ attr_reader :inspec
106
+ def initialize(inspec)
107
+ @inspec = inspec
108
+ end
109
+ end
110
+
111
+ # TODO: Add UDP infromation Get-NetUDPEndpoint
112
+ # TODO: currently Windows only supports tcp ports
113
+ # TODO: Get-NetTCPConnection does not return PIDs
114
+ # TODO: double-check output with 'netstat -ano'
115
+ # @see https://connect.microsoft.com/PowerShell/feedback/details/1349420/get-nettcpconnection-does-not-show-processid
116
+ class WindowsPorts < PortsInfo
117
+ def info
118
+ netstat_info || powershell_info
119
+ end
120
+
121
+ private
122
+
123
+ def powershell_info
124
+ cmd = inspec.command('Get-NetTCPConnection -state Listen | Select-Object -Property State, Caption, Description, LocalAddress, LocalPort, RemoteAddress, RemotePort, DisplayName, Status | ConvertTo-Json')
125
+ return nil if cmd.exit_status != 0
126
+
127
+ entries = JSON.parse(cmd.stdout)
128
+ return nil if entries.nil?
129
+
130
+ entries.map { |x|
131
+ {
132
+ 'port' => x['LocalPort'],
133
+ 'address' => x['LocalAddress'],
134
+ 'protocol' => 'tcp',
135
+ }
136
+ }
137
+ rescue JSON::ParserError => _e
138
+ return nil
139
+ end
140
+
141
+ def netstat_info
142
+ # retrieve processes grepping by LISTENING state with 0 lines before and 1 after to catch the process name
143
+ # also UDP ports have nothing in the State column
144
+ cmd = inspec.command('netstat -anbo | Select-String -CaseSensitive -pattern "^\s+UDP|\s+LISTENING\s+\d+$" -context 0,1')
145
+ return nil if cmd.exit_status != 0
146
+ lines = cmd.stdout.scan(/^>\s*(tcp\S*|udp\S*)\s+(\S+):(\d+)\s+(\S+)\s+(\S*)\s+(\d+)\s+(.+)/i)
147
+ lines.map do |line|
148
+ pid = line[5].to_i
149
+ process = line[6].delete('[').delete(']').strip
150
+ process = 'System' if process == 'Can not obtain ownership information' && pid == 4
151
+ {
152
+ 'port' => line[2].to_i,
153
+ 'address' => line[1].delete('[').delete(']'),
154
+ 'protocol' => line[0].downcase,
155
+ 'pid' => pid,
156
+ 'process' => process,
157
+ }
158
+ end
159
+ end
160
+ end
161
+
162
+ # extracts udp and tcp ports from the lsof command
163
+ class LsofPorts < PortsInfo
164
+ attr_reader :lsof
165
+
166
+ def initialize(inspec, lsofpath = nil)
167
+ @lsof = lsofpath || 'lsof'
168
+ super(inspec)
169
+ end
170
+
171
+ def info
172
+ ports = []
173
+
174
+ # check that lsof is available, otherwise fail
175
+ raise 'Please ensure `lsof` is available on the machine.' if !inspec.command(@lsof.to_s).exist?
176
+
177
+ # -F p=pid, c=command, P=protocol name, t=type, n=internet addresses
178
+ # see 'OUTPUT FOR OTHER PROGRAMS' in LSOF(8)
179
+ lsof_cmd = inspec.command("#{@lsof} -nP -i -FpctPn")
180
+ return nil if lsof_cmd.exit_status.to_i != 0
181
+
182
+ # map to desired return struct
183
+ lsof_parser(lsof_cmd).each do |process, port_ids|
184
+ pid, cmd = process.split(':')
185
+ port_ids.each do |port_str|
186
+ # should not break on ipv6 addresses
187
+ ipv, proto, port, host = port_str.split(':', 4)
188
+ ports.push({ 'port' => port.to_i,
189
+ 'address' => host,
190
+ 'protocol' => ipv == 'ipv6' ? proto + '6' : proto,
191
+ 'process' => cmd,
192
+ 'pid' => pid.to_i })
193
+ end
194
+ end
195
+
196
+ ports
197
+ end
198
+
199
+ # rubocop:disable Metrics/CyclomaticComplexity
200
+ # rubocop:disable Metrics/AbcSize
201
+ def lsof_parser(lsof_cmd)
202
+ procs = {}
203
+ # build this with formatted output (-F) from lsof
204
+ # procs = {
205
+ # '123:sshd' => [
206
+ # 'ipv4:tcp:22:127.0.0.1',
207
+ # 'ipv6:tcp:22:::1',
208
+ # 'ipv4:tcp:*',
209
+ # 'ipv6:tcp:*',
210
+ # ],
211
+ # '456:ntpd' => [
212
+ # 'ipv4:udp:123:*',
213
+ # 'ipv6:udp:123:*',
214
+ # ]
215
+ # }
216
+ proc_id = port_id = nil
217
+ lsof_cmd.stdout.each_line do |line|
218
+ line.chomp!
219
+ key = line.slice!(0)
220
+ case key
221
+ when 'p'
222
+ proc_id = line
223
+ port_id = nil
224
+ when 'c'
225
+ proc_id += ':' + line
226
+ when 't'
227
+ port_id = line.downcase
228
+ when 'P'
229
+ port_id += ':' + line.downcase
230
+ when 'n'
231
+ src, dst = line.split('->')
232
+
233
+ # skip active comm streams
234
+ next if dst
235
+
236
+ host, port = /^(\S+):(\d+|\*)$/.match(src)[1, 2]
237
+
238
+ # skip channels from port 0 - what does this mean?
239
+ next if port == '*'
240
+
241
+ # create new array stub if !exist?
242
+ procs[proc_id] = [] unless procs.key?(proc_id)
243
+
244
+ # change address '*' to zero
245
+ host = port_id =~ /^ipv6:/ ? '[::]' : '0.0.0.0' if host == '*'
246
+ # entrust URI to scrub the host and port
247
+ begin
248
+ uri = URI("addr://#{host}:#{port}")
249
+ uri.host && uri.port
250
+ rescue => e
251
+ warn "could not parse URI 'addr://#{host}:#{port}' - #{e}"
252
+ next
253
+ end
254
+
255
+ # e.g. 'ipv4:tcp:22:127.0.0.1'
256
+ # strip ipv6 squares for inspec
257
+ port_id += ':' + port + ':' + host.gsub(/^\[|\]$/, '')
258
+
259
+ # lsof will give us another port unless it's done
260
+ procs[proc_id] << port_id
261
+ end
262
+ end
263
+
264
+ procs
265
+ end
266
+ end
267
+
268
+ class AixPorts < PortsInfo
269
+ def info
270
+ ports_via_netstat || ports_via_lsof
271
+ end
272
+
273
+ def ports_via_lsof
274
+ return nil unless inspec.command('lsof').exist?
275
+ LsofPorts.new(inspec).info
276
+ end
277
+
278
+ def ports_via_netstat
279
+ return nil unless inspec.command('netstat').exist?
280
+
281
+ cmd = inspec.command('netstat -Aan | grep LISTEN')
282
+ return nil unless cmd.exit_status.to_i.zero?
283
+
284
+ ports = []
285
+ # parse all lines
286
+ cmd.stdout.each_line do |line|
287
+ port_info = parse_netstat_line(line)
288
+
289
+ # only push protocols we are interested in
290
+ next unless %w{tcp tcp6 udp udp6}.include?(port_info['protocol'])
291
+ ports.push(port_info)
292
+ end
293
+
294
+ ports
295
+ end
296
+
297
+ def parse_netstat_line(line)
298
+ # parse each line
299
+ # 1 - Socket, 2 - Proto, 3 - Receive-Q, 4 - Send-Q, 5 - Local address, 6 - Foreign Address, 7 - State
300
+ parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)/.match(line)
301
+ return {} if parsed.nil?
302
+
303
+ # parse ip4 and ip6 addresses
304
+ protocol = parsed[2].downcase
305
+
306
+ # detect protocol if not provided
307
+ protocol += '6' if parsed[5].count(':') > 1 && %w{tcp udp}.include?(protocol)
308
+ protocol.chop! if %w{tcp4 upd4}.include?(protocol)
309
+
310
+ # extract host and port information
311
+ host, port = parse_net_address(parsed[5], protocol)
312
+ return {} if host.nil?
313
+
314
+ # extract PID
315
+ cmd = inspec.command("rmsock #{parsed[1]} tcpcb")
316
+ parsed_pid = /^The socket (\S+) is being held by proccess (\d+) \((\S+)\)/.match(cmd.stdout)
317
+ return {} if parsed_pid.nil?
318
+ process = parsed_pid[3]
319
+ pid = parsed_pid[2]
320
+ pid = pid.to_i if pid =~ /^\d+$/
321
+
322
+ {
323
+ 'port' => port,
324
+ 'address' => host,
325
+ 'protocol' => protocol,
326
+ 'process' => process,
327
+ 'pid' => pid,
328
+ }
329
+ end
330
+
331
+ def parse_net_address(net_addr, protocol)
332
+ # local/foreign addresses on AIX use a '.' to separate the addresss
333
+ # from the port
334
+ address, _sep, port = net_addr.rpartition('.')
335
+ if protocol.eql?('tcp6') || protocol.eql?('udp6')
336
+ ip6addr = address
337
+ # AIX uses the wildcard character for ipv6 addresses listening on
338
+ # all interfaces.
339
+ ip6addr = '::' if ip6addr =~ /^\*$/
340
+
341
+ # v6 addresses need to end in a double-colon when using
342
+ # shorthand notation. netstat ends with a single colon.
343
+ # IPAddr will fail to properly parse an address unless it
344
+ # uses a double-colon for short-hand notation.
345
+ ip6addr += ':' if ip6addr =~ /\w:$/
346
+
347
+ begin
348
+ ip_parser = IPAddr.new(ip6addr)
349
+ rescue IPAddr::InvalidAddressError
350
+ # This IP is not parsable. There appears to be a bug in netstat
351
+ # output that truncates link-local IP addresses:
352
+ # example: udp6 0 0 fe80::42:acff:fe11::123 :::* 0 54550 3335/ntpd
353
+ # actual link address: inet6 fe80::42:acff:fe11:5/64 scope link
354
+ #
355
+ # in this example, the "5" is truncated making the netstat output
356
+ # an invalid IP address.
357
+ return [nil, nil]
358
+ end
359
+
360
+ # Check to see if this is a IPv4 address in a tcp6/udp6 line.
361
+ # If so, don't put brackets around the IP or URI won't know how
362
+ # to properly handle it.
363
+ # example: f000000000000000 tcp6 0 0 127.0.0.1.8005 *.* LISTEN
364
+ if ip_parser.ipv4?
365
+ ip_addr = URI("addr://#{ip6addr}:#{port}")
366
+ host = ip_addr.host
367
+ else
368
+ ip_addr = URI("addr://[#{ip6addr}]:#{port}")
369
+ host = ip_addr.host[1..ip_addr.host.size-2]
370
+ end
371
+ else
372
+ ip4addr = address
373
+ # In AIX the wildcard character is used to match all interfaces
374
+ ip4addr = '0.0.0.0' if ip4addr =~ /^\*$/
375
+ ip_addr = URI("addr://#{ip4addr}:#{port}")
376
+ host = ip_addr.host
377
+ end
378
+
379
+ [host, port.to_i]
380
+ end
381
+ end
382
+
383
+ # extract port information from netstat
384
+ class LinuxPorts < PortsInfo
385
+ ALLOWED_PROTOCOLS = %w{tcp tcp6 udp udp6}.freeze
386
+
387
+ def info
388
+ ports_via_ss || ports_via_netstat
389
+ end
390
+
391
+ def ports_via_ss
392
+ return nil unless inspec.command('ss').exist?
393
+
394
+ cmd = inspec.command('ss -tulpen')
395
+ return nil unless cmd.exit_status.to_i.zero?
396
+
397
+ ports = []
398
+
399
+ cmd.stdout.each_line do |line|
400
+ parsed_line = parse_ss_line(line)
401
+ ports << parsed_line unless parsed_line.nil?
402
+ end
403
+
404
+ ports
405
+ end
406
+
407
+ def ports_via_netstat
408
+ return nil unless inspec.command('netstat').exist?
409
+
410
+ cmd = inspec.command('netstat -tulpen')
411
+ return nil unless cmd.exit_status.to_i.zero?
412
+
413
+ ports = []
414
+ # parse all lines
415
+ cmd.stdout.each_line do |line|
416
+ port_info = parse_netstat_line(line)
417
+
418
+ # only push protocols we are interested in
419
+ next unless %w{tcp tcp6 udp udp6}.include?(port_info['protocol'])
420
+ ports.push(port_info)
421
+ end
422
+ ports
423
+ end
424
+
425
+ def parse_net_address(net_addr, protocol)
426
+ if protocol.eql?('tcp6') || protocol.eql?('udp6')
427
+ # prep for URI parsing, parse ip6 port
428
+ ip6 = /^(\S+):(\d+)$/.match(net_addr)
429
+ ip6addr = ip6[1]
430
+ ip6addr = '::' if ip6addr =~ /^:::$/
431
+
432
+ # v6 addresses need to end in a double-colon when using
433
+ # shorthand notation. netstat ends with a single colon.
434
+ # IPAddr will fail to properly parse an address unless it
435
+ # uses a double-colon for short-hand notation.
436
+ ip6addr += ':' if ip6addr =~ /\w:$/
437
+
438
+ begin
439
+ ip_parser = IPAddr.new(ip6addr)
440
+ rescue IPAddr::InvalidAddressError
441
+ # This IP is not parsable. There appears to be a bug in netstat
442
+ # output that truncates link-local IP addresses:
443
+ # example: udp6 0 0 fe80::42:acff:fe11::123 :::* 0 54550 3335/ntpd
444
+ # actual link address: inet6 fe80::42:acff:fe11:5/64 scope link
445
+ #
446
+ # in this example, the "5" is truncated making the netstat output
447
+ # an invalid IP address.
448
+ return [nil, nil]
449
+ end
450
+
451
+ # Check to see if this is a IPv4 address in a tcp6/udp6 line.
452
+ # If so, don't put brackets around the IP or URI won't know how
453
+ # to properly handle it.
454
+ # example: tcp6 0 0 127.0.0.1:8005 :::* LISTEN
455
+ if ip_parser.ipv4?
456
+ ip_addr = URI("addr://#{ip6addr}:#{ip6[2]}")
457
+ host = ip_addr.host
458
+ else
459
+ ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
460
+ # strip []
461
+ host = ip_addr.host[1..ip_addr.host.size-2]
462
+ end
463
+ else
464
+ ip_addr = URI('addr://'+net_addr)
465
+ host = ip_addr.host
466
+ end
467
+
468
+ port = ip_addr.port
469
+
470
+ [host, port]
471
+ rescue URI::InvalidURIError => e
472
+ warn "Could not parse #{net_addr}, #{e}"
473
+ nil
474
+ end
475
+
476
+ def parse_netstat_line(line)
477
+ # parse each line
478
+ # 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 - Inode, 8 - PID/Program name
479
+ parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)\s+(\S+)\s+(\S+)/.match(line)
480
+ return {} if parsed.nil? || line.match(/^proto/i)
481
+
482
+ # parse ip4 and ip6 addresses
483
+ protocol = parsed[1].downcase
484
+
485
+ # detect protocol if not provided
486
+ protocol += '6' if parsed[4].count(':') > 1 && %w{tcp udp}.include?(protocol)
487
+
488
+ # extract host and port information
489
+ host, port = parse_net_address(parsed[4], protocol)
490
+ return {} if host.nil?
491
+
492
+ # extract PID
493
+ process = parsed[9].split('/')
494
+ pid = process[0]
495
+ pid = pid.to_i if pid =~ /^\d+$/
496
+ process = process[1]
497
+
498
+ {
499
+ 'port' => port,
500
+ 'address' => host,
501
+ 'protocol' => protocol,
502
+ 'process' => process,
503
+ 'pid' => pid,
504
+ }
505
+ end
506
+
507
+ def tokenize_ss_line(line)
508
+ # iproute-2.6.32-54.el6 output:
509
+ # Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
510
+ # udp UNCONN 0 0 *:111 *:* users:(("rpcbind",1123,6)) ino=8680 sk=ffff8801390cf7c0
511
+ # tcp LISTEN 0 128 *:22 *:* users:(("sshd",3965,3)) ino:11604 sk:ffff88013a3b5800
512
+ #
513
+ # iproute-2.6.32-20.el6 output:
514
+ # Netid Recv-Q Send-Q Local Address:Port Peer Address:Port
515
+ # udp 0 0 *:111 *:* users:(("rpcbind",1123,6)) ino=8680 sk=ffff8801390cf7c0
516
+ # tcp 0 128 *:22 *:* users:(("sshd",3965,3)) ino:11604 sk:ffff88013a3b5800
517
+ tokens = line.split(/\s+/, 7)
518
+ if tokens[1] =~ /^\d+$/ # iproute-2.6.32-20
519
+ {
520
+ netid: tokens[0],
521
+ local_addr: tokens[3],
522
+ process_info: tokens[5],
523
+ }
524
+ else # iproute-2.6.32-54
525
+ {
526
+ netid: tokens[0],
527
+ local_addr: tokens[4],
528
+ process_info: tokens[6],
529
+ }
530
+ end
531
+ end
532
+
533
+ def parse_ss_line(line)
534
+ # parsed = line.split(/\s+/, 7)
535
+ parsed = tokenize_ss_line(line)
536
+
537
+ # ss only returns "tcp" and "udp" as the protocol. However, netstat would return
538
+ # "tcp6" and "udp6" as necessary. In order to maintain backward compatibility, we
539
+ # will manually modify the protocol value if the line we're parsing is an IPv6
540
+ # entry.
541
+ process_info = parsed[:process_info]
542
+ protocol = parsed[:netid]
543
+ protocol += '6' if process_info.include?('v6only:1')
544
+ return nil unless ALLOWED_PROTOCOLS.include?(protocol)
545
+
546
+ # parse the Local Address:Port
547
+ # examples:
548
+ # *:22
549
+ # :::22
550
+ # 10.0.2.15:1234
551
+ # ::ffff:10.0.2.15:9300
552
+ # fe80::a00:27ff:fe32:ed09%enp0s3:9200
553
+ parsed_net_address = parsed[:local_addr].match(/(\S+):(\*|\d+)$/)
554
+ return nil if parsed_net_address.nil?
555
+ host = parsed_net_address[1]
556
+ port = parsed_net_address[2]
557
+ return nil if host.nil? && port.nil?
558
+
559
+ # For backward compatibility with the netstat output, ensure the
560
+ # port is stored as an integer
561
+ port = port.to_i
562
+
563
+ # for those "v4-but-listed-in-v6" entries, strip off the
564
+ # leading IPv6 value at the beginning
565
+ # example: ::ffff:10.0.2.15:9200
566
+ host.delete!('::ffff:') if host.start_with?('::ffff:')
567
+
568
+ # if there's an interface name in the local address, which is common for
569
+ # IPv6 listeners, strip that out too.
570
+ # example: fe80::a00:27ff:fe32:ed09%enp0s3
571
+ host = host.split('%').first
572
+
573
+ # if host is "*", replace with "0.0.0.0" to maintain backward compatibility with
574
+ # the netstat-provided data
575
+ host = '0.0.0.0' if host == '*'
576
+
577
+ # in case process list parsing is not successfull
578
+ process = nil
579
+ pid = nil
580
+
581
+ # parse process and pid from the process list
582
+ #
583
+ # remove the "users:((" and "))" parts
584
+ # input: users:((\"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8))
585
+ # res: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
586
+ process_list_match = parsed[:process_info].match(/users:\(\((.+)\)\)/)
587
+ if process_list_match
588
+ # list entires are seperated by "," the braces can also be removed
589
+ # input: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
590
+ # res: ["\"nginx\",pid=583,fd=8", "\"nginx\",pid=582,fd=8", "\"nginx\",pid=580,fd=8", "\"nginx\",pid=579,fd=8"]
591
+ process_list = process_list_match[1].split('),(')
592
+ # To stay backwards compatible with netstat we need to select
593
+ # the last element in the resulting array.
594
+ # res: "\"nginx\",pid=579,fd=8"
595
+
596
+ # parse the process name from the process list
597
+ process_match = process_list.last.match(/^\"(\S+)\"/)
598
+ process = process_match.nil? ? nil : process_match[1]
599
+
600
+ # parse the PID from the process list
601
+ pid_match = process_list.last.match(/pid=(\d+)/)
602
+ pid = pid_match.nil? ? nil : pid_match[1].to_i
603
+ end
604
+
605
+ {
606
+ 'port' => port,
607
+ 'address' => host,
608
+ 'protocol' => protocol,
609
+ 'process' => process,
610
+ 'pid' => pid,
611
+ }
612
+ end
613
+ end
614
+
615
+ # extracts information from sockstat
616
+ class FreeBsdPorts < PortsInfo
617
+ def info
618
+ cmd = inspec.command('sockstat -46l')
619
+ return nil if cmd.exit_status.to_i != 0
620
+
621
+ ports = []
622
+ # split on each newline
623
+ cmd.stdout.each_line do |line|
624
+ port_info = parse_sockstat_line(line)
625
+
626
+ # push data, if not headerfile
627
+ next unless %w{tcp tcp6 udp udp6}.include?(port_info['protocol'])
628
+ ports.push(port_info)
629
+ end
630
+ ports
631
+ end
632
+
633
+ def parse_net_address(net_addr, protocol)
634
+ case protocol
635
+ when 'tcp4', 'udp4', 'tcp', 'udp'
636
+ # replace * with 0.0.0.0
637
+ net_addr = net_addr.gsub(/^\*:/, '0.0.0.0:') if net_addr =~ /^*:(\d+)$/
638
+ ip_addr = URI('addr://'+net_addr)
639
+ host = ip_addr.host
640
+ port = ip_addr.port
641
+ when 'tcp6', 'udp6'
642
+ return [] if net_addr == '*:*' # abort for now
643
+ # replace * with 0:0:0:0:0:0:0:0
644
+ net_addr = net_addr.gsub(/^\*:/, '0:0:0:0:0:0:0:0:') if net_addr =~ /^*:(\d+)$/
645
+ # extract port
646
+ ip6 = /^(\S+):(\d+)$/.match(net_addr)
647
+ ip6addr = ip6[1]
648
+ ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
649
+ # replace []
650
+ host = ip_addr.host[1..ip_addr.host.size-2]
651
+ port = ip_addr.port
652
+ end
653
+ [host, port]
654
+ rescue URI::InvalidURIError => e
655
+ warn "Could not parse #{net_addr}, #{e}"
656
+ nil
657
+ end
658
+
659
+ def parse_sockstat_line(line)
660
+ # 1 - USER, 2 - COMMAND, 3 - PID, 4 - FD 5 - PROTO, 6 - LOCAL ADDRESS, 7 - FOREIGN ADDRESS
661
+ parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/.match(line)
662
+ return {} if parsed.nil?
663
+
664
+ # extract ip information
665
+ protocol = parsed[5].downcase
666
+ host, port = parse_net_address(parsed[6], protocol)
667
+ return {} if host.nil? or port.nil?
668
+
669
+ # extract process
670
+ process = parsed[2]
671
+
672
+ # extract PID
673
+ pid = parsed[3]
674
+ pid = pid.to_i if pid =~ /^\d+$/
675
+
676
+ # map tcp4 and udp4
677
+ protocol = 'tcp' if protocol.eql?('tcp4')
678
+ protocol = 'udp' if protocol.eql?('udp4')
679
+
680
+ {
681
+ 'port' => port,
682
+ 'address' => host,
683
+ 'protocol' => protocol,
684
+ 'process' => process,
685
+ 'pid' => pid,
686
+ }
687
+ end
688
+ end
689
+
690
+ class SolarisPorts < FreeBsdPorts
691
+ include SolarisNetstatParser
692
+
693
+ def info
694
+ # extract all port info
695
+ cmd = inspec.command('netstat -an -f inet -f inet6')
696
+ return nil if cmd.exit_status.to_i != 0
697
+
698
+ # parse the content
699
+ netstat_ports = parse_netstat(cmd.stdout)
700
+
701
+ # filter all ports, where we `listen`
702
+ listen = netstat_ports.select { |val|
703
+ !val['state'].nil? && 'listen'.casecmp(val['state']) == 0
704
+ }
705
+
706
+ # map the data
707
+ ports = listen.map { |val|
708
+ protocol = val['protocol']
709
+ local_addr = val['local-address']
710
+
711
+ # solaris uses 127.0.0.1.57455 instead 127.0.0.1:57455, lets convert the
712
+ # the last . to :
713
+ local_addr[local_addr.rindex('.')] = ':'
714
+ host, port = parse_net_address(local_addr, protocol)
715
+ if host.nil?
716
+ nil
717
+ else
718
+ {
719
+ 'port' => port,
720
+ 'address' => host,
721
+ 'protocol' => protocol,
722
+ }
723
+ end
724
+ }
725
+ ports.compact
726
+ end
727
+ end
728
+
729
+ # extracts information from netstat for hpux
730
+ class HpuxPorts < FreeBsdPorts
731
+ def info
732
+ ## Can't use 'netstat -an -f inet -f inet6' as the latter -f option overrides the former one and return only inet ports
733
+ cmd1 = inspec.command('netstat -an -f inet')
734
+ return nil if cmd1.exit_status.to_i != 0
735
+ cmd2 = inspec.command('netstat -an -f inet6')
736
+ return nil if cmd2.exit_status.to_i != 0
737
+ cmd = cmd1.stdout + cmd2.stdout
738
+ ports = []
739
+ # parse all lines
740
+ cmd.each_line do |line|
741
+ port_info = parse_netstat_line(line)
742
+ next unless %w{tcp tcp6 udp udp6}.include?(port_info['protocol'])
743
+ ports.push(port_info)
744
+ end
745
+ # select all ports, where we `listen`
746
+ ports.select { |val| val if 'listen'.casecmp(val['state']) == 0 }
747
+ end
748
+
749
+ def parse_netstat_line(line)
750
+ # parse each line
751
+ # 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - (state)
752
+ parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?/.match(line)
753
+
754
+ return {} if parsed.nil? || line.match(/^proto/i) || line.match(/^active/i)
755
+ protocol = parsed[1].downcase
756
+ state = parsed[6].nil?? ' ' : parsed[6].downcase
757
+ local_addr = parsed[4]
758
+ local_addr[local_addr.rindex('.')] = ':'
759
+ # extract host and port information
760
+ host, port = parse_net_address(local_addr, protocol)
761
+ return {} if host.nil?
762
+ # map data
763
+ {
764
+ 'port' => port,
765
+ 'address' => host,
766
+ 'protocol' => protocol,
767
+ 'state' => state,
768
+ }
769
+ end
770
+ end
771
+ end