inspec 1.26.0 → 1.27.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.
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require 'digest'
3
2
  require 'fileutils'
4
3
 
5
4
  module Inspec
@@ -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
  #
@@ -7,6 +7,7 @@ require 'logger'
7
7
  require 'rubygems/version'
8
8
  require 'rubygems/requirement'
9
9
  require 'semverse'
10
+ require 'utils/spdx'
10
11
 
11
12
  module Inspec
12
13
  # Extract metadata.rb information
@@ -102,7 +103,7 @@ module Inspec
102
103
  end
103
104
 
104
105
  # return all warn and errors
105
- def valid
106
+ def valid # rubocop:disable Metrics/AbcSize
106
107
  errors = []
107
108
  warnings = []
108
109
 
@@ -116,11 +117,16 @@ module Inspec
116
117
  errors.push('Version needs to be in SemVer format')
117
118
  end
118
119
 
119
- %w{ title summary maintainer copyright }.each do |field|
120
+ %w{ title summary maintainer copyright license }.each do |field|
120
121
  next unless params[field.to_sym].nil?
121
122
  warnings.push("Missing profile #{field} in #{ref}")
122
123
  end
123
124
 
125
+ # if version is set, ensure it is in SPDX format
126
+ if !params[:license].nil? && !Spdx.valid_license?(params[:license])
127
+ warnings.push("License '#{params[:license]}' needs to be in SPDX format. See https://spdx.org/licenses/.")
128
+ end
129
+
124
130
  [errors, warnings]
125
131
  end
126
132
 
@@ -3,7 +3,6 @@
3
3
  # author: Christoph Hartmann
4
4
  require 'utils/plugin_registry'
5
5
  require 'inspec/file_provider'
6
- require 'digest'
7
6
 
8
7
  module Inspec
9
8
  module Plugins
@@ -4,7 +4,7 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  require 'forwardable'
7
- require 'digest'
7
+ require 'openssl'
8
8
  require 'inspec/polyfill'
9
9
  require 'inspec/cached_fetcher'
10
10
  require 'inspec/file_provider'
@@ -406,7 +406,7 @@ module Inspec
406
406
  # get all dependency checksums
407
407
  deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }]
408
408
 
409
- res = Digest::SHA256.new
409
+ res = OpenSSL::Digest::SHA256.new
410
410
  files = source_reader.tests.to_a + source_reader.libraries.to_a +
411
411
  source_reader.data_files.to_a +
412
412
  [['inspec.yml', source_reader.metadata.content]] +
@@ -415,7 +415,7 @@ module Inspec
415
415
  files.sort { |a, b| a[0] <=> b[0] }
416
416
  .map { |f| res << f[0] << "\0" << f[1] << "\0" }
417
417
 
418
- res.hexdigest
418
+ res.digest.unpack('H*')[0]
419
419
  end
420
420
 
421
421
  private
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.26.0'.freeze
7
+ VERSION = '1.27.0'.freeze
8
8
  end
@@ -88,7 +88,6 @@ end
88
88
 
89
89
  RSpec::Matchers.define :contain_duplicates do
90
90
  match do |arr|
91
- warn '[DEPRECATION] `contain_duplicates` is deprecated and will be removed in the next major version. See https://github.com/chef/inspec/issues/738 for more details'
92
91
  dup = arr.select { |element| arr.count(element) > 1 }
93
92
  !dup.uniq.empty?
94
93
  end
@@ -50,7 +50,7 @@ module Inspec::Resources
50
50
  if inspec.os.linux?
51
51
  res = inspec.backend.run_command("bash -c 'type \"#{@command}\"'")
52
52
  elsif inspec.os.windows?
53
- res = inspec.backend.run_command("where.exe \"#{@command}\"")
53
+ res = inspec.backend.run_command("Get-Command \"#{@command}\"")
54
54
  elsif inspec.os.unix?
55
55
  res = inspec.backend.run_command("type \"#{@command}\"")
56
56
  else
@@ -46,15 +46,30 @@ module Inspec::Resources
46
46
  data, = parse_comment_line(l, comment_char: '#', standalone_comments: false)
47
47
  return nil if data.nil? || data.empty?
48
48
 
49
- elements = data.split(/\s+/, 6)
50
- {
51
- 'minute' => elements.at(0),
52
- 'hour' => elements.at(1),
53
- 'day' => elements.at(2),
54
- 'month' => elements.at(3),
55
- 'weekday' => elements.at(4),
56
- 'command' => elements.at(5),
57
- }
49
+ case data
50
+ when /@hourly .*/
51
+ { 'minute' => '0', 'hour' => '*', 'day' => '*', 'month' => '*', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
52
+ when /@(midnight|daily) .*/
53
+ { 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
54
+ when /@weekly .*/
55
+ { 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '0', 'command' => data.split(/\s+/, 2).at(1) }
56
+ when /@monthly ./
57
+ { 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '*', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
58
+ when /@(annually|yearly) .*/
59
+ { 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '1', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
60
+ when /@reboot .*/
61
+ { 'minute' => '-1', 'hour' => '-1', 'day' => '-1', 'month' => '-1', 'weekday' => '-1', 'command' => data.split(/\s+/, 2).at(1) }
62
+ else
63
+ elements = data.split(/\s+/, 6)
64
+ {
65
+ 'minute' => elements.at(0),
66
+ 'hour' => elements.at(1),
67
+ 'day' => elements.at(2),
68
+ 'month' => elements.at(3),
69
+ 'weekday' => elements.at(4),
70
+ 'command' => elements.at(5),
71
+ }
72
+ end
58
73
  end
59
74
 
60
75
  def crontab_cmd
@@ -148,7 +148,7 @@ module Inspec::Resources
148
148
  def ping(hostname, port = nil, _proto = nil)
149
149
  # ICMP: Test-NetConnection www.microsoft.com
150
150
  # TCP and port: Test-NetConnection -ComputerName www.microsoft.com -RemotePort 80
151
- request = "Test-NetConnection -ComputerName #{hostname}"
151
+ request = "Test-NetConnection -ComputerName #{hostname} -WarningAction SilentlyContinue"
152
152
  request += " -RemotePort #{port}" unless port.nil?
153
153
  request += '| Select-Object -Property ComputerName, TcpTestSucceeded, PingSucceeded | ConvertTo-Json'
154
154
  cmd = inspec.command(request)
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # author: Christoph Hartmann
3
3
  # author: Dominik Richter
4
+ # author: Aaron Lippold
4
5
 
5
6
  require 'utils/convert'
6
7
 
@@ -64,7 +65,7 @@ module Inspec::Resources
64
65
  class LinuxInterface < InterfaceInfo
65
66
  def interface_info(iface)
66
67
  # will return "[mtu]\n1500\n[type]\n1"
67
- cmd = inspec.command("find /sys/class/net/#{iface}/ -type f -maxdepth 1 -exec sh -c 'echo \"[$(basename {})]\"; cat {} || echo -n' \\;")
68
+ cmd = inspec.command("find /sys/class/net/#{iface}/ -maxdepth 1 -type f -exec sh -c 'echo \"[$(basename {})]\"; cat {} || echo -n' \\;")
68
69
  return nil if cmd.exit_status.to_i != 0
69
70
 
70
71
  # parse values, we only recieve values, therefore we threat them as keys
@@ -2,13 +2,14 @@
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
8
9
  class Postgres < Inspec.resource(1)
9
10
  name 'postgres'
10
11
 
11
- attr_reader :service, :data_dir, :conf_dir, :conf_path
12
+ attr_reader :service, :data_dir, :conf_dir, :conf_path, :version, :cluster
12
13
  def initialize
13
14
  os = inspec.os
14
15
  if os.debian?
@@ -18,48 +19,26 @@ module Inspec::Resources
18
19
  # Debian allows multiple versions of postgresql to be
19
20
  # installed as well as multiple "clusters" to be configured.
20
21
  #
21
- version = version_from_dir('/etc/postgresql')
22
- cluster = cluster_from_dir("/etc/postgresql/#{version}")
23
- @conf_dir = "/etc/postgresql/#{version}/#{cluster}"
24
- @data_dir = "/var/lib/postgresql/#{version}/#{cluster}"
25
- elsif os.redhat?
26
- #
27
- # /var/lib/pgsql/data is the default data directory on RHEL6
28
- # and RHEL7. However, PR #824 explicitly added version-based
29
- # directories. Thus, we call #version_from_dir unless it looks
30
- # like we are using unversioned directories.
31
- #
32
- # TODO(ssd): This has the potential to be noisy because of the
33
- # warning in version_from_dir. We should determine which case
34
- # is more common and only warn in the less common case.
35
- #
36
- version = if inspec.directory('/var/lib/pgsql/data').exist?
37
- warn 'Found /var/lib/pgsql/data. Assuming postgresql install uses un-versioned directories.'
38
- nil
39
- else
40
- version_from_dir('/var/lib/pgsql/')
41
- end
42
-
43
- @data_dir = File.join('/var/lib/pgsql/', version.to_s, 'data')
44
- elsif os[:name] == 'arch'
45
- #
46
- # https://wiki.archlinux.org/index.php/PostgreSQL
47
- #
48
- # The archlinux wiki points to /var/lib/postgresql/data as the
49
- # main data directory.
50
- #
51
- @data_dir = '/var/lib/postgres/data'
22
+ @version = version_from_psql || version_from_dir('/etc/postgresql')
23
+ @cluster = cluster_from_dir("/etc/postgresql/#{@version}")
24
+ @conf_dir = "/etc/postgresql/#{@version}/#{@cluster}"
25
+ @data_dir = "/var/lib/postgresql/#{@version}/#{@cluster}"
52
26
  else
53
- #
54
- # According to https://www.postgresql.org/docs/9.5/static/creating-cluster.html
55
- #
56
- # > There is no default, although locations such as
57
- # > /usr/local/pgsql/data or /var/lib/pgsql/data are popular.
58
- #
59
- @data_dir = '/var/lib/pgsql/data'
27
+ @version = version_from_psql
28
+ if @version.nil?
29
+ if inspec.directory('/var/lib/pgsql/data').exist?
30
+ warn 'Unable to determine PostgreSQL version: psql did not return
31
+ a version number and unversioned data directories were found.'
32
+ nil
33
+ else
34
+ @version = version_from_dir('/var/lib/pgsql/')
35
+ end
36
+ end
37
+ @data_dir = locate_data_dir_location_by_version(@version)
60
38
  end
61
39
 
62
40
  @service = 'postgresql'
41
+ @service += "-#{@version}" if @version.to_f >= 9.4
63
42
  @conf_dir ||= @data_dir
64
43
  verify_dirs
65
44
  @conf_path = File.join @conf_dir, 'postgresql.conf'
@@ -81,6 +60,33 @@ module Inspec::Resources
81
60
  end
82
61
  end
83
62
 
63
+ def version_from_psql
64
+ return unless inspec.command('psql').exist?
65
+ inspec.command("psql --version | awk '{ print $NF }' | awk -F. '{ print $1\".\"$2 }'").stdout.strip
66
+ end
67
+
68
+ def locate_data_dir_location_by_version(ver = @version)
69
+ data_dir_loc = nil
70
+ dir_list = [
71
+ "/var/lib/pgsql/#{ver}/data",
72
+ '/var/lib/pgsql/data',
73
+ '/var/lib/postgres/data',
74
+ '/var/lib/postgresql/data',
75
+ ]
76
+
77
+ dir_list.each do |dir|
78
+ data_dir_loc if inspec.directory(dir).exists?
79
+ break
80
+ end
81
+
82
+ if data_dir_loc.nil?
83
+ warn 'Unable to find the PostgreSQL data_dir in expected location(s), please
84
+ execute "psql -t -A -p <port> -h <host> -c "show hba_file";" as the PostgreSQL
85
+ DBA to find the non-starndard data_dir location.'
86
+ end
87
+ data_dir_loc
88
+ end
89
+
84
90
  def version_from_dir(dir)
85
91
  dirs = inspec.command("ls -d #{dir}/*/").stdout
86
92
  entries = dirs.lines.count
@@ -25,15 +25,24 @@ module Inspec::Resources
25
25
  @grep = grep
26
26
  # turn into a regexp if it isn't one yet
27
27
  if grep.class == String
28
- grep = '(/[^/]*)*' + grep if grep[0] != '/'
29
- grep = Regexp.new('^' + grep + '(\s|$)')
28
+ # if windows ignore case as we can't make up our minds
29
+ if inspec.os.windows?
30
+ grep = '(?i)' + grep
31
+ else
32
+ grep = '(/[^/]*)*' + grep unless grep[0] == '/'
33
+ grep = '^' + grep + '(\s|$)'
34
+ end
35
+ grep = Regexp.new(grep)
30
36
  end
37
+
31
38
  all_cmds = ps_axo
32
39
  @list = all_cmds.find_all do |hm|
33
40
  hm[:command] =~ grep
34
41
  end
42
+ end
35
43
 
36
- return skip_resource 'The `processes` resource is not supported on your OS yet.' if inspec.os.windows?
44
+ def exists?
45
+ !@list.empty?
37
46
  end
38
47
 
39
48
  def to_s
@@ -74,6 +83,10 @@ module Inspec::Resources
74
83
  if os.linux?
75
84
  command = 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command'
76
85
  regex = /^([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
86
+ elsif os.windows?
87
+ command = '$Proc = Get-Process -IncludeUserName | Where-Object {$_.Path -ne $null } | Select-Object PriorityClass,Id,CPU,PM,VirtualMemorySize,NPM,SessionId,Responding,StartTime,TotalProcessorTime,UserName,Path | ConvertTo-Csv -NoTypeInformation;$Proc.Replace("""","").Replace("`r`n","`n")'
88
+ # Wanted to use /(?:^|,)([^,]*)/; works on rubular.com not sure why here?
89
+ regex = /^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/
77
90
  else
78
91
  command = 'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command'
79
92
  regex = /^\s*([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
@@ -95,7 +108,7 @@ module Inspec::Resources
95
108
  end.compact
96
109
  lines.map do |m|
97
110
  a = m.to_a[1..-1] # grab all matching groups
98
- a.unshift(nil) unless os.linux?
111
+ a.unshift(nil) unless os.linux? || os.windows?
99
112
  a[1] = a[1].to_i
100
113
  a[4] = a[4].to_i
101
114
  a[5] = a[5].to_i
@@ -26,8 +26,8 @@ module FindFiles
26
26
  type = TYPES[opts[:type].to_sym] if opts[:type]
27
27
 
28
28
  cmd = "find #{path}"
29
- cmd += " -maxdepth #{depth.to_i}" if depth.to_i > 0
30
29
  cmd += " -type #{type}" unless type.nil?
30
+ cmd += " -maxdepth #{depth.to_i}" if depth.to_i > 0
31
31
 
32
32
  result = inspec.command(cmd)
33
33
  exit_status = result.exit_status
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'parslet'
6
+
7
+ class NginxParser < Parslet::Parser
8
+ root :outermost
9
+ # only designed for rabbitmq config files for now:
10
+ rule(:outermost) { filler? >> exp.repeat }
11
+
12
+ rule(:filler?) { one_filler.repeat }
13
+ rule(:one_filler) { match('\s+') | match["\n"] | comment }
14
+ rule(:space) { match('\s+') }
15
+ rule(:comment) { str('#') >> (match["\n\r"].absent? >> any).repeat }
16
+
17
+ rule(:exp) {
18
+ section | assignment
19
+ }
20
+ rule(:assignment) {
21
+ (identifier >> values.maybe.as(:args)).as(:assignment) >> str(';') >> filler?
22
+ }
23
+
24
+ rule(:identifier) {
25
+ (match('[a-zA-Z]') >> match('[a-zA-Z0-9_]').repeat).as(:identifier) >> space >> space.repeat
26
+ }
27
+
28
+ rule(:value) {
29
+ ((match('[#;{]').absent? >> any) >> (
30
+ str('\\') >> any | match('[#;{]|\s').absent? >> any
31
+ ).repeat).as(:value) >> space.repeat
32
+ }
33
+ rule(:values) {
34
+ value.repeat >> space.maybe
35
+ }
36
+
37
+ rule(:section) {
38
+ identifier.as(:section) >> values.maybe.as(:args) >> str('{') >> filler? >> exp.repeat.as(:expressions) >> str('}') >> filler?
39
+ }
40
+ end
41
+
42
+ class NginxTransform < Parslet::Transform
43
+ Group = Struct.new(:id, :args, :body)
44
+ Exp = Struct.new(:key, :vals)
45
+
46
+ def self.assemble_binary(seq)
47
+ b = ErlangBitstream.new
48
+ seq.each { |i| b.add(i) }
49
+ b.value
50
+ end
51
+
52
+ rule(section: { identifier: simple(:x) }, args: subtree(:y), expressions: subtree(:z)) { Group.new(x.to_s, y, z) }
53
+ rule(assignment: { identifier: simple(:x), args: subtree(:y) }) { Exp.new(x.to_s, y) }
54
+ rule(value: simple(:x)) { x.to_s }
55
+ end
56
+
57
+ class NginxConfig
58
+ def self.parse(content)
59
+ lex = NginxParser.new.parse(content)
60
+ tree = NginxTransform.new.apply(lex)
61
+ gtree = NginxTransform::Group.new(nil, '', tree)
62
+ read_nginx_group(gtree)
63
+ end
64
+
65
+ def self.read_nginx_group(t)
66
+ agg_conf = Hash.new([])
67
+ agg_conf['_'] = t.args unless t.args == ''
68
+
69
+ groups, conf = t.body.partition { |i| i.is_a? NginxTransform::Group }
70
+ conf.each { |x| agg_conf[x.key] += [x.vals.join(' ')] }
71
+ groups.each { |x| agg_conf[x.id] += [read_nginx_group(x)] }
72
+ agg_conf
73
+ end
74
+ end
data/lib/utils/spdx.rb ADDED
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+ class Spdx
5
+ def self.licenses
6
+ spdx_file = File.join(File.dirname(__FILE__), 'spdx.txt').freeze
7
+ File.read(spdx_file).split("\n")
8
+ end
9
+
10
+ def self.valid_license?(license)
11
+ licenses.include?(license)
12
+ end
13
+ end