inspec-core 5.12.2 → 5.18.14

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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/etc/deprecations.json +4 -0
  4. data/etc/keys/progress-2022-05-04.pem.pub +9 -0
  5. data/inspec-core.gemspec +1 -1
  6. data/lib/inspec/base_cli.rb +5 -0
  7. data/lib/inspec/cli.rb +64 -9
  8. data/lib/inspec/dependencies/dependency_set.rb +6 -2
  9. data/lib/inspec/dependency_loader.rb +5 -1
  10. data/lib/inspec/dsl.rb +18 -5
  11. data/lib/inspec/errors.rb +2 -0
  12. data/lib/inspec/exceptions.rb +2 -0
  13. data/lib/inspec/fetcher/url.rb +1 -1
  14. data/lib/inspec/file_provider.rb +36 -0
  15. data/lib/inspec/iaf_file.rb +127 -0
  16. data/lib/inspec/profile.rb +31 -14
  17. data/lib/inspec/resources/aide_conf.rb +4 -0
  18. data/lib/inspec/resources/apache.rb +4 -0
  19. data/lib/inspec/resources/apache_conf.rb +4 -0
  20. data/lib/inspec/resources/apt.rb +6 -1
  21. data/lib/inspec/resources/audit_policy.rb +5 -0
  22. data/lib/inspec/resources/auditd_conf.rb +4 -0
  23. data/lib/inspec/resources/bash.rb +4 -0
  24. data/lib/inspec/resources/bond.rb +4 -0
  25. data/lib/inspec/resources/bridge.rb +4 -0
  26. data/lib/inspec/resources/cassandradb_conf.rb +5 -0
  27. data/lib/inspec/resources/cassandradb_session.rb +8 -3
  28. data/lib/inspec/resources/chocolatey_package.rb +4 -0
  29. data/lib/inspec/resources/chrony_conf.rb +4 -0
  30. data/lib/inspec/resources/command.rb +5 -0
  31. data/lib/inspec/resources/cpan.rb +4 -0
  32. data/lib/inspec/resources/cran.rb +4 -0
  33. data/lib/inspec/resources/cron.rb +5 -0
  34. data/lib/inspec/resources/csv.rb +6 -1
  35. data/lib/inspec/resources/default_gateway.rb +61 -0
  36. data/lib/inspec/resources/dh_params.rb +4 -0
  37. data/lib/inspec/resources/docker_container.rb +4 -0
  38. data/lib/inspec/resources/docker_image.rb +4 -0
  39. data/lib/inspec/resources/docker_plugin.rb +4 -0
  40. data/lib/inspec/resources/docker_service.rb +4 -0
  41. data/lib/inspec/resources/etc_group.rb +4 -0
  42. data/lib/inspec/resources/etc_hosts_allow_deny.rb +5 -0
  43. data/lib/inspec/resources/file.rb +97 -1
  44. data/lib/inspec/resources/filesystem.rb +4 -0
  45. data/lib/inspec/resources/gem.rb +4 -0
  46. data/lib/inspec/resources/groups.rb +9 -0
  47. data/lib/inspec/resources/grub_conf.rb +4 -0
  48. data/lib/inspec/resources/host.rb +46 -3
  49. data/lib/inspec/resources/http.rb +4 -0
  50. data/lib/inspec/resources/ibmdb2_conf.rb +8 -0
  51. data/lib/inspec/resources/ibmdb2_session.rb +12 -3
  52. data/lib/inspec/resources/iis_app.rb +4 -0
  53. data/lib/inspec/resources/iis_app_pool.rb +4 -0
  54. data/lib/inspec/resources/iis_site.rb +4 -0
  55. data/lib/inspec/resources/inetd_conf.rb +4 -0
  56. data/lib/inspec/resources/interface.rb +4 -0
  57. data/lib/inspec/resources/ip6tables.rb +4 -0
  58. data/lib/inspec/resources/ipfilter.rb +4 -0
  59. data/lib/inspec/resources/ipnat.rb +4 -0
  60. data/lib/inspec/resources/iptables.rb +4 -0
  61. data/lib/inspec/resources/json.rb +4 -0
  62. data/lib/inspec/resources/kernel_module.rb +4 -0
  63. data/lib/inspec/resources/kernel_parameter.rb +4 -0
  64. data/lib/inspec/resources/key_rsa.rb +4 -0
  65. data/lib/inspec/resources/ksh.rb +4 -0
  66. data/lib/inspec/resources/limits_conf.rb +4 -0
  67. data/lib/inspec/resources/linux_audit_system.rb +81 -0
  68. data/lib/inspec/resources/login_defs.rb +4 -0
  69. data/lib/inspec/resources/mongodb.rb +4 -0
  70. data/lib/inspec/resources/mongodb_conf.rb +5 -0
  71. data/lib/inspec/resources/mongodb_session.rb +6 -1
  72. data/lib/inspec/resources/mount.rb +4 -0
  73. data/lib/inspec/resources/mssql_session.rb +4 -0
  74. data/lib/inspec/resources/mssql_sys_conf.rb +7 -0
  75. data/lib/inspec/resources/mysql_conf.rb +4 -0
  76. data/lib/inspec/resources/mysql_session.rb +8 -1
  77. data/lib/inspec/resources/nginx.rb +6 -1
  78. data/lib/inspec/resources/nginx_conf.rb +4 -0
  79. data/lib/inspec/resources/noop.rb +4 -0
  80. data/lib/inspec/resources/npm.rb +4 -0
  81. data/lib/inspec/resources/ntp_conf.rb +4 -0
  82. data/lib/inspec/resources/oneget.rb +4 -0
  83. data/lib/inspec/resources/opa_api.rb +10 -0
  84. data/lib/inspec/resources/opa_cli.rb +14 -0
  85. data/lib/inspec/resources/oracledb_conf.rb +5 -0
  86. data/lib/inspec/resources/oracledb_listener_conf.rb +4 -0
  87. data/lib/inspec/resources/oracledb_session.rb +10 -0
  88. data/lib/inspec/resources/os.rb +4 -0
  89. data/lib/inspec/resources/os_env.rb +4 -0
  90. data/lib/inspec/resources/package.rb +4 -0
  91. data/lib/inspec/resources/parse_config.rb +10 -1
  92. data/lib/inspec/resources/php_config.rb +72 -0
  93. data/lib/inspec/resources/pip.rb +4 -0
  94. data/lib/inspec/resources/platform.rb +4 -0
  95. data/lib/inspec/resources/postfix_conf.rb +4 -0
  96. data/lib/inspec/resources/postgres_conf.rb +4 -0
  97. data/lib/inspec/resources/postgres_session.rb +8 -4
  98. data/lib/inspec/resources/powershell.rb +4 -0
  99. data/lib/inspec/resources/processes.rb +17 -4
  100. data/lib/inspec/resources/rabbitmq_config.rb +4 -0
  101. data/lib/inspec/resources/registry_key.rb +4 -0
  102. data/lib/inspec/resources/security_identifier.rb +4 -0
  103. data/lib/inspec/resources/security_policy.rb +4 -0
  104. data/lib/inspec/resources/service.rb +80 -1
  105. data/lib/inspec/resources/ssh_config.rb +4 -0
  106. data/lib/inspec/resources/sybase_conf.rb +4 -0
  107. data/lib/inspec/resources/sybase_session.rb +4 -0
  108. data/lib/inspec/resources/sys_info.rb +4 -0
  109. data/lib/inspec/resources/timezone.rb +4 -0
  110. data/lib/inspec/resources/users.rb +4 -0
  111. data/lib/inspec/resources/vbscript.rb +5 -0
  112. data/lib/inspec/resources/virtualization.rb +4 -0
  113. data/lib/inspec/resources/windows_feature.rb +5 -1
  114. data/lib/inspec/resources/windows_firewall.rb +4 -0
  115. data/lib/inspec/resources/windows_firewall_rule.rb +4 -0
  116. data/lib/inspec/resources/windows_hotfix.rb +4 -0
  117. data/lib/inspec/resources/windows_task.rb +4 -0
  118. data/lib/inspec/resources/wmi.rb +4 -0
  119. data/lib/inspec/resources/x509_certificate.rb +59 -0
  120. data/lib/inspec/resources/x509_private_key.rb +93 -0
  121. data/lib/inspec/resources/yum.rb +4 -0
  122. data/lib/inspec/resources/zfs.rb +48 -0
  123. data/lib/inspec/resources/zfs_dataset.rb +4 -0
  124. data/lib/inspec/resources/zfs_pool.rb +4 -0
  125. data/lib/inspec/rule.rb +1 -1
  126. data/lib/inspec/secrets/yaml.rb +7 -1
  127. data/lib/inspec/ui.rb +1 -0
  128. data/lib/inspec/utils/yaml_profile_summary.rb +34 -0
  129. data/lib/inspec/version.rb +1 -1
  130. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +4 -4
  131. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +1 -1
  132. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -1
  133. data/lib/plugins/{inspec-artifact/inspec-artifact.gemspec → inspec-sign/inspec-sign.gemspec} +2 -2
  134. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +161 -0
  135. data/lib/plugins/{inspec-artifact/lib/inspec-artifact → inspec-sign/lib/inspec-sign}/cli.rb +14 -23
  136. data/lib/plugins/inspec-sign/lib/inspec-sign.rb +12 -0
  137. data/lib/source_readers/inspec.rb +8 -2
  138. metadata +16 -8
  139. data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +0 -187
  140. data/lib/plugins/inspec-artifact/lib/inspec-artifact.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7242aa89bb64084dbeb75587b128af25c4967be0bc06ec8e9d8f386e821d36a
4
- data.tar.gz: 876166ebb7e7ae59bb39fe1b9fd4523f45cdb6c22702f08e0074dad4662bf5bf
3
+ metadata.gz: ba97ee3e25e02fba9b85f797fa1a773d2e8fa7628112be422d187c0e46cfce40
4
+ data.tar.gz: a82f51e5c6ba3e1db8c4580f92a33d91bcc2e0c2a8339850e1f168c1569b8818
5
5
  SHA512:
6
- metadata.gz: 155d7f957519b8358c1e8d92373f50b8839cb7f31ca3f32af953a556a2753f768b69b9a78dc7fa2b228ed30e2a26a5b33a393dbe472c501fcc1bda0b54a353f8
7
- data.tar.gz: af8ec2a17788ba7add5c58c07c0e842b31e8a7a92f6afd38ca0e794c2d297dde6464e8dd9cf8d8a07374042a2437ec41685226be098004a4396160432d68a14f
6
+ metadata.gz: beb78893376f0b92b9cf33741f8fab471f66dd328fa1f28c87b05737da7f73266327eded07ca67f0b91a747625dfcb61011b42c06a25914d01f727b07a962456
7
+ data.tar.gz: 193aeef4576e2af4466a176b80e107cd45fe0c0c0f6db290d17696cec1eb7dbf6b58b555bf8c0df4b9bb4a9d5aa8d26f542993b5be1d600a296c3ec50e531d09
data/Gemfile CHANGED
@@ -29,7 +29,7 @@ group :test do
29
29
  gem "json_schemer", ">= 0.2.1", "< 0.2.19"
30
30
  gem "m"
31
31
  gem "minitest-sprint", "~> 1.0"
32
- gem "minitest", "~> 5.5"
32
+ gem "minitest", "5.15.0"
33
33
  gem "mocha", "~> 1.1"
34
34
  gem "nokogiri", "~> 1.9"
35
35
  gem "pry-byebug"
@@ -121,6 +121,10 @@
121
121
  "cli_option_target_id":{
122
122
  "action": "warn",
123
123
  "prefix": "The --target-id option is deprecated in InSpec 5. Its value will be ignored."
124
+ },
125
+ "renamed_to_inspec_export":{
126
+ "action": "ignore",
127
+ "prefix": "The `inspec json` command is deprecated in InSpec 5 and replaced with `inspec export` command."
124
128
  }
125
129
  }
126
130
  }
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8FWKhVwT5ilFdk5/schY
3
+ J3X6DkDbZPul7MAKYuicmyvrk4VZFqFJYzUo9G+ZvtWCjCMZF3JLDbg0VJ+V3j2b
4
+ A5MRUDMH1MtbQZS8u0AIUAitHsSiMgu4w/EHUSHODoDmbdaHT1xkFe9IxMSM1/AV
5
+ /s5u2uAMuiayo+dGW5i9xf/LMZN1JCeX8Yqw85CpS01gMC2XxoUu5wGLwBwXkdql
6
+ +H3SaWRdDTtccgtu0Rxt9dzRtHAPNWKSLl9TScW6Qt/+7bgb3M6+7od/FuPziAS9
7
+ 1NGUiKL9vajpafxUJF8863q0l64dAXGWXVFed7/7GFiNVuLxBksPzK8VrkyCS214
8
+ kQIDAQAB
9
+ -----END PUBLIC KEY-----
data/inspec-core.gemspec CHANGED
@@ -45,5 +45,5 @@ Gem::Specification.new do |spec|
45
45
  spec.add_dependency "semverse", "~> 3.0"
46
46
  spec.add_dependency "multipart-post", "~> 2.0"
47
47
 
48
- spec.add_dependency "train-core", "~> 3.0"
48
+ spec.add_dependency "train-core", "~> 3.10"
49
49
  end
@@ -138,6 +138,8 @@ module Inspec
138
138
  desc: "Provides path to Docker API endpoint (Docker)"
139
139
  option :ssh_config_file, type: :array,
140
140
  desc: "A list of paths to the ssh config file, e.g ~/.ssh/config or /etc/ssh/ssh_config"
141
+ option :podman_url, type: :string,
142
+ desc: "Provides path to Podman API endpoint"
141
143
  end
142
144
 
143
145
  def self.profile_options
@@ -323,6 +325,9 @@ module Inspec
323
325
 
324
326
  def pretty_handle_exception(exception)
325
327
  case exception
328
+ when Inspec::InvalidProfileSignature
329
+ $stderr.puts exception.message
330
+ Inspec::UI.new.exit(:bad_signature)
326
331
  when Inspec::Error
327
332
  $stderr.puts exception.message
328
333
  exit(1)
data/lib/inspec/cli.rb CHANGED
@@ -5,6 +5,7 @@ require "inspec/dist"
5
5
  require "inspec/backend"
6
6
  require "inspec/dependencies/cache"
7
7
  require "inspec/utils/json_profile_summary"
8
+ require "inspec/utils/yaml_profile_summary"
8
9
 
9
10
  module Inspec # TODO: move this somewhere "better"?
10
11
  autoload :BaseCLI, "inspec/base_cli"
@@ -69,25 +70,77 @@ class Inspec::InspecCLI < Inspec::BaseCLI
69
70
  desc: "A list of tags to filter controls and include only those. Ignore all other tests."
70
71
  profile_options
71
72
  def json(target)
72
- require "json" unless defined?(JSON)
73
+ # This deprecation warning is ignored currently.
74
+ Inspec.deprecate(:renamed_to_inspec_export)
75
+ export(target, true)
76
+ end
73
77
 
78
+ desc "export PATH", "read the profile in PATH and generate a summary in the given format."
79
+ option :what, type: :string,
80
+ desc: "What to export: profile (default), readme, metadata."
81
+ option :format, type: :string,
82
+ desc: "The output format to use: json, raw, yaml. If valid format is not provided then it will use the default for the given 'what'."
83
+ option :output, aliases: :o, type: :string,
84
+ desc: "Save the created output to a path"
85
+ option :controls, type: :array,
86
+ desc: "For --what=profile, a list of controls to include. Ignore all other tests."
87
+ option :tags, type: :array,
88
+ desc: "For --what=profile, a list of tags to filter controls and include only those. Ignore all other tests."
89
+ profile_options
90
+ def export(target, as_json = false)
74
91
  o = config
75
92
  diagnose(o)
76
93
  o["log_location"] = $stderr
77
94
  configure_logger(o)
78
95
 
96
+ # using dup to resolve "can't modify frozen String" error.
97
+ what = o[:what].dup || "profile"
98
+ what.downcase!
99
+ raise Inspec::Error.new("Unrecognized option '#{what}' for --what - expected one of profile, readme, or metadata.") unless %w{profile readme metadata}.include?(what)
100
+
101
+ default_format_for_what = {
102
+ "profile" => "yaml",
103
+ "metadata" => "raw",
104
+ "readme" => "raw",
105
+ }
106
+ valid_formats_for_what = {
107
+ "profile" => %w{yaml json},
108
+ "metadata" => %w{yaml raw}, # not going to argue
109
+ "readme" => ["raw"],
110
+ }
111
+ format = o[:format] || default_format_for_what[what]
112
+ # default is json if we were called as old json command
113
+ format = "json" if as_json
114
+ raise Inspec::Error.new("Invalid option '#{format}' for --format and --what combination") unless format && valid_formats_for_what[what].include?(format)
115
+
79
116
  o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
80
117
  o[:check_mode] = true
81
118
  o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
82
-
83
119
  profile = Inspec::Profile.for_target(target, o)
84
120
  dst = o[:output].to_s
85
121
 
86
- # Write JSON
87
- Inspec::Utils::JsonProfileSummary.produce_json(
88
- info: profile.info,
89
- write_path: dst
90
- )
122
+ case what
123
+ when "profile"
124
+ if format == "json"
125
+ require "json" unless defined?(JSON)
126
+ # Write JSON
127
+ Inspec::Utils::JsonProfileSummary.produce_json(
128
+ info: profile.info,
129
+ write_path: dst
130
+ )
131
+ elsif format == "yaml"
132
+ Inspec::Utils::YamlProfileSummary.produce_yaml(
133
+ info: profile.info,
134
+ write_path: dst
135
+ )
136
+ end
137
+ when "readme"
138
+ out = dst.empty? ? $stdout : File.open(dst, "w")
139
+ out.write(profile.readme)
140
+ when "metadata"
141
+ out = dst.empty? ? $stdout : File.open(dst, "w")
142
+ out.write(profile.metadata_src)
143
+ end
91
144
  rescue StandardError => e
92
145
  pretty_handle_exception(e)
93
146
  end
@@ -95,6 +148,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
95
148
  desc "check PATH", "verify all tests at the specified PATH"
96
149
  option :format, type: :string,
97
150
  desc: "The output format to use doc (default), json. If valid format is not provided then it will use the default."
151
+ option :with_cookstyle, type: :boolean,
152
+ desc: "Enable or disable cookstyle checks.", default: false
98
153
  profile_options
99
154
  def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
100
155
  o = config
@@ -187,12 +242,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
187
242
  desc: "Fallback to using local archives if fetching fails."
188
243
  option :ignore_errors, type: :boolean, default: false,
189
244
  desc: "Ignore profile warnings."
190
- def archive(path)
245
+ def archive(path, log_level = nil)
191
246
  o = config
192
247
  diagnose(o)
193
248
 
194
249
  o[:logger] = Logger.new($stdout)
195
- o[:logger].level = get_log_level(o[:log_level])
250
+ o[:logger].level = get_log_level(log_level || o[:log_level])
196
251
  o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
197
252
 
198
253
  # Force vendoring with overwrite when archiving
@@ -25,7 +25,9 @@ module Inspec
25
25
  def self.from_array(dependencies, cwd, cache, backend)
26
26
  dep_list = {}
27
27
  dependencies.each do |d|
28
- dep_list[d.name] = d
28
+ # if depedent profile does not have a source version then only name is used in dependency hash
29
+ key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}") rescue "#{d.name}"
30
+ dep_list[key_name] = d
29
31
  end
30
32
  new(cwd, cache, dep_list, backend)
31
33
  end
@@ -39,7 +41,9 @@ module Inspec
39
41
  def self.flatten_dep_tree(dep_tree)
40
42
  dep_list = {}
41
43
  dep_tree.each do |d|
42
- dep_list[d.name] = d
44
+ # if depedent profile does not have a source version then only name is used in dependency hash
45
+ key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}") rescue d.name
46
+ dep_list[key_name] = d
43
47
  dep_list.merge!(flatten_dep_tree(d.dependencies))
44
48
  end
45
49
  dep_list
@@ -50,7 +50,11 @@ module Inspec
50
50
  end
51
51
 
52
52
  def gem_version_installed?(name, version)
53
- list_installed_gems.any? { |s| s.name == name && Gem::Requirement.new(version.split(",")) =~ s.version }
53
+ if version.nil?
54
+ list_installed_gems.any? { |s| s.name == name }
55
+ else
56
+ list_installed_gems.any? { |s| s.name == name && Gem::Requirement.new(version.split(",")) =~ s.version }
57
+ end
54
58
  end
55
59
 
56
60
  private
data/lib/inspec/dsl.rb CHANGED
@@ -6,13 +6,13 @@ require "inspec/utils/deprecated_cloud_resources_list"
6
6
  module Inspec::DSL
7
7
  attr_accessor :backend
8
8
 
9
- def require_controls(id, &block)
10
- opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies }
9
+ def require_controls(id, version = nil, &block)
10
+ opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies, profile_version: version }
11
11
  ::Inspec::DSL.load_spec_files_for_profile(self, opts, &block)
12
12
  end
13
13
 
14
- def include_controls(id, &block)
15
- opts = { profile_id: id, include_all: true, backend: @backend, conf: @conf, dependencies: @dependencies }
14
+ def include_controls(id, version = nil, &block)
15
+ opts = { profile_id: id, include_all: true, backend: @backend, conf: @conf, dependencies: @dependencies, profile_version: version }
16
16
  ::Inspec::DSL.load_spec_files_for_profile(self, opts, &block)
17
17
  end
18
18
 
@@ -85,7 +85,20 @@ module Inspec::DSL
85
85
  def self.load_spec_files_for_profile(bind_context, opts, &block)
86
86
  dependencies = opts[:dependencies]
87
87
  profile_id = opts[:profile_id]
88
- dep_entry = dependencies.list[profile_id]
88
+ profile_version = opts[:profile_version]
89
+
90
+ new_profile_id = nil
91
+ if profile_version
92
+ new_profile_id = "#{profile_id}-#{profile_version}"
93
+ else
94
+ dependencies.list.keys.each do |key|
95
+ # If dep profile does not contain a source version, key does not contain a version as well. In that case new_profile_id will be always nil and instead profile_id would be used to fetch profile from dependency list.
96
+ profile_id_key = key.split("-")
97
+ profile_id_key.pop
98
+ new_profile_id = key if profile_id_key.join("-") == profile_id
99
+ end
100
+ end
101
+ dep_entry = new_profile_id ? dependencies.list[new_profile_id] : dependencies.list[profile_id]
89
102
 
90
103
  if dep_entry.nil?
91
104
  raise <<~EOF
data/lib/inspec/errors.rb CHANGED
@@ -22,4 +22,6 @@ module Inspec
22
22
  attr_accessor :gem_name
23
23
  attr_accessor :version
24
24
  end
25
+
26
+ class InvalidProfileSignature < Error; end
25
27
  end
@@ -8,5 +8,7 @@ module Inspec
8
8
  class ResourceFailed < StandardError; end
9
9
  class ResourceSkipped < StandardError; end
10
10
  class SecretsBackendNotFound < ArgumentError; end
11
+ class ProfileValidationKeyNotFound < ArgumentError; end
12
+ class ProfileSigningKeyNotFound < ArgumentError; end
11
13
  end
12
14
  end
@@ -262,7 +262,7 @@ module Inspec::Fetcher
262
262
 
263
263
  open(target, opts)
264
264
 
265
- rescue SocketError, Errno::ECONNREFUSED, OpenURI::HTTPError => e
265
+ rescue SocketError, Net::OpenTimeout, Errno::ECONNREFUSED, OpenURI::HTTPError => e
266
266
  raise Inspec::FetcherFailure, "Profile URL dependency #{target} could not be fetched: #{e.message}"
267
267
  end
268
268
 
@@ -2,6 +2,7 @@ require "rubygems/package" unless defined?(Gem::Package)
2
2
  require "pathname" unless defined?(Pathname)
3
3
  require "zlib" unless defined?(Zlib)
4
4
  require "zip" unless defined?(Zip)
5
+ require "inspec/iaf_file"
5
6
 
6
7
  module Inspec
7
8
  class FileProvider
@@ -14,6 +15,13 @@ module Inspec
14
15
  TarProvider.new(path)
15
16
  elsif File.exist?(path) && path.end_with?(".zip")
16
17
  ZipProvider.new(path)
18
+ elsif File.exist?(path) && path.end_with?(".iaf")
19
+ iaf_file = IafFile.new(path)
20
+ if iaf_file.valid?
21
+ IafProvider.new(path)
22
+ else
23
+ raise Inspec::InvalidProfileSignature, "Profile signature is invalid."
24
+ end
17
25
  elsif File.exist?(path)
18
26
  DirProvider.new(path)
19
27
  else
@@ -216,6 +224,34 @@ module Inspec
216
224
  end
217
225
  end # class TarProvider
218
226
 
227
+ class IafProvider < TarProvider
228
+ attr_reader :files
229
+
230
+ def initialize(path)
231
+ f = File.open(path, "rb")
232
+ version = f.readline.strip!
233
+ if version == "INSPEC-PROFILE-1"
234
+ while f.readline != "\n" do end
235
+ content = f.read
236
+ f.close
237
+ elsif version == "INSPEC-PROFILE-2"
238
+ f.readline.strip!
239
+ content = f.read
240
+ f.close
241
+ content = content.slice(490, content.length).lstrip
242
+ else
243
+ raise Inspec::InvalidProfileSignature, "Unrecognized IAF version."
244
+ end
245
+
246
+ tmpfile = nil
247
+ Dir.mktmpdir do |workdir|
248
+ tmpfile = Pathname.new(workdir).join("temp_profile.tar.gz")
249
+ File.open(tmpfile, "wb") { |fl| fl.write(content) }
250
+ super(tmpfile)
251
+ end
252
+ end
253
+ end # class IafProvider
254
+
219
255
  class RelativeFileProvider
220
256
  BLACKLIST_FILES = [
221
257
  "/pax_global_header",
@@ -0,0 +1,127 @@
1
+ require "base64" unless defined?(Base64)
2
+ require "openssl" unless defined?(OpenSSL)
3
+ require "set" unless defined?(Set)
4
+ require "uri" unless defined?(URI)
5
+
6
+ module Inspec
7
+ class IafFile
8
+ KEY_ALG = OpenSSL::PKey::RSA
9
+ INSPEC_PROFILE_VERSION_1 = "INSPEC-PROFILE-1".freeze
10
+ INSPEC_PROFILE_VERSION_2 = "INSPEC-PROFILE-2".freeze
11
+
12
+ ARTIFACT_DIGEST = OpenSSL::Digest::SHA512
13
+ ARTIFACT_DIGEST_NAME = "SHA512".freeze
14
+
15
+ VALID_PROFILE_VERSIONS = Set.new [INSPEC_PROFILE_VERSION_1, INSPEC_PROFILE_VERSION_2]
16
+ VALID_PROFILE_DIGESTS = Set.new [ARTIFACT_DIGEST_NAME]
17
+
18
+ def self.find_validation_key(keyname)
19
+ [
20
+ ".",
21
+ File.join(Inspec.config_dir, "keys"),
22
+ File.join(Inspec.src_root, "etc", "keys"),
23
+ ].each do |path|
24
+ filename = File.join(path, "#{keyname}.pem.pub")
25
+ return filename if File.exist?(filename)
26
+ rescue ArgumentError => e
27
+ puts e
28
+ raise Inspec::Exceptions::ProfileValidationKeyNotFound.new("Validation key #{keyname} not found")
29
+ end
30
+
31
+ # Retry if we can fetch it from github
32
+ return find_validation_key(keyname) if fetch_validation_key_from_github(keyname)
33
+
34
+ raise Inspec::Exceptions::ProfileValidationKeyNotFound.new("Validation key #{keyname} not found")
35
+ end
36
+
37
+ def self.find_signing_key(keyname)
38
+ [".", File.join(Inspec.config_dir, "keys")].each do |path|
39
+ filename = File.join(path, "#{keyname}.pem.key")
40
+ return filename if File.exist?(filename)
41
+ end
42
+ raise Inspec::Exceptions::ProfileSigningKeyNotFound.new("Signing key #{keyname} not found")
43
+ end
44
+
45
+ def self.fetch_validation_key_from_github(keyname)
46
+ URI.open("https://raw.githubusercontent.com/inspec/inspec/main/etc/keys/#{keyname}.pem.pub") do |r|
47
+ puts "Fetching validation key '#{keyname}' from github"
48
+ dir = File.join(Inspec.config_dir, "keys")
49
+ FileUtils.mkdir_p dir
50
+ key_file = File.join(dir, "#{keyname}.pem.pub")
51
+ File.open(key_file, "w") do |f|
52
+ r.each_line do |line|
53
+ f.puts line
54
+ end
55
+ end
56
+ end
57
+ true
58
+ rescue OpenURI::HTTPError
59
+ false
60
+ end
61
+
62
+ attr_reader :key_name, :version
63
+
64
+ def initialize(path)
65
+ @path = path
66
+ @key_name = nil
67
+ end
68
+
69
+ def valid?
70
+ header = []
71
+ valid = true
72
+ f = File.open(@path, "rb")
73
+ @version = f.readline.strip!
74
+ if version == INSPEC_PROFILE_VERSION_1
75
+ header << version
76
+ header << f.readline.strip!
77
+ header << f.readline.strip!
78
+
79
+ file_sig = ""
80
+ # the signature is multi-line
81
+ while (line = f.readline) != "\n"
82
+ file_sig += line
83
+ end
84
+ header << file_sig.strip!
85
+ f.close
86
+
87
+ f = File.open(@path, "rb")
88
+ while f.readline != "\n" do end
89
+ content = f.read
90
+ f.close
91
+ elsif version == INSPEC_PROFILE_VERSION_2
92
+ header << version
93
+ header << f.readline.strip!
94
+ content = f.read
95
+ f.close
96
+
97
+ header_content = content.unpack("h*").pack("H*")
98
+ header << header_content.slice(0, 100).rstrip
99
+ header << header_content.slice(100, 20).rstrip
100
+ header << header_content.slice(120, 370).rstrip + "\n" # \n at the end is require in this field
101
+ content = content.slice(490, content.length).lstrip
102
+ else
103
+ valid = false
104
+ end
105
+
106
+ @key_name = header[2]
107
+ validation_key_path = Inspec::IafFile.find_validation_key(@key_name)
108
+
109
+ unless valid_header?(header)
110
+ valid = false
111
+ end
112
+
113
+ verification_key = KEY_ALG.new File.read validation_key_path
114
+ signature = Base64.decode64(header[4])
115
+ digest = ARTIFACT_DIGEST.new
116
+ unless verification_key.verify digest, signature, content
117
+ valid = false
118
+ end
119
+
120
+ valid
121
+ end
122
+
123
+ def valid_header?(header)
124
+ VALID_PROFILE_VERSIONS.member?(header[0]) && VALID_PROFILE_DIGESTS.member?(header[3])
125
+ end
126
+ end
127
+ end
@@ -105,6 +105,7 @@ module Inspec
105
105
  @check_mode = options[:check_mode] || false
106
106
  @parent_profile = options[:parent_profile]
107
107
  @legacy_profile_path = options[:profiles_path] || false
108
+ @check_cookstyle = options[:with_cookstyle]
108
109
  Metadata.finalize(@source_reader.metadata, @profile_id, options)
109
110
 
110
111
  # if a backend has already been created, clone it so each profile has its own unique backend object
@@ -349,22 +350,24 @@ module Inspec
349
350
  def load_libraries
350
351
  return @runner_context if @libraries_loaded
351
352
 
352
- locked_dependencies.dep_list.each_with_index do |(_name, dep), i|
353
+ locked_dependencies.dep_list.each_with_index do |(_name, dep), index|
353
354
  d = dep.profile
354
355
  # this will force a dependent profile load so we are only going to add
355
356
  # this metadata if the parent profile is supported.
356
357
  if @supports_platform && !d.supports_platform?
357
358
  # since ruby 1.9 hashes are ordered so we can just use index values here
358
359
  # TODO: NO! this is a violation of encapsulation to an extreme
359
- metadata.dependencies[i][:status] = "skipped"
360
- msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'."
361
- metadata.dependencies[i][:status_message] = msg
362
- metadata.dependencies[i][:skip_message] = msg # Repeat as skip_message for backward compatibility
360
+ if metadata.dependencies[index]
361
+ metadata.dependencies[index][:status] = "skipped"
362
+ msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'."
363
+ metadata.dependencies[index][:status_message] = msg
364
+ metadata.dependencies[index][:skip_message] = msg # Repeat as skip_message for backward compatibility
365
+ end
363
366
  next
364
- elsif metadata.dependencies[i]
367
+ elsif metadata.dependencies[index]
365
368
  # Currently wrapper profiles will load all dependencies, and then we
366
369
  # load them again when we dive down. This needs to be re-done.
367
- metadata.dependencies[i][:status] = "loaded"
370
+ metadata.dependencies[index][:status] = "loaded"
368
371
  end
369
372
 
370
373
  # rubocop:disable Layout/ExtraSpacing
@@ -409,7 +412,7 @@ module Inspec
409
412
  else
410
413
  ui = Inspec::UI.new
411
414
  gem_dependencies.each { |gem_dependency| ui.list_item("#{gem_dependency[:name]} #{gem_dependency[:version]}") }
412
- choice = ui.prompt.select("Would you like to install profile gem dependencies listed above?", %w{Yes No})
415
+ choice = ui.prompt.select("The above listed gem dependencies are required to run the profile. Would you like to install them ?", %w{Yes No})
413
416
  if choice == "Yes"
414
417
  Inspec::Config.cached[:auto_install_gems] = true
415
418
  load_gem_dependencies
@@ -655,12 +658,13 @@ module Inspec
655
658
  end
656
659
 
657
660
  # Running cookstyle to check for code offenses
658
- cookstyle_linting_check.each do |lint_output|
659
- data = lint_output.split(":")
660
- msg = "#{data[-2]}:#{data[-1]}"
661
- offense.call(data[0], data[1], data[2], nil, msg)
661
+ if @check_cookstyle
662
+ cookstyle_linting_check.each do |lint_output|
663
+ data = lint_output.split(":")
664
+ msg = "#{data[-2]}:#{data[-1]}"
665
+ offense.call(data[0], data[1], data[2], nil, msg)
666
+ end
662
667
  end
663
-
664
668
  # profile is valid if we could not find any error & offenses
665
669
  result[:summary][:valid] = result[:errors].empty? && result[:offenses].empty?
666
670
 
@@ -744,6 +748,14 @@ module Inspec
744
748
  @source_reader.target.files
745
749
  end
746
750
 
751
+ def readme
752
+ @source_reader.readme&.values&.first
753
+ end
754
+
755
+ def metadata_src
756
+ @source_reader.metadata_src
757
+ end
758
+
747
759
  #
748
760
  # TODO(ssd): Relative path handling really needs to be carefully
749
761
  # thought through, especially with respect to relative paths in
@@ -848,7 +860,12 @@ module Inspec
848
860
  f = load_rule_filepath(prefix, rule)
849
861
  load_rule(rule, f, controls, groups)
850
862
  end
851
- params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
863
+ if @profile_id.nil?
864
+ # identifying inputs using profile name
865
+ params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(params[:name])
866
+ else
867
+ params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
868
+ end
852
869
  params
853
870
  end
854
871
 
@@ -48,6 +48,10 @@ module Inspec::Resources
48
48
 
49
49
  filter.install_filter_methods_on_resource(self, :params)
50
50
 
51
+ def resource_id
52
+ @conf_path || "aide_conf"
53
+ end
54
+
51
55
  def to_s
52
56
  "AIDE Config"
53
57
  end
@@ -40,6 +40,10 @@ module Inspec::Resources
40
40
  end
41
41
  end
42
42
 
43
+ def resource_id
44
+ @conf_path || "apache"
45
+ end
46
+
43
47
  def to_s
44
48
  "Apache Environment"
45
49
  end
@@ -124,6 +124,10 @@ module Inspec::Resources
124
124
  @params["ServerRoot"] || @params["serverroot"]
125
125
  end
126
126
 
127
+ def resource_id
128
+ @conf_path || "apache_conf"
129
+ end
130
+
127
131
  def to_s
128
132
  "Apache Config #{conf_path}"
129
133
  end
@@ -39,10 +39,11 @@ module Inspec::Resources
39
39
  EXAMPLE
40
40
 
41
41
  def initialize(ppa_name)
42
+ @ppa_name = ppa_name
42
43
  @deb_url = nil
43
44
  # check if the os is ubuntu or debian
44
45
  if inspec.os.debian?
45
- @deb_url = determine_ppa_url(ppa_name)
46
+ @deb_url = determine_ppa_url(@ppa_name)
46
47
  else
47
48
  # this resource is only supported on ubuntu and debian
48
49
  skip_resource "The `apt` resource is not supported on your OS yet."
@@ -61,6 +62,10 @@ module Inspec::Resources
61
62
  actives.size == 1 && actives[0] = true
62
63
  end
63
64
 
65
+ def resource_id
66
+ @deb_url || @ppa_name || "apt repository"
67
+ end
68
+
64
69
  def to_s
65
70
  "Apt Repository #{@deb_url}"
66
71
  end
@@ -57,6 +57,11 @@ module Inspec::Resources
57
57
  values
58
58
  end
59
59
 
60
+ # Since this resource does not have any unique identifier for resource, sending the Auditpol command as UUID.
61
+ def resource_id
62
+ "audit_policy"
63
+ end
64
+
60
65
  def to_s
61
66
  "Audit Policy"
62
67
  end
@@ -27,6 +27,10 @@ module Inspec::Resources
27
27
  read_params[name.to_s]
28
28
  end
29
29
 
30
+ def resource_id
31
+ @conf_path || "auditd_conf"
32
+ end
33
+
30
34
  def to_s
31
35
  "Audit Daemon Config"
32
36
  end
@@ -28,6 +28,10 @@ module Inspec::Resources
28
28
  super(CommandWrapper.wrap(command, options))
29
29
  end
30
30
 
31
+ def resource_id
32
+ @raw_command || "bash"
33
+ end
34
+
31
35
  def to_s
32
36
  "Bash command #{@raw_command}"
33
37
  end