inspec 1.0.0 → 1.35.1

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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +933 -101
  3. data/Gemfile +8 -17
  4. data/MAINTAINERS.md +2 -1
  5. data/MAINTAINERS.toml +9 -4
  6. data/README.md +49 -28
  7. data/Rakefile +54 -39
  8. data/bin/inspec +1 -1
  9. data/docs/dsl_inspec.md +17 -4
  10. data/docs/dsl_resource.md +1 -1
  11. data/docs/habitat.md +192 -0
  12. data/docs/inspec_and_friends.md +4 -2
  13. data/docs/matchers.md +11 -0
  14. data/docs/migration.md +293 -0
  15. data/docs/profiles.md +124 -76
  16. data/docs/resources/aide_conf.md.erb +81 -0
  17. data/docs/resources/apache_conf.md.erb +10 -10
  18. data/docs/resources/apt.md.erb +13 -13
  19. data/docs/resources/audit_policy.md.erb +10 -10
  20. data/docs/resources/auditd_conf.md.erb +10 -10
  21. data/docs/resources/auditd_rules.md.erb +11 -11
  22. data/docs/resources/bash.md.erb +12 -12
  23. data/docs/resources/bond.md.erb +15 -15
  24. data/docs/resources/bridge.md.erb +11 -11
  25. data/docs/resources/bsd_service.md.erb +11 -11
  26. data/docs/resources/command.md.erb +23 -23
  27. data/docs/resources/crontab.md.erb +84 -0
  28. data/docs/resources/csv.md.erb +10 -10
  29. data/docs/resources/dh_params.md +191 -0
  30. data/docs/resources/directory.md.erb +8 -8
  31. data/docs/resources/docker.md.erb +160 -0
  32. data/docs/resources/docker_container.md.erb +89 -0
  33. data/docs/resources/docker_image.md.erb +86 -0
  34. data/docs/resources/etc_group.md.erb +16 -16
  35. data/docs/resources/etc_hosts.md.erb +62 -0
  36. data/docs/resources/file.md.erb +80 -58
  37. data/docs/resources/gem.md.erb +32 -13
  38. data/docs/resources/group.md.erb +12 -12
  39. data/docs/resources/grub_conf.md.erb +11 -11
  40. data/docs/resources/host.md.erb +27 -18
  41. data/docs/resources/http.md.erb +104 -0
  42. data/docs/resources/iis_app.md.erb +126 -0
  43. data/docs/resources/iis_site.md.erb +17 -17
  44. data/docs/resources/inetd_conf.md.erb +10 -10
  45. data/docs/resources/ini.md.erb +9 -9
  46. data/docs/resources/interface.md.erb +13 -13
  47. data/docs/resources/iptables.md.erb +21 -14
  48. data/docs/resources/json.md.erb +11 -11
  49. data/docs/resources/kernel_module.md.erb +73 -12
  50. data/docs/resources/kernel_parameter.md.erb +12 -12
  51. data/docs/resources/key_rsa.md.erb +70 -0
  52. data/docs/resources/launchd_service.md.erb +11 -11
  53. data/docs/resources/limits_conf.md.erb +10 -10
  54. data/docs/resources/login_def.md.erb +12 -12
  55. data/docs/resources/mount.md.erb +14 -14
  56. data/docs/resources/mssql_session.md.erb +62 -0
  57. data/docs/resources/mysql_conf.md.erb +14 -14
  58. data/docs/resources/mysql_session.md.erb +22 -10
  59. data/docs/resources/npm.md.erb +13 -13
  60. data/docs/resources/ntp_conf.md.erb +9 -9
  61. data/docs/resources/oneget.md.erb +11 -11
  62. data/docs/resources/oracledb_session.md.erb +53 -0
  63. data/docs/resources/os.md.erb +17 -17
  64. data/docs/resources/os_env.md.erb +12 -12
  65. data/docs/resources/package.md.erb +29 -16
  66. data/docs/resources/parse_config.md.erb +18 -18
  67. data/docs/resources/parse_config_file.md.erb +29 -23
  68. data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +17 -17
  69. data/docs/resources/pip.md.erb +15 -15
  70. data/docs/resources/port.md.erb +23 -23
  71. data/docs/resources/postgres_conf.md.erb +15 -13
  72. data/docs/resources/postgres_hba_conf.md.erb +104 -0
  73. data/docs/resources/postgres_ident_conf.md.erb +87 -0
  74. data/docs/resources/postgres_session.md.erb +28 -21
  75. data/docs/resources/powershell.md.erb +14 -14
  76. data/docs/resources/processes.md.erb +123 -0
  77. data/docs/resources/rabbitmq_config.md.erb +55 -0
  78. data/docs/resources/registry_key.md.erb +44 -21
  79. data/docs/resources/runit_service.md.erb +11 -11
  80. data/docs/resources/security_policy.md.erb +10 -10
  81. data/docs/resources/service.md.erb +18 -18
  82. data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +19 -19
  83. data/docs/resources/ssh_config.md.erb +13 -13
  84. data/docs/resources/sshd_config.md.erb +15 -15
  85. data/docs/resources/ssl.md.erb +13 -13
  86. data/docs/resources/sys_info.md.erb +10 -10
  87. data/docs/resources/systemd_service.md.erb +11 -11
  88. data/docs/resources/sysv_service.md.erb +11 -11
  89. data/docs/resources/upstart_service.md.erb +11 -11
  90. data/docs/resources/user.md.erb +20 -20
  91. data/docs/resources/users.md.erb +19 -19
  92. data/docs/resources/vbscript.md.erb +13 -13
  93. data/docs/resources/virtualization.md.erb +71 -0
  94. data/docs/resources/windows_feature.md.erb +10 -10
  95. data/docs/resources/windows_task.md.erb +103 -0
  96. data/docs/resources/wmi.md.erb +10 -10
  97. data/docs/resources/x509_certificate.md.erb +146 -0
  98. data/docs/resources/xinetd_conf.md.erb +17 -17
  99. data/docs/resources/xml.md.erb +75 -0
  100. data/docs/resources/yaml.md.erb +25 -11
  101. data/docs/resources/yum.md.erb +24 -16
  102. data/docs/resources/zfs_dataset.md.erb +63 -0
  103. data/docs/resources/zfs_pool.md.erb +57 -0
  104. data/docs/ruby_usage.md +50 -0
  105. data/docs/shared/matcher_cmp.md.erb +4 -6
  106. data/examples/inheritance/README.md +49 -3
  107. data/examples/inheritance/controls/example.rb +4 -1
  108. data/examples/inheritance/inspec.yml +3 -1
  109. data/examples/meta-profile/README.md +29 -3
  110. data/examples/meta-profile/controls/example.rb +10 -5
  111. data/examples/meta-profile/inspec.yml +4 -8
  112. data/examples/profile/README.md +1 -1
  113. data/examples/profile/controls/example.rb +0 -1
  114. data/examples/profile/controls/gordon.rb +0 -1
  115. data/examples/profile/inspec.yml +1 -1
  116. data/examples/profile-attribute/inspec.yml +1 -1
  117. data/examples/profile-sensitive/README.md +29 -0
  118. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
  119. data/examples/profile-sensitive/controls/sensitive.rb +9 -0
  120. data/examples/profile-sensitive/inspec.yml +8 -0
  121. data/inspec.gemspec +10 -2
  122. data/lib/bundles/inspec-artifact/README.md +1 -0
  123. data/lib/bundles/inspec-artifact/cli.rb +284 -0
  124. data/lib/bundles/inspec-artifact.rb +7 -0
  125. data/lib/bundles/inspec-compliance/README.md +44 -15
  126. data/lib/bundles/inspec-compliance/api.rb +135 -29
  127. data/lib/bundles/inspec-compliance/cli.rb +128 -21
  128. data/lib/bundles/inspec-compliance/configuration.rb +8 -0
  129. data/lib/bundles/inspec-compliance/http.rb +26 -19
  130. data/lib/bundles/inspec-compliance/target.rb +50 -25
  131. data/lib/bundles/inspec-habitat/cli.rb +31 -0
  132. data/lib/bundles/inspec-habitat/log.rb +10 -0
  133. data/lib/bundles/inspec-habitat/profile.rb +372 -0
  134. data/lib/bundles/inspec-habitat.rb +12 -0
  135. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +0 -1
  136. data/lib/bundles/inspec-init/templates/profile/inspec.yml +1 -1
  137. data/lib/bundles/inspec-supermarket/api.rb +7 -5
  138. data/lib/bundles/inspec-supermarket/cli.rb +2 -2
  139. data/lib/fetchers/git.rb +4 -2
  140. data/lib/fetchers/local.rb +4 -1
  141. data/lib/fetchers/url.rb +64 -10
  142. data/lib/inspec/archive/tar.rb +1 -1
  143. data/lib/inspec/backend.rb +26 -3
  144. data/lib/inspec/base_cli.rb +44 -7
  145. data/lib/inspec/cached_fetcher.rb +2 -2
  146. data/lib/inspec/cli.rb +34 -12
  147. data/lib/inspec/completions/fish.sh.erb +34 -0
  148. data/lib/inspec/control_eval_context.rb +0 -2
  149. data/lib/inspec/dependencies/cache.rb +0 -1
  150. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  151. data/lib/inspec/dependencies/lockfile.rb +13 -6
  152. data/lib/inspec/dependencies/requirement.rb +6 -6
  153. data/lib/inspec/dependencies/resolver.rb +4 -4
  154. data/lib/inspec/dsl.rb +3 -5
  155. data/lib/inspec/env_printer.rb +9 -1
  156. data/lib/inspec/exceptions.rb +8 -0
  157. data/lib/inspec/fetcher.rb +1 -1
  158. data/lib/inspec/file_provider.rb +47 -11
  159. data/lib/inspec/library_eval_context.rb +1 -1
  160. data/lib/inspec/metadata.rb +62 -18
  161. data/lib/inspec/method_source.rb +24 -0
  162. data/lib/inspec/objects/attribute.rb +21 -0
  163. data/lib/inspec/objects/control.rb +21 -3
  164. data/lib/inspec/objects/each_loop.rb +2 -1
  165. data/lib/inspec/objects/list.rb +1 -1
  166. data/lib/inspec/objects/or_test.rb +17 -2
  167. data/lib/inspec/objects/tag.rb +27 -0
  168. data/lib/inspec/objects/test.rb +9 -8
  169. data/lib/inspec/objects.rb +1 -0
  170. data/lib/inspec/plugins/fetcher.rb +4 -5
  171. data/lib/inspec/plugins/resource.rb +1 -2
  172. data/lib/inspec/plugins/source_reader.rb +3 -3
  173. data/lib/inspec/plugins.rb +1 -1
  174. data/lib/inspec/polyfill.rb +0 -1
  175. data/lib/inspec/profile.rb +92 -23
  176. data/lib/inspec/profile_context.rb +1 -1
  177. data/lib/inspec/profile_vendor.rb +66 -0
  178. data/lib/inspec/resource.rb +29 -3
  179. data/lib/inspec/rspec_json_formatter.rb +672 -285
  180. data/lib/inspec/rule.rb +12 -16
  181. data/lib/inspec/runner.rb +26 -15
  182. data/lib/inspec/runner_rspec.rb +1 -0
  183. data/lib/inspec/runtime_profile.rb +26 -0
  184. data/lib/inspec/schema.rb +185 -0
  185. data/lib/inspec/secrets/yaml.rb +8 -1
  186. data/lib/inspec/secrets.rb +1 -1
  187. data/lib/inspec/shell.rb +92 -22
  188. data/lib/inspec/source_reader.rb +1 -1
  189. data/lib/inspec/version.rb +1 -1
  190. data/lib/inspec.rb +1 -1
  191. data/lib/matchers/matchers.rb +67 -17
  192. data/lib/resources/aide_conf.rb +162 -0
  193. data/lib/resources/apache.rb +0 -1
  194. data/lib/resources/apache_conf.rb +3 -3
  195. data/lib/resources/apt.rb +2 -1
  196. data/lib/resources/audit_policy.rb +0 -1
  197. data/lib/resources/auditd_conf.rb +1 -2
  198. data/lib/resources/auditd_rules.rb +5 -5
  199. data/lib/resources/bond.rb +1 -1
  200. data/lib/resources/bridge.rb +1 -1
  201. data/lib/resources/command.rb +5 -3
  202. data/lib/resources/crontab.rb +102 -0
  203. data/lib/resources/csv.rb +18 -1
  204. data/lib/resources/dh_params.rb +84 -0
  205. data/lib/resources/directory.rb +7 -3
  206. data/lib/resources/docker.rb +198 -0
  207. data/lib/resources/docker_container.rb +114 -0
  208. data/lib/resources/docker_image.rb +96 -0
  209. data/lib/resources/etc_group.rb +2 -3
  210. data/lib/resources/etc_hosts.rb +81 -0
  211. data/lib/resources/file.rb +88 -30
  212. data/lib/resources/gem.rb +26 -6
  213. data/lib/resources/groups.rb +4 -5
  214. data/lib/resources/grub_conf.rb +3 -4
  215. data/lib/resources/host.rb +175 -35
  216. data/lib/resources/http.rb +73 -0
  217. data/lib/resources/iis_app.rb +103 -0
  218. data/lib/resources/iis_site.rb +23 -0
  219. data/lib/resources/inetd_conf.rb +9 -4
  220. data/lib/resources/interface.rb +4 -3
  221. data/lib/resources/json.rb +37 -14
  222. data/lib/resources/kernel_module.rb +77 -10
  223. data/lib/resources/kernel_parameter.rb +0 -1
  224. data/lib/resources/key_rsa.rb +67 -0
  225. data/lib/resources/limits_conf.rb +3 -4
  226. data/lib/resources/login_def.rb +2 -3
  227. data/lib/resources/mount.rb +34 -4
  228. data/lib/resources/mssql_session.rb +81 -12
  229. data/lib/resources/mysql.rb +0 -1
  230. data/lib/resources/mysql_conf.rb +1 -2
  231. data/lib/resources/mysql_session.rb +18 -7
  232. data/lib/resources/nginx_conf.rb +95 -0
  233. data/lib/resources/ntp_conf.rb +2 -3
  234. data/lib/resources/oracledb_session.rb +138 -0
  235. data/lib/resources/os_env.rb +7 -5
  236. data/lib/resources/package.rb +70 -13
  237. data/lib/resources/packages.rb +111 -0
  238. data/lib/resources/parse_config.rb +3 -4
  239. data/lib/resources/passwd.rb +4 -5
  240. data/lib/resources/pip.rb +29 -21
  241. data/lib/resources/port.rb +133 -14
  242. data/lib/resources/postgres.rb +53 -43
  243. data/lib/resources/postgres_conf.rb +40 -13
  244. data/lib/resources/postgres_hba_conf.rb +101 -0
  245. data/lib/resources/postgres_ident_conf.rb +79 -0
  246. data/lib/resources/postgres_session.rb +28 -20
  247. data/lib/resources/powershell.rb +2 -2
  248. data/lib/resources/processes.rb +61 -26
  249. data/lib/resources/rabbitmq_conf.rb +53 -0
  250. data/lib/resources/registry_key.rb +34 -11
  251. data/lib/resources/security_policy.rb +2 -2
  252. data/lib/resources/service.rb +17 -7
  253. data/lib/resources/ssh_conf.rb +17 -6
  254. data/lib/resources/ssl.rb +3 -4
  255. data/lib/resources/sys_info.rb +1 -1
  256. data/lib/resources/toml.rb +26 -0
  257. data/lib/resources/users.rb +41 -41
  258. data/lib/resources/virtualization.rb +252 -0
  259. data/lib/resources/windows_task.rb +106 -0
  260. data/lib/resources/x509_certificate.rb +143 -0
  261. data/lib/resources/xinetd.rb +18 -2
  262. data/lib/resources/xml.rb +27 -0
  263. data/lib/resources/yaml.rb +10 -2
  264. data/lib/resources/yum.rb +25 -7
  265. data/lib/resources/zfs_dataset.rb +60 -0
  266. data/lib/resources/zfs_pool.rb +49 -0
  267. data/lib/source_readers/flat.rb +2 -1
  268. data/lib/source_readers/inspec.rb +30 -11
  269. data/lib/utils/command_wrapper.rb +3 -3
  270. data/lib/utils/database_helpers.rb +77 -0
  271. data/lib/utils/erlang_parser.rb +192 -0
  272. data/lib/utils/filter.rb +2 -1
  273. data/lib/utils/filter_array.rb +0 -1
  274. data/lib/utils/find_files.rb +1 -2
  275. data/lib/utils/latest_version.rb +21 -0
  276. data/lib/utils/nginx_parser.rb +76 -0
  277. data/lib/utils/object_traversal.rb +5 -4
  278. data/lib/utils/parser.rb +23 -4
  279. data/lib/utils/plugin_registry.rb +3 -3
  280. data/lib/utils/simpleconfig.rb +25 -8
  281. data/lib/utils/spdx.rb +13 -0
  282. data/lib/utils/spdx.txt +344 -0
  283. metadata +168 -22
  284. data/docs/cli.md +0 -342
  285. data/docs/resources/process.md.erb +0 -73
  286. /data/docs/{plugin_kitchen_inspec.html.md → plugin_kitchen_inspec.md} +0 -0
data/lib/fetchers/git.rb CHANGED
@@ -24,7 +24,7 @@ module Fetchers
24
24
  # you got to this file during debugging, you may want to look at the
25
25
  # omnibus source for hints.
26
26
  #
27
- class Git < Inspec.fetcher(1)
27
+ class Git < Inspec.fetcher(1) # rubocop:disable ClassLength
28
28
  name 'git'
29
29
  priority 200
30
30
 
@@ -44,6 +44,8 @@ module Fetchers
44
44
 
45
45
  def fetch(dir)
46
46
  @repo_directory = dir
47
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
48
+
47
49
  if cloned?
48
50
  checkout
49
51
  else
@@ -86,7 +88,7 @@ module Fetchers
86
88
  cmd = shellout("git ls-remote \"#{@remote_url}\" \"#{ref_name}*\"")
87
89
  ref = parse_ls_remote(cmd.stdout, ref_name)
88
90
  if !ref
89
- fail "Unable to resolve #{ref_name} to a specific git commit for #{@remote_url}"
91
+ raise "Unable to resolve #{ref_name} to a specific git commit for #{@remote_url}"
90
92
  end
91
93
  ref
92
94
  end
@@ -2,6 +2,8 @@
2
2
  # author: Dominik Richter
3
3
  # author: Christoph Hartmann
4
4
 
5
+ require 'openssl'
6
+
5
7
  module Fetchers
6
8
  class Local < Inspec.fetcher(1)
7
9
  name 'local'
@@ -65,7 +67,8 @@ module Fetchers
65
67
 
66
68
  def sha256
67
69
  return nil if File.directory?(@target)
68
- @archive_shasum ||= Digest::SHA256.hexdigest File.read(@target)
70
+ @archive_shasum ||=
71
+ OpenSSL::Digest::SHA256.digest(File.read(@target)).unpack('H*')[0]
69
72
  end
70
73
 
71
74
  def resolved_source
data/lib/fetchers/url.rb CHANGED
@@ -3,12 +3,12 @@
3
3
  # author: Christoph Hartmann
4
4
 
5
5
  require 'uri'
6
- require 'digest'
6
+ require 'openssl'
7
7
  require 'tempfile'
8
8
  require 'open-uri'
9
9
 
10
10
  module Fetchers
11
- class Url < Inspec.fetcher(1)
11
+ class Url < Inspec.fetcher(1) # rubocop:disable Metrics/ClassLength
12
12
  MIME_TYPES = {
13
13
  'application/x-zip-compressed' => '.zip',
14
14
  'application/zip' => '.zip',
@@ -37,8 +37,8 @@ module Fetchers
37
37
  nil
38
38
  end
39
39
 
40
- # Transforms a browser github url to github tar url
41
- # We distinguish between three different Github URL types:
40
+ # Transforms a browser github/bitbucket url to github/bitbucket tar url
41
+ # We distinguish between three different Github/Bitbucket URL types:
42
42
  # - Master URL
43
43
  # - Branch URL
44
44
  # - Commit URL
@@ -46,22 +46,39 @@ module Fetchers
46
46
  # master url:
47
47
  # https://github.com/nathenharvey/tmp_compliance_profile/ is transformed to
48
48
  # https://github.com/nathenharvey/tmp_compliance_profile/archive/master.tar.gz
49
+ # https://bitbucket.org/username/repo is transformed to
50
+ # https://bitbucket.org/username/repo/get/master.tar.gz
49
51
  #
50
- # github branch:
52
+ # branch:
51
53
  # https://github.com/hardening-io/tests-os-hardening/tree/2.0 is transformed to
52
54
  # https://github.com/hardening-io/tests-os-hardening/archive/2.0.tar.gz
55
+ # https://bitbucket.org/username/repo/branch/branchname is transformed to
56
+ # https://bitbucket.org/username/repo/get/newbranch.tar.gz
53
57
  #
54
- # github commit:
58
+ # commit:
55
59
  # https://github.com/hardening-io/tests-os-hardening/tree/48bd4388ddffde68badd83aefa654e7af3231876
56
60
  # is transformed to
57
61
  # https://github.com/hardening-io/tests-os-hardening/archive/48bd4388ddffde68badd83aefa654e7af3231876.tar.gz
62
+ # https://bitbucket.org/username/repo/commits/95ce1f83d5bbe9eec34c5973f6894617e8d6d8cc is transformed to
63
+ # https://bitbucket.org/username/repo/get/95ce1f83d5bbe9eec34c5973f6894617e8d6d8cc.tar.gz
64
+
58
65
  GITHUB_URL_REGEX = %r{^https?://(www\.)?github\.com/(?<user>[\w-]+)/(?<repo>[\w-]+)(\.git)?(/)?$}
59
66
  GITHUB_URL_WITH_TREE_REGEX = %r{^https?://(www\.)?github\.com/(?<user>[\w-]+)/(?<repo>[\w-]+)/tree/(?<commit>[\w\.]+)(/)?$}
67
+ BITBUCKET_URL_REGEX = %r{^https?://(www\.)?bitbucket\.org/(?<user>[\w-]+)/(?<repo>[\w-]+)(\.git)?(/)?$}
68
+ BITBUCKET_URL_BRANCH_REGEX = %r{^https?://(www\.)?bitbucket\.org/(?<user>[\w-]+)/(?<repo>[\w-]+)/branch/(?<branch>[\w\.]+)(/)?$}
69
+ BITBUCKET_URL_COMMIT_REGEX = %r{^https?://(www\.)?bitbucket\.org/(?<user>[\w-]+)/(?<repo>[\w-]+)/commits/(?<commit>[\w\.]+)(/)?$}
70
+
60
71
  def self.transform(target)
61
72
  transformed_target = if m = GITHUB_URL_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
62
73
  "https://github.com/#{m[:user]}/#{m[:repo]}/archive/master.tar.gz"
63
74
  elsif m = GITHUB_URL_WITH_TREE_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
64
75
  "https://github.com/#{m[:user]}/#{m[:repo]}/archive/#{m[:commit]}.tar.gz"
76
+ elsif m = BITBUCKET_URL_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
77
+ "https://bitbucket.org/#{m[:user]}/#{m[:repo]}/get/master.tar.gz"
78
+ elsif m = BITBUCKET_URL_BRANCH_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
79
+ "https://bitbucket.org/#{m[:user]}/#{m[:repo]}/get/#{m[:branch]}.tar.gz"
80
+ elsif m = BITBUCKET_URL_COMMIT_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
81
+ "https://bitbucket.org/#{m[:user]}/#{m[:repo]}/get/#{m[:commit]}.tar.gz"
65
82
  end
66
83
 
67
84
  if transformed_target
@@ -79,6 +96,8 @@ module Fetchers
79
96
  @insecure = opts['insecure']
80
97
  @token = opts['token']
81
98
  @config = opts
99
+ @archive_path = nil
100
+ @temp_archive_path = nil
82
101
  end
83
102
 
84
103
  def fetch(path)
@@ -101,7 +120,7 @@ module Fetchers
101
120
 
102
121
  def sha256
103
122
  file = @archive_path || temp_archive_path
104
- Digest::SHA256.hexdigest File.read(file)
123
+ OpenSSL::Digest::SHA256.digest(File.read(file)).unpack('H*')[0]
105
124
  end
106
125
 
107
126
  def file_type_from_remote(remote)
@@ -124,9 +143,6 @@ module Fetchers
124
143
  def download_archive_to_temp
125
144
  return @temp_archive_path if ! @temp_archive_path.nil?
126
145
  Inspec::Log.debug("Fetching URL: #{@target}")
127
- http_opts = {}
128
- http_opts['ssl_verify_mode'.to_sym] = OpenSSL::SSL::VERIFY_NONE if @insecure
129
- http_opts['Authorization'] = "Bearer #{@token}" if @token
130
146
  remote = open(@target, http_opts)
131
147
  @archive_type = file_type_from_remote(remote) # side effect :(
132
148
  archive = Tempfile.new(['inspec-dl-', @archive_type])
@@ -141,10 +157,48 @@ module Fetchers
141
157
  def download_archive(path)
142
158
  download_archive_to_temp
143
159
  final_path = "#{path}#{@archive_type}"
160
+ FileUtils.mkdir_p(File.dirname(final_path))
144
161
  FileUtils.mv(temp_archive_path, final_path)
145
162
  Inspec::Log.debug("Fetched archive moved to: #{final_path}")
146
163
  @temp_archive_path = nil
147
164
  final_path
148
165
  end
166
+
167
+ def http_opts
168
+ opts = {}
169
+ opts[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE if @insecure
170
+
171
+ if @config['server_type'] == 'automate'
172
+ opts['chef-delivery-enterprise'] = @config['automate']['ent']
173
+ if @config['automate']['token_type'] == 'dctoken'
174
+ opts['x-data-collector-token'] = @config['token']
175
+ else
176
+ opts['chef-delivery-user'] = @config['user']
177
+ opts['chef-delivery-token'] = @config['token']
178
+ end
179
+ elsif @token
180
+ opts['Authorization'] = "Bearer #{@token}"
181
+ end
182
+
183
+ # Do not send any headers that have nil values.
184
+ # Net::HTTP does not gracefully handle this situation.
185
+ check_for_missing_values!(opts)
186
+
187
+ opts
188
+ end
189
+
190
+ def check_for_missing_values!(opts)
191
+ keys_missing_values = opts.keys.delete_if do |k|
192
+ if opts[k].nil?
193
+ false
194
+ elsif opts[k].respond_to?(:empty?) && opts[k].empty?
195
+ false
196
+ else
197
+ true
198
+ end
199
+ end
200
+ raise 'Unable to fetch profile - the following HTTP headers have no value: ' \
201
+ "#{keys_missing_values.join(', ')}" unless keys_missing_values.empty?
202
+ end
149
203
  end
150
204
  end
@@ -17,7 +17,7 @@ module Inspec::Archive
17
17
  tar.mkdir(input_filename, stat.mode)
18
18
  else
19
19
  tar.add_file_simple(input_filename, stat.mode, stat.size) do |io|
20
- io.write(File.read(path))
20
+ io.write(File.binread(path))
21
21
  end
22
22
  end
23
23
  end
@@ -1,6 +1,5 @@
1
1
  # encoding: utf-8
2
2
  # copyright: 2015, Dominik Richter
3
- # license: All rights reserved
4
3
  # author: Dominik Richter
5
4
  # author: Christoph Hartmann
6
5
 
@@ -8,6 +7,27 @@ require 'train'
8
7
 
9
8
  module Inspec
10
9
  module Backend
10
+ module Base
11
+ attr_accessor :profile
12
+
13
+ # Provide a shorthand to retrieve the inspec version from within a profile
14
+ #
15
+ # @return [String] inspec version
16
+ def version
17
+ Inspec::VERSION
18
+ end
19
+
20
+ # Ruby internal for printing a nice name for this class
21
+ def to_s
22
+ 'Inspec::Backend::Class'
23
+ end
24
+
25
+ # Ruby internal for pretty-printing a summary for this class
26
+ def inspect
27
+ "Inspec::Backend::Class @transport=#{backend.class}"
28
+ end
29
+ end
30
+
11
31
  # Create the transport backend with aggregated resources.
12
32
  #
13
33
  # @param [Hash] config for the transport backend
@@ -17,18 +37,21 @@ module Inspec
17
37
  name = Train.validate_backend(conf)
18
38
  transport = Train.create(name, conf)
19
39
  if transport.nil?
20
- fail "Can't find transport backend '#{name}'."
40
+ raise "Can't find transport backend '#{name}'."
21
41
  end
22
42
 
23
43
  connection = transport.connection
24
44
  if connection.nil?
25
- fail "Can't connect to transport backend '#{name}'."
45
+ raise "Can't connect to transport backend '#{name}'."
26
46
  end
27
47
 
28
48
  cls = Class.new do
49
+ include Base
50
+
29
51
  define_method :backend do
30
52
  connection
31
53
  end
54
+
32
55
  Inspec::Resource.registry.each do |id, r|
33
56
  define_method id.to_sym do |*args|
34
57
  r.new(self, id.to_s, *args)
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'thor'
6
6
  require 'inspec/log'
7
+ require 'inspec/profile_vendor'
7
8
 
8
9
  module Inspec
9
10
  class BaseCLI < Thor # rubocop:disable Metrics/ClassLength
@@ -18,7 +19,7 @@ module Inspec
18
19
  desc: 'Specify the login port for a remote scan.'
19
20
  option :user, type: :string,
20
21
  desc: 'The login user for a remote scan.'
21
- option :password, type: :string,
22
+ option :password, type: :string, lazy_default: -1,
22
23
  desc: 'Login password for a remote scan, if required.'
23
24
  option :key_files, aliases: :i, type: :array,
24
25
  desc: 'Login key or certificate file for a remote scan.'
@@ -26,20 +27,24 @@ module Inspec
26
27
  desc: 'Login path to use when connecting to the target (WinRM).'
27
28
  option :sudo, type: :boolean,
28
29
  desc: 'Run scans with sudo. Only activates on Unix and non-root user.'
29
- option :sudo_password, type: :string,
30
+ option :sudo_password, type: :string, lazy_default: -1,
30
31
  desc: 'Specify a sudo password, if it is required.'
31
32
  option :sudo_options, type: :string,
32
33
  desc: 'Additional sudo options for a remote scan.'
33
34
  option :sudo_command, type: :string,
34
35
  desc: 'Alternate command for sudo.'
36
+ option :shell, type: :boolean,
37
+ desc: 'Run scans in a subshell. Only activates on Unix.'
38
+ option :shell_options, type: :string,
39
+ desc: 'Additional shell options.'
40
+ option :shell_command, type: :string,
41
+ desc: 'Specify a particular shell to use.'
35
42
  option :ssl, type: :boolean,
36
43
  desc: 'Use SSL for transport layer encryption (WinRM).'
37
44
  option :self_signed, type: :boolean,
38
45
  desc: 'Allow remote scans with self-signed certificates (WinRM).'
39
46
  option :json_config, type: :string,
40
47
  desc: 'Read configuration from JSON file (`-` reads from stdin).'
41
- option :log_level, aliases: :l, type: :string,
42
- desc: 'Set the log level: info (default), debug, warn, error'
43
48
  end
44
49
 
45
50
  def self.profile_options
@@ -53,7 +58,7 @@ module Inspec
53
58
  option :controls, type: :array,
54
59
  desc: 'A list of controls to run. Ignore all other tests.'
55
60
  option :format, type: :string,
56
- desc: 'Which formatter to use: cli, progress, documentation, json, json-min'
61
+ desc: 'Which formatter to use: cli, progress, documentation, json, json-min, junit'
57
62
  option :color, type: :boolean, default: true,
58
63
  desc: 'Use colors in output.'
59
64
  option :attrs, type: :array,
@@ -76,7 +81,7 @@ module Inspec
76
81
  runner = Inspec::Runner.new(o)
77
82
  targets.each { |target| runner.add_target(target) }
78
83
  exit runner.run
79
- rescue RuntimeError, Train::UserError => e
84
+ rescue ArgumentError, RuntimeError, Train::UserError => e
80
85
  $stderr.puts e.message
81
86
  exit 1
82
87
  end
@@ -95,6 +100,23 @@ module Inspec
95
100
  end
96
101
 
97
102
  def opts
103
+ o = merged_opts
104
+
105
+ # Due to limitations in Thor it is not possible to set an argument to be
106
+ # both optional and its value to be mandatory. E.g. the user supplying
107
+ # the --password argument is optional and not always required, but
108
+ # whenever it is used, it requires a value. Handle options that were
109
+ # defined above and require a value here:
110
+ %w{password sudo-password}.each do |v|
111
+ id = v.tr('-', '_').to_sym
112
+ next unless o[id] == -1
113
+ raise ArgumentError, "Please provide a value for --#{v}. For example: --#{v}=hello."
114
+ end
115
+
116
+ o
117
+ end
118
+
119
+ def merged_opts
98
120
  # argv overrides json
99
121
  Thor::CoreExt::HashWithIndifferentAccess.new(options_json.merge(options))
100
122
  end
@@ -138,10 +160,25 @@ module Inspec
138
160
  $stderr.puts exception.message
139
161
  exit(1)
140
162
  else
141
- raise exception # rubocop:disable Style/SignalException
163
+ raise exception
142
164
  end
143
165
  end
144
166
 
167
+ def vendor_deps(path, opts)
168
+ profile_path = path || Dir.pwd
169
+ profile_vendor = Inspec::ProfileVendor.new(profile_path)
170
+
171
+ if (profile_vendor.cache_path.exist? || profile_vendor.lockfile.exist?) && !opts[:overwrite]
172
+ puts 'Profile is already vendored. Use --overwrite.'
173
+ return false
174
+ end
175
+
176
+ profile_vendor.vendor!
177
+ puts "Dependencies for profile #{profile_path} successfully vendored to #{profile_vendor.cache_path}"
178
+ rescue StandardError => e
179
+ pretty_handle_exception(e)
180
+ end
181
+
145
182
  def configure_logger(o)
146
183
  #
147
184
  # TODO(ssd): This is a big gross, but this configures the
@@ -12,7 +12,7 @@ module Inspec
12
12
  @fetcher = Inspec::Fetcher.resolve(target)
13
13
 
14
14
  if @fetcher.nil?
15
- fail("Could not fetch inspec profile in #{target.inspect}.")
15
+ raise("Could not fetch inspec profile in #{target.inspect}.")
16
16
  end
17
17
 
18
18
  @cache = cache
@@ -50,7 +50,7 @@ module Inspec
50
50
  def assert_cache_sanity!
51
51
  if target.respond_to?(:key?) && target.key?(:sha256)
52
52
  if fetcher.resolved_source[:sha256] != target[:sha256]
53
- fail <<EOF
53
+ raise <<EOF
54
54
  The remote source #{fetcher} no longer has the requested content:
55
55
 
56
56
  Request Content Hash: #{target[:sha256]}
data/lib/inspec/cli.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: utf-8
3
- # Copyright 2015 Dominik Richter. All rights reserved.
3
+ # Copyright 2015 Dominik Richter
4
4
  # author: Dominik Richter
5
5
  # author: Christoph Hartmann
6
6
 
@@ -9,10 +9,12 @@ require 'thor'
9
9
  require 'json'
10
10
  require 'pp'
11
11
  require 'utils/json_log'
12
+ require 'utils/latest_version'
12
13
  require 'inspec/base_cli'
13
14
  require 'inspec/plugins'
14
15
  require 'inspec/runner_mock'
15
16
  require 'inspec/env_printer'
17
+ require 'inspec/schema'
16
18
 
17
19
  class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
18
20
  class_option :log_level, aliases: :l, type: :string,
@@ -34,6 +36,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
34
36
  diagnose
35
37
  o = opts.dup
36
38
  o[:ignore_supports] = true
39
+ o[:backend] = Inspec::Backend.create(target: 'mock://')
37
40
 
38
41
  profile = Inspec::Profile.for_target(target, o)
39
42
  dst = o[:output].to_s
@@ -58,8 +61,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
58
61
  def check(path) # rubocop:disable Metrics/AbcSize
59
62
  diagnose
60
63
  o = opts.dup
61
- # configure_logger(o) # we do not need a logger for check yet
62
64
  o[:ignore_supports] = true # we check for integrity only
65
+ o[:backend] = Inspec::Backend.create(target: 'mock://')
63
66
 
64
67
  # run check
65
68
  profile = Inspec::Profile.for_target(path, o)
@@ -103,14 +106,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
103
106
  pretty_handle_exception(e)
104
107
  end
105
108
 
106
- desc 'vendor', 'Download all dependencies and generate a lockfile'
109
+ desc 'vendor PATH', 'Download all dependencies and generate a lockfile in a `vendor` directory'
110
+ option :overwrite, type: :boolean, default: false,
111
+ desc: 'Overwrite existing vendored dependencies and lockfile.'
107
112
  def vendor(path = nil)
108
- configure_logger(opts)
109
- profile = Inspec::Profile.for_target('./', opts.merge(cache: Inspec::Cache.new(path)))
110
- lockfile = profile.generate_lockfile
111
- File.write('inspec.lock', lockfile.to_yaml)
112
- rescue StandardError => e
113
- pretty_handle_exception(e)
113
+ o = opts.dup
114
+ vendor_deps(path, o)
114
115
  end
115
116
 
116
117
  desc 'archive PATH', 'archive a profile to tar.gz (default) or zip'
@@ -131,12 +132,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
131
132
  o = opts.dup
132
133
  o[:logger] = Logger.new(STDOUT)
133
134
  o[:logger].level = get_log_level(o.log_level)
135
+ o[:backend] = Inspec::Backend.create(target: 'mock://')
134
136
 
135
137
  profile = Inspec::Profile.for_target(path, o)
136
138
  result = profile.check
137
139
 
138
140
  if result && !opts[:ignore_errors] == false
139
- @logger.info 'Profile check failed. Please fix the profile before generating an archive.'
141
+ o[:logger].info 'Profile check failed. Please fix the profile before generating an archive.'
140
142
  return exit 1
141
143
  end
142
144
 
@@ -184,7 +186,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
184
186
  option :command, aliases: :c,
185
187
  desc: 'A single command string to run instead of launching the shell'
186
188
  option :format, type: :string, default: nil, hide: true,
187
- desc: 'Which formatter to use: cli, progress, documentation, json, json-min'
189
+ desc: 'Which formatter to use: cli, progress, documentation, json, json-min, junit'
188
190
  def shell_func
189
191
  diagnose
190
192
  o = opts.dup
@@ -220,10 +222,30 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
220
222
  pretty_handle_exception(e)
221
223
  end
222
224
 
225
+ desc 'schema NAME', 'print the JSON schema', hide: true
226
+ def schema(name)
227
+ puts Inspec::Schema.json(name)
228
+ rescue StandardError => e
229
+ puts e
230
+ puts "Valid schemas are #{Inspec::Schema.names.join(', ')}"
231
+ end
232
+
223
233
  desc 'version', 'prints the version of this tool'
234
+ option :format, type: :string
224
235
  def version
225
- puts Inspec::VERSION
236
+ if opts['format'] == 'json'
237
+ v = { version: Inspec::VERSION }
238
+ puts v.to_json
239
+ else
240
+ puts Inspec::VERSION
241
+ # display outdated version
242
+ latest = LatestInSpecVersion.new.latest
243
+ if Gem::Version.new(Inspec::VERSION) < Gem::Version.new(latest)
244
+ puts "\nYour version of InSpec is out of date! The latest version is #{latest}."
245
+ end
246
+ end
226
247
  end
248
+ map %w{-v --version} => :version
227
249
 
228
250
  private
229
251
 
@@ -0,0 +1,34 @@
1
+ function __fish_inspec_no_command --description 'Test if inspec has yet to be given the main command'
2
+ set -l cmd (commandline -opc)
3
+ test (count $cmd) -eq 1
4
+ end
5
+
6
+ function __fish_inspec_using_command
7
+ set -l cmd (commandline -opc)
8
+ set -q cmd[2]; and test "$argv[1]" = $cmd[2]
9
+ end
10
+
11
+ function __fish_inspec_using_command_and_no_subcommand
12
+ set -l cmd (commandline -opc)
13
+ test (count $cmd) -eq 2; and test "$argv[1]" = "$cmd[2]"
14
+ end
15
+
16
+ function __fish_inspec_using_subcommand --argument-names cmd_main cmd_sub
17
+ set -l cmd (commandline -opc)
18
+ set -q cmd[3]; and test "$cmd_main" = $cmd[2] -a "$cmd_sub" = $cmd[3]
19
+ end
20
+
21
+ <% top_level_commands_with_descriptions.each do |command_and_description| %>
22
+ <% command, description = command_and_description.split(':') %>
23
+ <% description.gsub!(/\\/, '') %>
24
+ # <%= command %> commands
25
+ complete -c inspec -f -n '__fish_inspec_no_command' -a <%= command %> -d "<%= description %>"
26
+ # <%= command %> help
27
+ complete -c inspec -f -n '__fish_inspec_using_command help' -a <%= command %> -d "<%= description %>"
28
+
29
+ <% (subcommands_with_commands_and_descriptions[command] || []).each do |command_and_description| %>
30
+ <% subcommand, description = command_and_description.split(':') %>
31
+ <% description.gsub!(/\\/, '') %>
32
+ complete -c inspec -f -n '__fish_inspec_using_command_and_no_subcommand <%= command %>' -a <%= subcommand %> -d "<%= description %>"
33
+ <% end %>
34
+ <% end %>
@@ -35,8 +35,6 @@ module Inspec
35
35
  # @param profile_context [Inspec::ProfileContext]
36
36
  # @param outer_dsl [OuterDSLClass]
37
37
  # @return [ProfileContextClass]
38
- #
39
- # rubocop:disable Lint/NestedMethodDefinition
40
38
  def self.create(profile_context, resources_dsl) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
41
39
  rule_class = rule_context(resources_dsl)
42
40
  profile_context_owner = profile_context
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require 'digest'
3
2
  require 'fileutils'
4
3
 
5
4
  module Inspec
@@ -14,9 +14,9 @@ module Inspec
14
14
  # @param cwd [String] Current working directory for relative path includes
15
15
  # @param vendor_path [String] Path to the vendor directory
16
16
  #
17
- def self.from_lockfile(lockfile, cwd, cache, backend)
17
+ def self.from_lockfile(lockfile, cwd, cache, backend, opts = {})
18
18
  dep_tree = lockfile.deps.map do |dep|
19
- Inspec::Requirement.from_lock_entry(dep, cwd, cache, backend)
19
+ Inspec::Requirement.from_lock_entry(dep, cwd, cache, backend, opts)
20
20
  end
21
21
 
22
22
  dep_list = flatten_dep_tree(dep_tree)
@@ -15,17 +15,23 @@ module Inspec
15
15
  new(lockfile_content)
16
16
  end
17
17
 
18
- def self.from_file(path)
19
- parsed_content = YAML.load(File.read(path))
18
+ def self.from_content(content)
19
+ parsed_content = YAML.load(content)
20
20
  version = parsed_content['lockfile_version']
21
- fail "No lockfile_version set in #{path}!" if version.nil?
21
+ raise "No lockfile_version set in #{path}!" if version.nil?
22
22
  validate_lockfile_version!(version.to_i)
23
23
  new(parsed_content)
24
24
  end
25
25
 
26
+ def self.from_file(path)
27
+ content = File.read(path)
28
+ from_content(content)
29
+ end
30
+
31
+ # rubocop:disable Style/GuardClause
26
32
  def self.validate_lockfile_version!(version)
27
33
  if version < MINIMUM_SUPPORTED_VERSION
28
- fail <<EOF
34
+ raise <<EOF
29
35
  This lockfile specifies a lockfile_version of #{version} which is
30
36
  lower than the minimum supported version #{MINIMUM_SUPPORTED_VERSION}.
31
37
 
@@ -34,7 +40,7 @@ Please create a new lockfile for this project by running:
34
40
  inspec vendor
35
41
  EOF
36
42
  elsif version > CURRENT_LOCKFILE_VERSION
37
- fail <<EOF
43
+ raise <<EOF
38
44
  This lockfile claims to be version #{version} which is greater than
39
45
  the most recent lockfile version(#{CURRENT_LOCKFILE_VERSION}).
40
46
 
@@ -43,6 +49,7 @@ used to create the lockfile.
43
49
  EOF
44
50
  end
45
51
  end
52
+ # rubocop:enable Style/GuardClause
46
53
 
47
54
  attr_reader :version, :deps
48
55
  def initialize(lockfile_content_hash)
@@ -75,7 +82,7 @@ EOF
75
82
  else
76
83
  # If we've gotten here, there is likely a mistake in the
77
84
  # lockfile version validation in the constructor.
78
- fail "No lockfile parser for version #{version}"
85
+ raise "No lockfile parser for version #{version}"
79
86
  end
80
87
  end
81
88
 
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require 'inspec/cached_fetcher'
3
3
  require 'inspec/dependencies/dependency_set'
4
- require 'digest'
5
4
 
6
5
  module Inspec
7
6
  #
@@ -10,20 +9,20 @@ module Inspec
10
9
  #
11
10
  class Requirement
12
11
  def self.from_metadata(dep, cache, opts)
13
- fail 'Cannot load empty dependency.' if dep.nil? || dep.empty?
12
+ raise 'Cannot load empty dependency.' if dep.nil? || dep.empty?
14
13
  new(dep[:name], dep[:version], cache, opts[:cwd], opts.merge(dep))
15
14
  end
16
15
 
17
- def self.from_lock_entry(entry, cwd, cache, backend)
16
+ def self.from_lock_entry(entry, cwd, cache, backend, opts = {})
18
17
  req = new(entry[:name],
19
18
  entry[:version_constraints],
20
19
  cache,
21
20
  cwd,
22
- entry[:resolved_source].merge(backend: backend))
21
+ entry[:resolved_source].merge(backend: backend).merge(opts))
23
22
 
24
23
  locked_deps = []
25
24
  Array(entry[:dependencies]).each do |dep_entry|
26
- locked_deps << Inspec::Requirement.from_lock_entry(dep_entry, cwd, cache, backend)
25
+ locked_deps << Inspec::Requirement.from_lock_entry(dep_entry, cwd, cache, backend, opts)
27
26
  end
28
27
  req.lock_deps(locked_deps)
29
28
  req
@@ -85,6 +84,7 @@ module Inspec
85
84
  @fetcher ||= Inspec::CachedFetcher.new(opts, @cache)
86
85
  end
87
86
 
87
+ # load dependencies of the dependency
88
88
  def dependencies
89
89
  @dependencies ||= profile.metadata.dependencies.map do |r|
90
90
  Inspec::Requirement.from_metadata(r, @cache, cwd: @cwd, backend: @backend)
@@ -95,9 +95,9 @@ module Inspec
95
95
  name
96
96
  end
97
97
 
98
+ # load the profile for the requirement
98
99
  def profile
99
100
  return @profile if ! @profile.nil?
100
-
101
101
  opts = @opts.dup
102
102
  opts[:backend] = @backend
103
103
  if !@dependencies.nil?