inspec 1.26.0 → 1.27.0

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