inspec 1.23.0 → 1.24.0

Sign up to get free protection for your applications and to get access to all the features.
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