inspec-core 5.22.65 → 5.22.80

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a7f8456410caebef0bb3dfdad7df4d9aac0e72d33effef12d1581c919be2e54
4
- data.tar.gz: ca21ae25ee3c9d43e1820d45663b6232e0be0dd3c336305acf15628c1cc68c37
3
+ metadata.gz: 6b46f57de93a21fa00ddeb70944cc1bb3cf846c73d1b33c5460496472773b0fa
4
+ data.tar.gz: a1f020b14dc5f06c81f98d7fabaa593b5a0696b5cd4370625382b627c216493e
5
5
  SHA512:
6
- metadata.gz: 0f822677d07b5c1d2c8b70f23ad6cf94f303686200cc9b68683fc54af2d0e362ae257278bdcb510febeefff2e1f163aadc42a019dbe39089444665d80c29da28
7
- data.tar.gz: 6800aef92c54e66bc4fcf2fe604326c8caaae4514bbf2a11aa913d3a2f18a9f8d6775427d81fdc1bf1c59f25a588f36b94c59af4ab69f155634b47eaf3944015
6
+ metadata.gz: f72371f72e1195698128502801aefea2e698bd2c705c00b849547c2f8b18881bb05964de87715cbea5301ee6284db60fba6a9f080915d3f672686174c0ec1e59
7
+ data.tar.gz: 56a96407e6c3dd29f7d33c0ee128cf419aac43e01c48c6c220c3041cf20aeee4478d9be1ec280f2732391ca9d7eef5db19371e69d6105036e13fbbca5a6c3e53
data/Gemfile CHANGED
@@ -12,7 +12,7 @@ gem "inspec-bin", path: "./inspec-bin"
12
12
  # ffi version v1.17.0 is breaking verify pipeline as it requires
13
13
  # rubygems version to be upgraded to >= 3.3.22 Ref:https://buildkite.com/chef/inspec-inspec-main-verify-private/builds/812#018fe177-2ccb-45ed-a25e-213c8a6453df/698-707
14
14
 
15
- gem "ffi", ">= 1.15.5", "< 1.18.0"
15
+ gem "ffi", ">= 1.15.5", "< 1.17.0"
16
16
 
17
17
  # We have a build issue 2023-11-13 with unf_ext 0.0.9 so we are pinning to 0.0.8.2
18
18
  # See https://github.com/knu/ruby-unf_ext/issues/74 https://buildkite.com/chef/inspec-inspec-inspec-5-omnibus-release/builds/22
@@ -37,8 +37,10 @@ group :test do
37
37
  gem "minitest-sprint", "~> 1.0"
38
38
  gem "minitest", "5.15.0"
39
39
  gem "mocha"
40
- gem "nokogiri"
41
- gem "pry-byebug"
40
+ # Pinning this version as it breaking for ruby 3.1.0
41
+ gem "nokogiri", "< 1.17.2"
42
+ # Pinning this version as it breaking for ruby 3.0.0
43
+ gem "pry-byebug", "< 3.11.0"
42
44
  gem "pry"
43
45
  gem "rake"
44
46
  gem "simplecov"
@@ -61,6 +63,11 @@ end
61
63
  # Remove this pin when upgrading to Ruby 3.2 or higher.
62
64
  gem "zeitwerk", "~> 2.6.0", "< 2.7"
63
65
 
66
+ # Pinning dry-core,dry-core,dry-types to < 1.1.0 as it is breaking the build because 1.1.0 is incompatible with the current version, ruby 3.0.x on CI
67
+ gem "dry-types", "<= 1.7.2" if RUBY_VERSION < "3.1.0"
68
+ gem "dry-core", "> 1.0.0", "< 1.1.0" if RUBY_VERSION < "3.1.0"
69
+ gem "dry-inflector", "<= 1.1.0" if RUBY_VERSION < "3.1.0"
70
+
64
71
  # Pinning securerandom to < 0.4.0 as it is breaking the build because 0.4.0 is incompatible with the current version, ruby 3.0.x on CI
65
72
  # Remove this pin when upgrading to Ruby 3.1 or higher on CI.
66
73
  gem "securerandom", "< 0.4.0" if RUBY_VERSION < "3.1.0"
data/inspec-core.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency "rspec-its", "~> 1.2"
37
37
  spec.add_dependency "pry", "~> 0.13"
38
38
  spec.add_dependency "hashie", ">= 3.4", "< 6.0"
39
- spec.add_dependency "mixlib-log", "~> 3.0"
39
+ spec.add_dependency "mixlib-log", "~> 3.0", "< 3.2"
40
40
  spec.add_dependency "sslshake", "~> 1.2"
41
41
  spec.add_dependency "parallel", "~> 1.9"
42
42
  spec.add_dependency "faraday", ">= 1", "< 3"
@@ -55,5 +55,5 @@ Gem::Specification.new do |spec|
55
55
  # which was causing a LoadError ('cannot load such file -- ast') for users/applications using 'inspec-core'.
56
56
  spec.add_dependency "cookstyle"
57
57
 
58
- spec.add_dependency "train-core", "~> 3.10"
58
+ spec.add_dependency "train-core", "~> 3.12.13"
59
59
  end
@@ -12,5 +12,6 @@ module Inspec
12
12
  class ProfileSigningKeyNotFound < ArgumentError; end
13
13
  class WaiversFileNotReadable < ArgumentError; end
14
14
  class WaiversFileDoesNotExist < ArgumentError; end
15
+ class WaiversFileInvalidFormatting < ArgumentError; end
15
16
  end
16
17
  end
@@ -575,7 +575,7 @@ module Inspec
575
575
  include_tests: include_tests)
576
576
 
577
577
  # Collect all metadata defined in the control block and inputs defined inside the control block
578
- src.ast.each_node { |n|
578
+ src.ast&.each_node { |n|
579
579
  ctl_id_collector.process(n)
580
580
  input_collector.process(n)
581
581
  }
@@ -637,7 +637,7 @@ module Inspec
637
637
  }
638
638
  source_location_ref = @source_reader.target.abs_path(control_filename)
639
639
  Inspec::Profile::AstHelper::TitleCollector.new(group_data)
640
- .process(src.ast.child_nodes.first) # Picking the title defined for the whole controls file
640
+ .process(src.ast&.child_nodes&.first) # Picking the title defined for the whole controls file
641
641
  group_controls = @info_from_parse[:controls].select { |control| control[:source_location][:ref] == source_location_ref }
642
642
  group_data[:controls] = group_controls.map { |control| control[:id] }
643
643
 
@@ -39,11 +39,17 @@ module Inspec::Resources
39
39
  # expected result:
40
40
  # Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
41
41
  # WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
42
- result ||= inspec.command("Auditpol /get /subcategory:'#{key}' /r").stdout
42
+ auditpol_cmd = "Auditpol /get /subcategory:'#{key}' /r"
43
+ result ||= inspec.command(auditpol_cmd)
44
+
45
+ unless result.exit_status == 0
46
+ error = result.stdout + "\n" + result.stderr
47
+ raise Inspec::Exceptions::ResourceFailed, "Error while executing #{auditpol_cmd} command: #{error}"
48
+ end
43
49
 
44
50
  # find line
45
51
  target = nil
46
- result.each_line do |s|
52
+ result.stdout.each_line do |s|
47
53
  target = s.strip if s =~ /\b.*#{key}.*\b/
48
54
  end
49
55
 
@@ -200,8 +200,60 @@ module Inspec::Resources
200
200
  # implements generic unix groups via /etc/group
201
201
  class UnixGroup < GroupInfo
202
202
  def groups
203
+ get_group_info
204
+ end
205
+
206
+ private
207
+
208
+ def get_group_info
209
+ # First, try to fetch group info using getent
210
+ group_info = fetch_group_info_using_getent
211
+
212
+ return group_info unless group_info.empty?
213
+
214
+ # If getent fails, fallback to reading group info from /etc/group using inspec.etc_group.entries
215
+ Inspec::Log.debug("Falling back to reading group info from /etc/group as getent is unavailable or failed.")
203
216
  inspec.etc_group.entries
204
217
  end
218
+
219
+ # Fetches group information using the getent utility
220
+ def fetch_group_info_using_getent
221
+ # Find getent utility on the system
222
+ bin = find_getent_utility
223
+
224
+ # If getent is available, fetch group info
225
+ return [] unless bin
226
+
227
+ cmd = inspec.command("#{bin} group")
228
+ return parse_group_info(cmd) if cmd.exit_status.to_i == 0
229
+
230
+ # If getent fails, log the error and return an empty array
231
+ Inspec::Log.debug("Failed to execute #{bin} group: #{cmd.stderr}.")
232
+ []
233
+ end
234
+
235
+ # Parses group info from the command output
236
+ def parse_group_info(cmd)
237
+ cmd.stdout.strip.split("\n").map do |line|
238
+ name, password, gid, members = line.split(":")
239
+ {
240
+ "name" => name,
241
+ "password" => password,
242
+ "gid" => gid.to_i,
243
+ "members" => members,
244
+ }
245
+ end
246
+ end
247
+
248
+ # Checks if getent exists on the system
249
+ def find_getent_utility
250
+ %w{/usr/bin/getent /bin/getent getent}.each do |cmd|
251
+ return cmd if inspec.command(cmd).exist?
252
+ end
253
+ # Log debug information if getent is not found
254
+ Inspec::Log.debug("Could not find `getent` on your system.")
255
+ nil # Return nil if getent is not found
256
+ end
205
257
  end
206
258
 
207
259
  # OSX uses opendirectory for groups, so `/etc/group` may not be fully accurate
@@ -1,7 +1,7 @@
1
1
  # copyright: 2015, Vulcano Security GmbH
2
2
 
3
3
  require "shellwords" unless defined?(Shellwords)
4
-
4
+ require "cgi" unless defined?(CGI)
5
5
  module Inspec::Resources
6
6
  class Lines
7
7
  attr_reader :output, :exit_status
@@ -74,6 +74,10 @@ module Inspec::Resources
74
74
  Shellwords.escape(query)
75
75
  end
76
76
 
77
+ def encoded_password(password)
78
+ CGI.escape(password)
79
+ end
80
+
77
81
  def create_psql_cmd(query, db = [])
78
82
  dbs = db.map { |x| "#{x}" }.join(" ")
79
83
 
@@ -82,14 +86,14 @@ module Inspec::Resources
82
86
  # Socket connection only enabled for non-windows platforms
83
87
  # Windows does not support unix domain sockets
84
88
  option_port = @port.nil? ? "" : "-p #{@port}" # add explicit port if specified
85
- "psql -d postgresql://#{@user}:#{@pass}@/#{dbs}?host=#{@socket_path} #{option_port} -A -t -w -c #{escaped_query(query)}"
89
+ "psql -d postgresql://#{@user}:#{encoded_password(@pass)}@/#{dbs}?host=#{@socket_path} #{option_port} -A -t -w -c #{escaped_query(query)}"
86
90
  else
87
91
  # Host in connection string establishes tcp/ip connection
88
92
  if inspec.os.windows?
89
93
  warn "Socket based connection not supported in windows, connecting using host" if @socket_path
90
- "psql -d postgresql://#{@user}:#{@pass}@#{@host}:#{@port}/#{dbs} -A -t -w -c \"#{query}\""
94
+ "psql -d postgresql://#{@user}:#{encoded_password(@pass)}@#{@host}:#{@port}/#{dbs} -A -t -w -c \"#{query}\""
91
95
  else
92
- "psql -d postgresql://#{@user}:#{@pass}@#{@host}:#{@port}/#{dbs} -A -t -w -c #{escaped_query(query)}"
96
+ "psql -d postgresql://#{@user}:#{encoded_password(@pass)}@#{@host}:#{@port}/#{dbs} -A -t -w -c #{escaped_query(query)}"
93
97
  end
94
98
  end
95
99
  end
@@ -24,12 +24,18 @@ module Secrets
24
24
  @inputs = ::YAML.load_file(target)
25
25
  end
26
26
 
27
- if @inputs == false || !@inputs.is_a?(Hash)
28
- Inspec::Log.warn("#{self.class} unable to parse #{target}: invalid YAML or contents is not a Hash")
27
+ # In case of empty yaml file raise the warning else raise the parsing error.
28
+ if !@inputs || @inputs.empty?
29
+ Inspec::Log.warn("Unable to parse #{target}: YAML file is empty.")
29
30
  @inputs = nil
31
+ elsif !@inputs.is_a?(Hash)
32
+ # Exits with usage error.
33
+ Inspec::Log.error("Unable to parse #{target}: invalid YAML or contents is not a Hash")
34
+ Inspec::UI.new.exit(:usage_error)
30
35
  end
31
36
  rescue => e
32
- raise "Error reading InSpec inputs: #{e}"
37
+ # Any other error related to Yaml parsing will be raised here.
38
+ raise "Error reading YAML file #{target}: #{e}"
33
39
  end
34
40
  end
35
41
  end
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = "5.22.65".freeze
2
+ VERSION = "5.22.80".freeze
3
3
  end
@@ -5,6 +5,8 @@ require "inspec/utils/waivers/json_file_reader"
5
5
  module Inspec
6
6
  class WaiverFileReader
7
7
 
8
+ SUPPORTED_FILE_EXTENSION = %w{.yaml .yml .csv .json}.freeze
9
+
8
10
  def self.fetch_waivers_by_profile(profile_id, files)
9
11
  read_waivers_from_file(profile_id, files) if @waivers_data.nil? || @waivers_data[profile_id].nil?
10
12
  @waivers_data[profile_id]
@@ -15,49 +17,105 @@ module Inspec
15
17
  output = {}
16
18
 
17
19
  files.each do |file_path|
18
- file_extension = File.extname(file_path)
19
- data = nil
20
- if [".yaml", ".yml"].include? file_extension
21
- data = Secrets::YAML.resolve(file_path)
22
- unless data.nil?
23
- data = data.inputs
24
- validate_json_yaml(data)
25
- end
26
- elsif file_extension == ".csv"
27
- data = Waivers::CSVFileReader.resolve(file_path)
28
- headers = Waivers::CSVFileReader.headers
29
- validate_headers(headers)
30
- elsif file_extension == ".json"
31
- data = Waivers::JSONFileReader.resolve(file_path)
32
- validate_json_yaml(data) unless data.nil?
33
- end
34
- output.merge!(data) if !data.nil? && data.is_a?(Hash)
20
+ next unless valid_waiver_file?(file_path)
35
21
 
36
- if data.nil?
37
- raise Inspec::Exceptions::WaiversFileNotReadable,
38
- "Cannot find parser for waivers file '#{file_path}'. " \
39
- "Check to make sure file has the appropriate extension."
40
- end
22
+ data = parse_waiver_file(file_path)
23
+ output.merge!(data) if data.is_a?(Hash)
24
+ rescue Inspec::Exceptions::WaiversFileNotReadable, Inspec::Exceptions::WaiversFileInvalidFormatting => e
25
+ Inspec::Log.error "Error reading waivers file #{file_path}. #{e.message}"
26
+ Inspec::UI.new.exit(:usage_error)
41
27
  end
42
28
 
43
29
  @waivers_data[profile_id] = output
44
30
  end
45
31
 
46
- def self.validate_headers(headers, json_yaml = false)
47
- required_fields = json_yaml ? %w{justification} : %w{control_id justification}
48
- all_fields = %w{control_id justification expiration_date run}
32
+ def self.valid_waiver_file?(file_path)
33
+ # Check if the file is readable
34
+ file_extension = File.extname(file_path).downcase
35
+ unless SUPPORTED_FILE_EXTENSION.include?(file_extension)
36
+ raise Inspec::Exceptions::WaiversFileNotReadable,
37
+ "Unsupported file extension for '#{file_path}'. Allowed waiver file extensions: #{SUPPORTED_FILE_EXTENSION.join(", ")}"
38
+ end
39
+
40
+ # Check if the file is empty
41
+ if File.zero?(file_path)
42
+ Inspec::Log.warn "Waivers file '#{file_path}' is empty. Skipping waivers."
43
+ return false
44
+ end
49
45
 
50
- Inspec::Log.warn "Missing column headers: #{(required_fields - headers)}" unless (required_fields - headers).empty?
51
- Inspec::Log.warn "Invalid column header: Column can't be nil" if headers.include? nil
52
- Inspec::Log.warn "Extra column headers: #{(headers - all_fields)}" unless (headers - all_fields).empty?
46
+ true
47
+ end
48
+
49
+ def self.parse_waiver_file(file_path)
50
+ file_extension = File.extname(file_path).downcase
51
+
52
+ case file_extension
53
+ when ".yaml", ".yml"
54
+ data = Secrets::YAML.resolve(file_path)&.inputs
55
+ validate_json_yaml(data)
56
+ when ".csv"
57
+ data = Waivers::CSVFileReader.resolve(file_path)
58
+ validate_csv_headers(Waivers::CSVFileReader.headers)
59
+ when ".json"
60
+ data = Waivers::JSONFileReader.resolve(file_path)
61
+ validate_json_yaml(data)
62
+ end
63
+
64
+ data
65
+ end
66
+
67
+ def self.all_fields
68
+ %w{control_id justification expiration_date run}
69
+ end
70
+
71
+ def self.validate_csv_headers(headers)
72
+ invalid_headers_info = fetch_invalid_headers_info(headers)
73
+ # Warn if blank column found in csv file
74
+ Inspec::Log.warn "Invalid column headers: Column can't be nil" if invalid_headers_info[:blank_column]
75
+ # Warn if extra header found in csv file
76
+ Inspec::Log.warn "Extra header/s #{invalid_headers_info[:extra_headers]}" unless invalid_headers_info[:extra_headers].empty?
77
+ unless invalid_headers_info[:missing_required_fields].empty?
78
+ raise Inspec::Exceptions::WaiversFileInvalidFormatting,
79
+ "Missing required header/s #{invalid_headers_info[:missing_required_fields]}. Fix headers in file to proceed."
80
+ end
81
+ end
82
+
83
+ def self.fetch_invalid_headers_info(headers, json_yaml = false)
84
+ required_fields = json_yaml ? %w{justification} : %w{control_id justification}
85
+ data = {}
86
+ data[:missing_required_fields] = []
87
+ # Finds missing required fields
88
+ unless (required_fields - headers).empty?
89
+ data[:missing_required_fields] = required_fields - headers
90
+ end
91
+ # If column with no header found set the blank_column flag. Only applicable for csv
92
+ data[:blank_column] = headers.include?(nil) ? true : false
93
+ # Find extra headers/parameters
94
+ data[:extra_headers] = (headers - all_fields)
95
+ data
53
96
  end
54
97
 
55
98
  def self.validate_json_yaml(data)
56
- headers = []
57
- data.each_value do |value|
58
- headers.push value.keys
99
+ return if data.nil?
100
+
101
+ missing_required_field = false
102
+ data.each do |key, value|
103
+ # In case of yaml or json we need to validate headers/parametes for each value
104
+ invalid_headers_info = fetch_invalid_headers_info(value.keys, true)
105
+ # WARN in case of extra parameters found in each waived control
106
+ Inspec::Log.warn "Control ID #{key}: extra parameter/s #{invalid_headers_info[:extra_headers]}" unless invalid_headers_info[:extra_headers].empty?
107
+ unless invalid_headers_info[:missing_required_fields].empty?
108
+ missing_required_field = true
109
+ # Log error for each waived control
110
+ Inspec::Log.error "Control ID #{key}: missing required parameter/s #{invalid_headers_info[:missing_required_fields]}"
111
+ end
112
+ end
113
+
114
+ # Raise error if any of the waived control has missing required filed
115
+ if missing_required_field
116
+ raise Inspec::Exceptions::WaiversFileInvalidFormatting,
117
+ "Missing required parameter [justification]. Fix parameters in file to proceed."
59
118
  end
60
- validate_headers(headers.flatten.uniq, true)
61
119
  end
62
120
  end
63
121
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.22.65
4
+ version: 5.22.80
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef InSpec Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-12 00:00:00.000000000 Z
11
+ date: 2025-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-telemetry
@@ -185,6 +185,9 @@ dependencies:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
187
  version: '3.0'
188
+ - - "<"
189
+ - !ruby/object:Gem::Version
190
+ version: '3.2'
188
191
  type: :runtime
189
192
  prerelease: false
190
193
  version_requirements: !ruby/object:Gem::Requirement
@@ -192,6 +195,9 @@ dependencies:
192
195
  - - "~>"
193
196
  - !ruby/object:Gem::Version
194
197
  version: '3.0'
198
+ - - "<"
199
+ - !ruby/object:Gem::Version
200
+ version: '3.2'
195
201
  - !ruby/object:Gem::Dependency
196
202
  name: sslshake
197
203
  requirement: !ruby/object:Gem::Requirement
@@ -384,14 +390,14 @@ dependencies:
384
390
  requirements:
385
391
  - - "~>"
386
392
  - !ruby/object:Gem::Version
387
- version: '3.10'
393
+ version: 3.12.13
388
394
  type: :runtime
389
395
  prerelease: false
390
396
  version_requirements: !ruby/object:Gem::Requirement
391
397
  requirements:
392
398
  - - "~>"
393
399
  - !ruby/object:Gem::Version
394
- version: '3.10'
400
+ version: 3.12.13
395
401
  description: InSpec provides a framework for creating end-to-end infrastructure tests.
396
402
  You can use it for integration or even compliance testing. Create fully portable
397
403
  test profiles and use them in your workflow to ensure stability and security. Integrate
@@ -868,7 +874,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
868
874
  - !ruby/object:Gem::Version
869
875
  version: '0'
870
876
  requirements: []
871
- rubygems_version: 3.2.3
877
+ rubygems_version: 3.3.27
872
878
  signing_key:
873
879
  specification_version: 4
874
880
  summary: Infrastructure and compliance testing. Core library.