inspec 1.23.0 → 1.24.0

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -2429
  3. data/Rakefile +1 -14
  4. data/docs/migration.md +1 -1
  5. data/docs/profiles.md +14 -12
  6. data/docs/resources/mssql_session.md.erb +79 -0
  7. data/docs/resources/oracledb_session.md.erb +71 -0
  8. data/docs/resources/postgres_session.md.erb +17 -10
  9. data/docs/resources/processes.md.erb +11 -1
  10. data/docs/resources/service.md.erb +1 -1
  11. data/examples/inheritance/inspec.lock +11 -0
  12. data/examples/meta-profile/README.md +2 -2
  13. data/examples/meta-profile/controls/example.rb +1 -1
  14. data/examples/meta-profile/inspec.lock +18 -0
  15. data/examples/meta-profile/inspec.yml +1 -1
  16. data/examples/meta-profile/vendor/4d5c9187409941b96f00fb25d0888c301ede999fd63149f35ad4594d698d6535.tar.gz +0 -0
  17. data/examples/meta-profile/vendor/79e6b9846ab539669bbfcf5adcd246f1be484d4b55acb7c1c3dbd852203e4fae.tar.gz +0 -0
  18. data/examples/meta-profile/vendor/dbb5602f09f58d86f8743dfb44327207e9a23a49ef34f65614f1c1d8cc145f6b.tar.gz +0 -0
  19. data/lib/bundles/inspec-habitat/profile.rb +1 -1
  20. data/lib/inspec.rb +1 -0
  21. data/lib/inspec/base_cli.rb +1 -1
  22. data/lib/inspec/dsl.rb +1 -3
  23. data/lib/inspec/exceptions.rb +8 -0
  24. data/lib/inspec/metadata.rb +12 -9
  25. data/lib/inspec/profile.rb +22 -0
  26. data/lib/inspec/resource.rb +1 -0
  27. data/lib/inspec/rspec_json_formatter.rb +9 -0
  28. data/lib/inspec/runner.rb +14 -9
  29. data/lib/inspec/schema.rb +11 -0
  30. data/lib/inspec/secrets/yaml.rb +5 -0
  31. data/lib/inspec/version.rb +1 -1
  32. data/lib/resources/mssql_session.rb +30 -9
  33. data/lib/resources/mysql_session.rb +5 -3
  34. data/lib/resources/oracledb_session.rb +42 -0
  35. data/lib/resources/postgres_session.rb +12 -9
  36. metadata +12 -2
@@ -11,10 +11,11 @@ module Inspec
11
11
  # Extract metadata.rb information
12
12
  class Metadata # rubocop:disable Metrics/ClassLength
13
13
  attr_reader :ref
14
- attr_accessor :params
14
+ attr_accessor :params, :content
15
15
  def initialize(ref, logger = nil)
16
16
  @ref = ref
17
17
  @logger = logger || Logger.new(nil)
18
+ @content = ''
18
19
  @params = {}
19
20
  @missing_methods = []
20
21
  end
@@ -206,26 +207,28 @@ module Inspec
206
207
  metadata
207
208
  end
208
209
 
209
- def self.from_yaml(ref, contents, profile_id, logger = nil)
210
+ def self.from_yaml(ref, content, profile_id, logger = nil)
210
211
  res = Metadata.new(ref, logger)
211
- res.params = YAML.load(contents)
212
+ res.params = YAML.load(content)
213
+ res.content = content
212
214
  finalize(res, profile_id, {}, logger)
213
215
  end
214
216
 
215
- def self.from_ruby(ref, contents, profile_id, logger = nil)
217
+ def self.from_ruby(ref, content, profile_id, logger = nil)
216
218
  res = Metadata.new(ref, logger)
217
- res.instance_eval(contents, ref, 1)
219
+ res.instance_eval(content, ref, 1)
220
+ res.content = content
218
221
  finalize(res, profile_id, {}, logger)
219
222
  end
220
223
 
221
- def self.from_ref(ref, contents, profile_id, logger = nil)
224
+ def self.from_ref(ref, content, profile_id, logger = nil)
222
225
  # NOTE there doesn't have to exist an actual file, it may come from an
223
- # archive (i.e., contents)
226
+ # archive (i.e., content)
224
227
  case File.basename(ref)
225
228
  when 'inspec.yml'
226
- from_yaml(ref, contents, profile_id, logger)
229
+ from_yaml(ref, content, profile_id, logger)
227
230
  when 'metadata.rb'
228
- from_ruby(ref, contents, profile_id, logger)
231
+ from_ruby(ref, content, profile_id, logger)
229
232
  else
230
233
  logger ||= Logger.new(nil)
231
234
  logger.error "Don't know how to handle metadata in #{ref}"
@@ -4,6 +4,7 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  require 'forwardable'
7
+ require 'digest'
7
8
  require 'inspec/polyfill'
8
9
  require 'inspec/cached_fetcher'
9
10
  require 'inspec/file_provider'
@@ -208,6 +209,7 @@ module Inspec
208
209
 
209
210
  # add information about the required attributes
210
211
  res[:attributes] = res[:attributes].map(&:to_hash) unless res[:attributes].nil? || res[:attributes].empty?
212
+ res[:sha256] = sha256
211
213
  res
212
214
  end
213
215
 
@@ -395,6 +397,26 @@ module Inspec
395
397
  Inspec::DependencySet.from_lockfile(lockfile, cwd, @cache, @backend, { attributes: @attr_values })
396
398
  end
397
399
 
400
+ # Calculate this profile's SHA256 checksum. Includes metadata, dependencies,
401
+ # libraries, data files, and controls.
402
+ #
403
+ # @return [Type] description of returned object
404
+ def sha256
405
+ # get all dependency checksums
406
+ deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }]
407
+
408
+ res = Digest::SHA256.new
409
+ files = source_reader.tests.to_a + source_reader.libraries.to_a +
410
+ source_reader.data_files.to_a +
411
+ [['inspec.yml', source_reader.metadata.content]] +
412
+ [['inspec.lock.deps', YAML.dump(deps)]]
413
+
414
+ files.sort { |a, b| a[0] <=> b[0] }
415
+ .map { |f| res << f[0] << "\0" << f[1] << "\0" }
416
+
417
+ res.hexdigest
418
+ end
419
+
398
420
  private
399
421
 
400
422
  # Create an archive name for this profile and an additional options
@@ -114,6 +114,7 @@ require 'resources/mysql_session'
114
114
  require 'resources/npm'
115
115
  require 'resources/ntp_conf'
116
116
  require 'resources/oneget'
117
+ require 'resources/oracledb_session'
117
118
  require 'resources/os'
118
119
  require 'resources/os_env'
119
120
  require 'resources/package'
@@ -114,6 +114,10 @@ class InspecRspecJson < InspecRspecMiniJson # rubocop:disable Metrics/ClassLengt
114
114
 
115
115
  @output_hash[:other_checks] = examples_without_controls
116
116
  @output_hash[:profiles] = profiles_info
117
+ @output_hash[:platform] = {
118
+ name: os(:name),
119
+ release: os(:release),
120
+ }
117
121
 
118
122
  examples_with_controls.each do |example|
119
123
  control = example2control(example)
@@ -123,6 +127,11 @@ class InspecRspecJson < InspecRspecMiniJson # rubocop:disable Metrics/ClassLengt
123
127
 
124
128
  private
125
129
 
130
+ def os(field)
131
+ return nil if @backend.nil?
132
+ @backend.os.params[field]
133
+ end
134
+
126
135
  def all_unique_controls
127
136
  Array(@all_controls).uniq
128
137
  end
@@ -119,18 +119,23 @@ module Inspec
119
119
 
120
120
  # determine all attributes before the execution, fetch data from secrets backend
121
121
  def load_attributes(options)
122
- attributes = {}
123
- # read endpoints for secrets eg. yml file
122
+ options[:attributes] ||= {}
123
+
124
124
  secrets_targets = options[:attrs]
125
- unless secrets_targets.nil?
126
- secrets_targets.each do |target|
127
- secrets = Inspec::SecretsBackend.resolve(target)
128
- # merge hash values
129
- attributes = attributes.merge(secrets.attributes) unless secrets.nil? || secrets.attributes.nil?
125
+ return options[:attributes] if secrets_targets.nil?
126
+
127
+ secrets_targets.each do |target|
128
+ secrets = Inspec::SecretsBackend.resolve(target)
129
+ if secrets.nil?
130
+ raise Inspec::Exceptions::SecretsBackendNotFound,
131
+ "Unable to find a parser for attributes file #{target}. " \
132
+ 'Check to make sure the file exists and has the appropriate extension.'
130
133
  end
134
+
135
+ next if secrets.attributes.nil?
136
+ options[:attributes].merge!(secrets.attributes)
131
137
  end
132
- options[:attributes] = options[:attributes] || {}
133
- options[:attributes] = options[:attributes].merge(attributes)
138
+
134
139
  options[:attributes]
135
140
  end
136
141
 
@@ -11,6 +11,15 @@ module Inspec
11
11
  },
12
12
  }.freeze
13
13
 
14
+ PLATFORM = {
15
+ 'type' => 'object',
16
+ 'additionalProperties' => false,
17
+ 'properties' => {
18
+ 'name' => { 'type' => 'string' },
19
+ 'release' => { 'type' => 'string' },
20
+ },
21
+ }.freeze
22
+
14
23
  # Tags are open right, with simple key-value associations and not restrictions
15
24
  TAGS = { 'type' => 'object' }.freeze
16
25
 
@@ -85,6 +94,7 @@ module Inspec
85
94
  'properties' => {
86
95
  'name' => { 'type' => 'string' },
87
96
  'version' => { 'type' => 'string', 'optional' => true },
97
+ 'sha256' => { 'type' => 'string', 'optional' => false },
88
98
 
89
99
  'title' => { 'type' => 'string', 'optional' => true },
90
100
  'maintainer' => { 'type' => 'string', 'optional' => true },
@@ -117,6 +127,7 @@ module Inspec
117
127
  'type' => 'object',
118
128
  'additionalProperties' => false,
119
129
  'properties' => {
130
+ 'platform' => PLATFORM,
120
131
  'profiles' => {
121
132
  'type' => 'array',
122
133
  'items' => PROFILE,
@@ -18,6 +18,11 @@ module Secrets
18
18
  # array of yaml file paths
19
19
  def initialize(target)
20
20
  @attributes = ::YAML.load_file(target)
21
+
22
+ if @attributes == false || !@attributes.is_a?(Hash)
23
+ Inspec::Log.warn("#{self.class} unable to parse #{target}: invalid YAML or contents is not a Hash")
24
+ @attributes = nil
25
+ end
21
26
  rescue => e
22
27
  raise "Error reading Inspec attributes: #{e}"
23
28
  end
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.23.0'.freeze
7
+ VERSION = '1.24.0'.freeze
8
8
  end
@@ -7,27 +7,48 @@ module Inspec::Resources
7
7
  name 'mssql_session'
8
8
  desc 'Use the mssql_session InSpec audit resource to test SQL commands run against a MS Sql Server database.'
9
9
  example "
10
- sql = mssql_session('myuser','mypassword')
10
+ # Using SQL authentication
11
+ sql = mssql_session(user: 'myuser', pass: 'mypassword')
11
12
  describe sql.query('select * from sys.databases where name like \'*test*\') do
12
- its('stdout') {should_not match(/test/) }
13
+ its('stdout') { should_not match(/test/) }
14
+ end
15
+
16
+ # Passing no credentials to mssql_session forces it to use Windows authentication
17
+ sql_windows_auth = mssql_session
18
+ describe sql_window_auth.query('select * from sys.databases where name like \'*test*\') do
19
+ its('stdout') { should_not match(/test/) }
13
20
  end
14
21
  "
15
22
 
16
- def initialize(user = nil, pass = nil)
17
- @user = user
18
- @pass = pass
19
- skip_resource('user and pass are required for MSSQL tests') if @user.nil? or @pass.nil?
23
+ attr_reader :user, :pass, :host
24
+
25
+ def initialize(opts = {})
26
+ @user = opts[:user]
27
+ @pass = opts[:pass]
28
+ @host = opts[:host] || 'localhost'
29
+ @instance = opts[:instance]
20
30
  end
21
31
 
22
32
  def query(q)
23
33
  escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$').gsub(/\@/, '`@')
24
- cmd = inspec.command("sqlcmd -U #{@user} -P #{@pass} -Q \"#{escaped_query}\"")
25
-
34
+ cmd_string = "sqlcmd -Q \"#{escaped_query}\""
35
+ cmd_string += " -U #{@user} -P #{@pass}" unless @user.nil? or @pass.nil?
36
+ if @instance.nil?
37
+ cmd_string += " -S #{@host}"
38
+ else
39
+ cmd_string += " -S #{@host}\\#{@instance}"
40
+ end
41
+ puts cmd_string
42
+ cmd = inspec.command(cmd_string)
43
+ out = cmd.stdout + "\n" + cmd.stderr
44
+ if out =~ /Sqlcmd: Error/
45
+ skip_resource("Can't connect to the MS SQL Server.")
46
+ end
26
47
  cmd
27
48
  end
28
49
 
29
50
  def to_s
30
- 'MSSQL'
51
+ 'MSSQL session'
31
52
  end
32
53
  end
33
54
  end
@@ -2,6 +2,7 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
+ # author: Aaron Lippold
5
6
  # license: All rights reserved
6
7
 
7
8
  module Inspec::Resources
@@ -9,15 +10,16 @@ module Inspec::Resources
9
10
  name 'mysql_session'
10
11
  desc 'Use the mysql_session InSpec audit resource to test SQL commands run against a MySQL database.'
11
12
  example "
12
- sql = mysql_session('my_user','password')
13
+ sql = mysql_session('my_user','password','host')
13
14
  describe sql.query('show databases like \'test\';') do
14
15
  its('stdout') { should_not match(/test/) }
15
16
  end
16
17
  "
17
18
 
18
- def initialize(user = nil, pass = nil)
19
+ def initialize(user = nil, pass = nil, host = 'localhost')
19
20
  @user = user
20
21
  @pass = pass
22
+ @host = host
21
23
  init_fallback if user.nil? or pass.nil?
22
24
  skip_resource("Can't run MySQL SQL checks without authentication") if @user.nil? or @pass.nil?
23
25
  end
@@ -28,7 +30,7 @@ module Inspec::Resources
28
30
  escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$')
29
31
 
30
32
  # run the query
31
- cmd = inspec.command("mysql -u#{@user} -p#{@pass} #{db} -s -e \"#{escaped_query}\"")
33
+ cmd = inspec.command("mysql -u#{@user} -p#{@pass} -h #{@host} #{db} -s -e \"#{escaped_query}\"")
32
34
  out = cmd.stdout + "\n" + cmd.stderr
33
35
  if out =~ /Can't connect to .* MySQL server/ or
34
36
  out.downcase =~ /^error/
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ # author: Nolan Davidson
3
+ # license: All rights reserved
4
+
5
+ module Inspec::Resources
6
+ class OracledbSession < Inspec.resource(1)
7
+ name 'oracledb_session'
8
+ desc 'Use the oracledb_session InSpec resource to test commands against an Oracle database'
9
+ example "
10
+ sql = oracledb_session(user: 'my_user', pass: 'password')
11
+ describe sql.query('SELECT NAME FROM v$database;') do
12
+ its('stdout') { should_not match(/test/) }
13
+ end
14
+ "
15
+
16
+ attr_reader :user, :pass, :host, :sid, :sqlplus_bin
17
+
18
+ def initialize(opts = {})
19
+ @user = opts[:user]
20
+ @pass = opts[:pass]
21
+ @host = opts[:host] || 'localhost'
22
+ @sid = opts[:sid]
23
+ @sqlplus_bin = opts[:sqlplus_bin] || 'sqlplus'
24
+ return skip_resource("Can't run Oracle checks without authentication") if @user.nil? or @pass.nil?
25
+ end
26
+
27
+ def query(q)
28
+ escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"')
29
+ cmd = inspec.command("echo \"#{escaped_query}\" | #{@sqlplus_bin} -s #{@user}/#{@pass}@#{@host}/#{@sid}")
30
+ out = cmd.stdout + "\n" + cmd.stderr
31
+ if out.downcase =~ /^error/
32
+ skip_resource("Can't connect to Oracle instance for SQL checks.")
33
+ end
34
+
35
+ cmd
36
+ end
37
+
38
+ def to_s
39
+ 'Oracle Session'
40
+ end
41
+ end
42
+ end
@@ -2,6 +2,7 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
+ # author: Aaron Lippold
5
6
  # license: All rights reserved
6
7
 
7
8
  module Inspec::Resources
@@ -26,16 +27,23 @@ module Inspec::Resources
26
27
  name 'postgres_session'
27
28
  desc 'Use the postgres_session InSpec audit resource to test SQL commands run against a PostgreSQL database.'
28
29
  example "
29
- sql = postgres_session('username', 'password')
30
+ sql = postgres_session('username', 'password', 'host')
31
+ query('sql_query', ['database_name'])` contains the query and (optional) database to execute
32
+
33
+ # default values:
34
+ # username: 'postgres'
35
+ # host: 'localhost'
36
+ # db: databse == db_user running the sql query
30
37
 
31
38
  describe sql.query('SELECT * FROM pg_shadow WHERE passwd IS NULL;') do
32
39
  its('output') { should eq('') }
33
40
  end
34
41
  "
35
42
 
36
- def initialize(user, pass)
43
+ def initialize(user, pass, host = nil)
37
44
  @user = user || 'postgres'
38
45
  @pass = pass
46
+ @host = host || 'localhost'
39
47
  end
40
48
 
41
49
  def query(query, db = [])
@@ -44,7 +52,7 @@ module Inspec::Resources
44
52
  # that does this securely
45
53
  escaped_query = query.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$')
46
54
  # run the query
47
- cmd = inspec.command("PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h localhost -c \"#{escaped_query}\"")
55
+ cmd = inspec.command("PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h #{@host} -A -t -c \"#{escaped_query}\"")
48
56
  out = cmd.stdout + "\n" + cmd.stderr
49
57
  if cmd.exit_status != 0 or
50
58
  out =~ /could not connect to .*/ or
@@ -52,12 +60,7 @@ module Inspec::Resources
52
60
  # skip this test if the server can't run the query
53
61
  skip_resource "Can't read run query #{query.inspect} on postgres_session: #{out}"
54
62
  else
55
- # remove the whole header (i.e. up to the first ^-----+------+------$)
56
- # remove the tail
57
- lines = cmd.stdout
58
- .sub(/(.*\n)+([-]+[+])*[-]+\n/, '')
59
- .sub(/\n[^\n]*\n\n$/, '')
60
- Lines.new(lines.strip, "PostgreSQL query: #{query}")
63
+ Lines.new(cmd.stdout.strip, "PostgreSQL query: #{query}")
61
64
  end
62
65
  end
63
66
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.23.0
4
+ version: 1.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-04 00:00:00.000000000 Z
11
+ date: 2017-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train
@@ -291,6 +291,7 @@ files:
291
291
  - docs/migration.md
292
292
  - docs/plugin_kitchen_inspec.html.md
293
293
  - docs/profiles.md
294
+ - docs/resources.md
294
295
  - docs/resources/apache_conf.md.erb
295
296
  - docs/resources/apt.md.erb
296
297
  - docs/resources/audit_policy.md.erb
@@ -330,11 +331,13 @@ files:
330
331
  - docs/resources/limits_conf.md.erb
331
332
  - docs/resources/login_def.md.erb
332
333
  - docs/resources/mount.md.erb
334
+ - docs/resources/mssql_session.md.erb
333
335
  - docs/resources/mysql_conf.md.erb
334
336
  - docs/resources/mysql_session.md.erb
335
337
  - docs/resources/npm.md.erb
336
338
  - docs/resources/ntp_conf.md.erb
337
339
  - docs/resources/oneget.md.erb
340
+ - docs/resources/oracledb_session.md.erb
338
341
  - docs/resources/os.md.erb
339
342
  - docs/resources/os_env.md.erb
340
343
  - docs/resources/package.md.erb
@@ -380,6 +383,7 @@ files:
380
383
  - examples/README.md
381
384
  - examples/inheritance/README.md
382
385
  - examples/inheritance/controls/example.rb
386
+ - examples/inheritance/inspec.lock
383
387
  - examples/inheritance/inspec.yml
384
388
  - examples/kitchen-ansible/.kitchen.yml
385
389
  - examples/kitchen-ansible/Gemfile
@@ -405,7 +409,11 @@ files:
405
409
  - examples/kitchen-puppet/test/integration/default/web_spec.rb
406
410
  - examples/meta-profile/README.md
407
411
  - examples/meta-profile/controls/example.rb
412
+ - examples/meta-profile/inspec.lock
408
413
  - examples/meta-profile/inspec.yml
414
+ - examples/meta-profile/vendor/4d5c9187409941b96f00fb25d0888c301ede999fd63149f35ad4594d698d6535.tar.gz
415
+ - examples/meta-profile/vendor/79e6b9846ab539669bbfcf5adcd246f1be484d4b55acb7c1c3dbd852203e4fae.tar.gz
416
+ - examples/meta-profile/vendor/dbb5602f09f58d86f8743dfb44327207e9a23a49ef34f65614f1c1d8cc145f6b.tar.gz
409
417
  - examples/profile-attribute.yml
410
418
  - examples/profile-attribute/README.md
411
419
  - examples/profile-attribute/controls/example.rb
@@ -474,6 +482,7 @@ files:
474
482
  - lib/inspec/dsl_shared.rb
475
483
  - lib/inspec/env_printer.rb
476
484
  - lib/inspec/errors.rb
485
+ - lib/inspec/exceptions.rb
477
486
  - lib/inspec/expect.rb
478
487
  - lib/inspec/fetcher.rb
479
488
  - lib/inspec/file_provider.rb
@@ -559,6 +568,7 @@ files:
559
568
  - lib/resources/npm.rb
560
569
  - lib/resources/ntp_conf.rb
561
570
  - lib/resources/oneget.rb
571
+ - lib/resources/oracledb_session.rb
562
572
  - lib/resources/os.rb
563
573
  - lib/resources/os_env.rb
564
574
  - lib/resources/package.rb