inspec-core 4.21.3 → 4.23.4

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/inspec-core.gemspec +3 -5
  4. data/lib/bundles/inspec-supermarket/cli.rb +1 -1
  5. data/lib/inspec/base_cli.rb +5 -1
  6. data/lib/inspec/config.rb +19 -1
  7. data/lib/inspec/exceptions.rb +1 -0
  8. data/lib/inspec/input.rb +4 -3
  9. data/lib/inspec/input_registry.rb +9 -2
  10. data/lib/inspec/metadata.rb +6 -1
  11. data/lib/inspec/plugin/v2/plugin_types/reporter.rb +4 -25
  12. data/lib/inspec/profile.rb +30 -9
  13. data/lib/inspec/reporters.rb +0 -3
  14. data/lib/inspec/reporters/automate.rb +3 -3
  15. data/lib/inspec/reporters/base.rb +7 -23
  16. data/lib/inspec/reporters/cli.rb +1 -0
  17. data/lib/inspec/reporters/json.rb +9 -4
  18. data/lib/inspec/resources/apt.rb +2 -0
  19. data/lib/inspec/resources/bridge.rb +1 -1
  20. data/lib/inspec/resources/host.rb +1 -1
  21. data/lib/inspec/resources/mount.rb +1 -1
  22. data/lib/inspec/resources/mysql_session.rb +31 -8
  23. data/lib/inspec/resources/postgres.rb +1 -1
  24. data/lib/inspec/resources/postgres_session.rb +6 -4
  25. data/lib/inspec/resources/processes.rb +1 -1
  26. data/lib/inspec/resources/service.rb +2 -2
  27. data/lib/inspec/resources/users.rb +1 -1
  28. data/lib/inspec/resources/windows_firewall.rb +110 -0
  29. data/lib/inspec/resources/windows_firewall_rule.rb +137 -0
  30. data/lib/inspec/run_data.rb +1 -1
  31. data/lib/inspec/run_data/profile.rb +7 -6
  32. data/lib/inspec/runner.rb +8 -2
  33. data/lib/inspec/runner_rspec.rb +4 -1
  34. data/lib/inspec/schema.rb +2 -0
  35. data/lib/inspec/schema/exec_json.rb +4 -3
  36. data/lib/inspec/schema/primitives.rb +1 -1
  37. data/lib/inspec/utils/parser.rb +1 -1
  38. data/lib/inspec/utils/run_data_filters.rb +104 -0
  39. data/lib/inspec/version.rb +1 -1
  40. data/lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb +4 -4
  41. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +1 -1
  42. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +5 -2
  43. data/lib/plugins/inspec-reporter-junit/README.md +15 -0
  44. data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit.rb +12 -0
  45. data/lib/{inspec/reporters/junit.rb → plugins/inspec-reporter-junit/lib/inspec-reporter-junit/reporter.rb} +22 -26
  46. data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit/version.rb +5 -0
  47. metadata +19 -36
  48. data/README.md +0 -474
@@ -72,6 +72,7 @@ module Inspec::Reporters
72
72
  "Profile" => format_profile_name(profile),
73
73
  "Version" => profile[:version] || "(not specified)",
74
74
  }
75
+ header["Failure Message"] = profile[:status_message] if profile[:status] == "failed"
75
76
  header["Target"] = run_data[:platform][:target] unless run_data[:platform][:target].nil?
76
77
  header["Target ID"] = @config["target_id"] unless @config["target_id"].nil?
77
78
 
@@ -45,8 +45,8 @@ module Inspec::Reporters
45
45
  end
46
46
 
47
47
  def profiles
48
- run_data[:profiles].map { |p|
49
- {
48
+ run_data[:profiles].map do |p|
49
+ res = {
50
50
  name: p[:name],
51
51
  version: p[:version],
52
52
  sha256: p[:sha256],
@@ -64,10 +64,15 @@ module Inspec::Reporters
64
64
  groups: profile_groups(p),
65
65
  controls: profile_controls(p),
66
66
  status: p[:status],
67
- skip_message: p[:skip_message],
67
+ status_message: p[:status_message],
68
68
  waiver_data: p[:waiver_data],
69
69
  }.reject { |_k, v| v.nil? }
70
- }
70
+
71
+ # For backwards compatibility
72
+ res[:skip_message] = res[:status_message] if res[:status] == "skipped"
73
+
74
+ res
75
+ end
71
76
  end
72
77
 
73
78
  def profile_groups(profile)
@@ -90,12 +90,14 @@ module Inspec::Resources
90
90
  # deb "http://archive.ubuntu.com/ubuntu/" wily main restricted ...
91
91
  # deb http://archive.ubuntu.com/ubuntu/ wily main restricted ...
92
92
  # deb [trusted=yes] http://archive.ubuntu.com/ubuntu/ wily main restricted ...
93
+ # deb cdrom:[Ubuntu 15.10 _Wily Werewolf_ - Release amd64 (20151021)]/ wily main restricted ...
93
94
 
94
95
  words = line.split
95
96
  words.delete_at 1 if words[1] && words[1].start_with?("[")
96
97
  type, url, distro, *components = words
97
98
  url = url.delete('"') if url
98
99
 
100
+ next if words[1] && words[1].start_with?("cdrom:") # skip unsupported apt-cdrom repos
99
101
  next if components.empty?
100
102
  next unless URI::HTTP === URI.parse(url)
101
103
  next unless %w{deb deb-src}.include? type
@@ -27,7 +27,7 @@ module Inspec::Resources
27
27
  elsif inspec.os.windows?
28
28
  @bridge_provider = WindowsBridge.new(inspec)
29
29
  else
30
- return skip_resource "The `bridge` resource is not supported on your OS yet."
30
+ skip_resource "The `bridge` resource is not supported on your OS yet."
31
31
  end
32
32
  end
33
33
 
@@ -71,7 +71,7 @@ module Inspec::Resources
71
71
 
72
72
  missing_requirements = @host_provider.missing_requirements(protocol)
73
73
  unless missing_requirements.empty?
74
- return skip_resource "The following requirements are not met for this resource: " \
74
+ skip_resource "The following requirements are not met for this resource: " \
75
75
  "#{missing_requirements.join(", ")}"
76
76
  end
77
77
  end
@@ -61,7 +61,7 @@ module Inspec::Resources
61
61
  os = inspec.os
62
62
  if os.linux?
63
63
  LinuxMounts.new(inspec)
64
- elsif ["freebsd"].include?(os[:family])
64
+ elsif os.bsd?
65
65
  BsdMounts.new(inspec)
66
66
  end
67
67
  end
@@ -4,6 +4,27 @@ require "inspec/resources/command"
4
4
  require "shellwords"
5
5
 
6
6
  module Inspec::Resources
7
+ class Lines
8
+ attr_reader :output, :stdout, :stderr, :exit_status
9
+
10
+ def initialize(raw, desc, exit_status)
11
+ @output = raw
12
+ @desc = desc
13
+ @exit_status = exit_status
14
+ # backwards compatibility
15
+ @stdout = raw
16
+ @stderr = raw
17
+ end
18
+
19
+ def lines
20
+ output.split("\n")
21
+ end
22
+
23
+ def to_s
24
+ @desc
25
+ end
26
+ end
27
+
7
28
  class MysqlSession < Inspec.resource(1)
8
29
  name "mysql_session"
9
30
  supports platform: "unix"
@@ -12,7 +33,7 @@ module Inspec::Resources
12
33
  example <<~EXAMPLE
13
34
  sql = mysql_session('my_user','password','host')
14
35
  describe sql.query('show databases like \'test\';') do
15
- its('stdout') { should_not match(/test/) }
36
+ its('output') { should_not match(/test/) }
16
37
  end
17
38
  EXAMPLE
18
39
 
@@ -28,15 +49,17 @@ module Inspec::Resources
28
49
 
29
50
  def query(q, db = "")
30
51
  mysql_cmd = create_mysql_cmd(q, db)
31
- cmd = inspec.command(mysql_cmd)
52
+ cmd = if !@pass.nil?
53
+ inspec.command(mysql_cmd, redact_regex: /(mysql -u\w+ -p).+(\s-(h|S).*)/)
54
+ else
55
+ inspec.command(mysql_cmd)
56
+ end
32
57
  out = cmd.stdout + "\n" + cmd.stderr
33
- if out =~ /Can't connect to .* MySQL server/ || out.downcase =~ /^error /
34
- # skip this test if the server can't run the query
35
- warn("Can't connect to MySQL instance for SQL checks.")
58
+ if cmd.exit_status != 0 || out =~ /Can't connect to .* MySQL server/ || out.downcase =~ /^error:.*/
59
+ Lines.new(out, "MySQL query with errors: #{q}", cmd.exit_status)
60
+ else
61
+ Lines.new(cmd.stdout.strip, "MySQL query: #{q}", cmd.exit_status)
36
62
  end
37
-
38
- # return the raw command output
39
- cmd
40
63
  end
41
64
 
42
65
  def to_s
@@ -19,7 +19,7 @@ module Inspec::Resources
19
19
  @conf_path = File.join @conf_dir, "postgresql.conf"
20
20
  else
21
21
  @conf_path = nil
22
- return skip_resource "Seems like PostgreSQL is not installed on your system"
22
+ skip_resource "Seems like PostgreSQL is not installed on your system"
23
23
  end
24
24
  end
25
25
 
@@ -26,12 +26,13 @@ module Inspec::Resources
26
26
  supports platform: "windows"
27
27
  desc "Use the postgres_session InSpec audit resource to test SQL commands run against a PostgreSQL database."
28
28
  example <<~EXAMPLE
29
- sql = postgres_session('username', 'password', 'host')
29
+ sql = postgres_session('username', 'password', 'host', 'port')
30
30
  query('sql_query', ['database_name'])` contains the query and (optional) database to execute
31
31
 
32
32
  # default values:
33
33
  # username: 'postgres'
34
34
  # host: 'localhost'
35
+ # port: 5432
35
36
  # db: databse == db_user running the sql query
36
37
 
37
38
  describe sql.query('SELECT * FROM pg_shadow WHERE passwd IS NULL;') do
@@ -39,15 +40,16 @@ module Inspec::Resources
39
40
  end
40
41
  EXAMPLE
41
42
 
42
- def initialize(user, pass, host = nil)
43
+ def initialize(user, pass, host = nil, port = nil)
43
44
  @user = user || "postgres"
44
45
  @pass = pass
45
46
  @host = host || "localhost"
47
+ @port = port || 5432
46
48
  end
47
49
 
48
50
  def query(query, db = [])
49
51
  psql_cmd = create_psql_cmd(query, db)
50
- cmd = inspec.command(psql_cmd)
52
+ cmd = inspec.command(psql_cmd, redact_regex: /(PGPASSWORD=').+(' psql .*)/)
51
53
  out = cmd.stdout + "\n" + cmd.stderr
52
54
  if cmd.exit_status != 0 || out =~ /could not connect to .*/ || out.downcase =~ /^error:.*/
53
55
  Lines.new(out, "PostgreSQL query with errors: #{query}")
@@ -64,7 +66,7 @@ module Inspec::Resources
64
66
 
65
67
  def create_psql_cmd(query, db = [])
66
68
  dbs = db.map { |x| "-d #{x}" }.join(" ")
67
- "PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h #{@host} -A -t -c #{escaped_query(query)}"
69
+ "PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h #{@host} -p #{@port} -A -t -c #{escaped_query(query)}"
68
70
  end
69
71
  end
70
72
  end
@@ -138,7 +138,7 @@ module Inspec::Resources
138
138
  command: 8,
139
139
  }
140
140
  else
141
- command = "ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command"
141
+ command = "ps wwaxo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command"
142
142
  regex = /^(.+?)\s+(\d+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
143
143
  field_map = {
144
144
  label: 1,
@@ -141,7 +141,7 @@ module Inspec::Resources
141
141
  elsif version > 0
142
142
  SysV.new(inspec, service_ctl || "/usr/sbin/service")
143
143
  end
144
- when "redhat", "fedora", "centos", "oracle", "cloudlinux"
144
+ when "redhat", "fedora", "centos", "oracle", "cloudlinux", "scientific"
145
145
  version = os[:release].to_i
146
146
 
147
147
  systemd = ((platform != "fedora" && version >= 7) ||
@@ -154,7 +154,7 @@ module Inspec::Resources
154
154
  end
155
155
  when "wrlinux"
156
156
  SysV.new(inspec, service_ctl)
157
- when "mac_os_x"
157
+ when "mac_os_x", "darwin"
158
158
  LaunchCtl.new(inspec, service_ctl)
159
159
  when "freebsd"
160
160
  BSDInit.new(inspec, service_ctl)
@@ -20,7 +20,7 @@ module Inspec::Resources
20
20
  WindowsUser.new(inspec)
21
21
  elsif ["darwin"].include?(os[:family])
22
22
  DarwinUser.new(inspec)
23
- elsif ["freebsd"].include?(os[:family])
23
+ elsif ["bsd"].include?(os[:family])
24
24
  FreeBSDUser.new(inspec)
25
25
  elsif ["aix"].include?(os[:family])
26
26
  AixUser.new(inspec)
@@ -0,0 +1,110 @@
1
+ module Inspec::Resources
2
+ class WindowsFirewall < Inspec.resource(1)
3
+ name "windows_firewall"
4
+ supports platform: "windows"
5
+ desc "Check properties of the Windows Firewall for a specific profile."
6
+ example <<~EXAMPLE
7
+ describe windows_firewall("Public") do
8
+ it { should be_enabled }
9
+ its("default_inbound_action") { should_not cmp "NotConfigured" }
10
+ its("num_rules") { should be 19 }
11
+ end
12
+ EXAMPLE
13
+
14
+ def initialize(profile = "Public")
15
+ @profile = profile
16
+ @state = {}
17
+
18
+ load_profile_cmd = load_firewall_profile(profile)
19
+ cmd = inspec.powershell(load_profile_cmd)
20
+
21
+ @state = JSON.load(cmd.stdout) unless cmd.stdout.empty?
22
+ end
23
+
24
+ def to_s
25
+ "Windows Firewall (Profile #{@profile})"
26
+ end
27
+
28
+ def exist?
29
+ !@state.empty?
30
+ end
31
+
32
+ def enabled?
33
+ @state["enabled"]
34
+ end
35
+
36
+ def default_inbound_allowed?
37
+ @state["default_inbound_action"] == "Allow"
38
+ end
39
+
40
+ def default_outbound_allowed?
41
+ @state["default_outbound_action"] == "Allow"
42
+ end
43
+
44
+ # Access to return values from Powershell via `its("PROPERTY")` and `have_PROPERTY "VALUE"`
45
+ def method_missing(method_name, *arguments, &_block)
46
+ property = normalize_for_have_access(method_name)
47
+
48
+ if method_name.to_s.start_with? "has_"
49
+ expected_value = arguments.first
50
+ respond_to_have(property, expected_value)
51
+ else
52
+ access_property(property)
53
+ end
54
+ end
55
+
56
+ def respond_to_missing?(method_name, _include_private = false)
57
+ property = normalize_for_have_access(method_name)
58
+
59
+ @state.key? property
60
+ end
61
+
62
+ private
63
+
64
+ def normalize_for_have_access(property)
65
+ property.to_s
66
+ .delete_prefix("has_")
67
+ .delete_suffix("?")
68
+ end
69
+
70
+ def access_property(property)
71
+ @state[property]
72
+ end
73
+
74
+ def respond_to_have(property, value)
75
+ @state[property] == value
76
+ end
77
+
78
+ def load_firewall_profile(profile_name)
79
+ <<-EOH
80
+ Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M
81
+ $profile = Get-NetFirewallProfile -Name "#{profile_name}"
82
+ $count = @($profile | Get-NetFirewallRule).Count
83
+ ([PSCustomObject]@{
84
+ profile_name = $profile.Name
85
+ profile = $profile.Profile.ToString()
86
+ description = $profile.Description
87
+ enabled = [bool]::Parse($profile.Enabled.ToString())
88
+ default_inbound_action = $profile.DefaultInboundAction.ToString()
89
+ default_outbound_action = $profile.DefaultOutboundAction.ToString()
90
+
91
+ allow_inbound_rules = $profile.AllowInboundRules.ToString()
92
+ allow_local_firewall_rules = $profile.AllowLocalFirewallRules.ToString()
93
+ allow_local_ipsec_rules = $profile.AllowLocalIPsecRules.ToString()
94
+ allow_user_apps = $profile.AllowUserApps.ToString()
95
+ allow_user_ports = $profile.AllowUserPorts.ToString()
96
+ allow_unicast_response_to_multicast = $profile.AllowUnicastResponseToMulticast.ToString()
97
+
98
+ notify_on_listen = $profile.NotifyOnListen.ToString()
99
+ enable_stealth_mode_for_ipsec = $profile.EnableStealthModeForIPsec.ToString()
100
+ log_max_size_kilobytes = $profile.LogMaxSizeKilobytes
101
+ log_allowed = $profile.LogAllowed.ToString()
102
+ log_blocked = $profile.LogBlocked.ToString()
103
+ log_ignored = $profile.LogIgnored.ToString()
104
+
105
+ num_rules = $count
106
+ }) | ConvertTo-Json
107
+ EOH
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,137 @@
1
+ module Inspec::Resources
2
+ class WindowsFirewallRule < Inspec.resource(1)
3
+ name "windows_firewall_rule"
4
+ supports platform: "windows"
5
+ desc "Check properties of a Windows Firewall rule."
6
+ example <<~EXAMPLE
7
+ describe windows_firewall_rule("Name") do
8
+ it { should exist }
9
+ it { should be_enabled }
10
+
11
+ it { should be_outbound}
12
+ it { should be_tcp }
13
+ it { should have_remote_port 80 }
14
+ end
15
+ EXAMPLE
16
+
17
+ def initialize(name)
18
+ @name = name
19
+ @state = {}
20
+
21
+ query = load_firewall_state(name)
22
+ cmd = inspec.powershell(query)
23
+ @state = JSON.load(cmd.stdout) unless cmd.stdout.empty?
24
+ end
25
+
26
+ def to_s
27
+ "Windows Firewall Rule #{@name}"
28
+ end
29
+
30
+ def exist?
31
+ !@state.empty?
32
+ end
33
+
34
+ def enabled?
35
+ @state["enabled"]
36
+ end
37
+
38
+ def allowed?
39
+ @state["action"] == "Allow"
40
+ end
41
+
42
+ def inbound?
43
+ @state["direction"] == "Inbound"
44
+ end
45
+
46
+ def outbound?
47
+ ! inbound?
48
+ end
49
+
50
+ def tcp?
51
+ @state["protocol"] == "TCP"
52
+ end
53
+
54
+ def udp?
55
+ @state["protocol"] == "UDP"
56
+ end
57
+
58
+ def icmp?
59
+ @state["protocol"].start_with? "ICMP"
60
+ end
61
+
62
+ def icmpv4?
63
+ @state["protocol"] == "ICMPv4"
64
+ end
65
+
66
+ def icmpv6?
67
+ @state["protocol"] == "ICMPv6"
68
+ end
69
+
70
+ # Access to return values from Powershell via `its("PROPERTY")` and `have_PROPERTY? "VALUE"`
71
+ def method_missing(method_name, *arguments, &_block)
72
+ property = normalize_for_have_access(method_name)
73
+
74
+ if method_name.to_s.start_with? "has_"
75
+ expected_value = arguments.first
76
+ respond_to_have(property, expected_value)
77
+ else
78
+ access_property(property)
79
+ end
80
+ end
81
+
82
+ def respond_to_missing?(method_name, _include_private = false)
83
+ property = normalize_for_have_access(method_name)
84
+
85
+ @state.key? property
86
+ end
87
+
88
+ private
89
+
90
+ def normalize_for_have_access(property)
91
+ property.to_s
92
+ .delete_prefix("has_")
93
+ .delete_suffix("?")
94
+ end
95
+
96
+ def access_property(property)
97
+ @state[property]
98
+ end
99
+
100
+ def respond_to_have(property, value)
101
+ @state[property] == value
102
+ end
103
+
104
+ # Taken from Chef, but changed `firewall_action` to `action` for consistency
105
+ # @see https://github.com/chef/chef/blob/master/lib/chef/resource/windows_firewall_rule.rb
106
+ def load_firewall_state(rule_name)
107
+ <<-EOH
108
+ Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M
109
+ $rule = Get-NetFirewallRule -Name "#{rule_name}"
110
+ $addressFilter = $rule | Get-NetFirewallAddressFilter
111
+ $portFilter = $rule | Get-NetFirewallPortFilter
112
+ $applicationFilter = $rule | Get-NetFirewallApplicationFilter
113
+ $serviceFilter = $rule | Get-NetFirewallServiceFilter
114
+ $interfaceTypeFilter = $rule | Get-NetFirewallInterfaceTypeFilter
115
+ ([PSCustomObject]@{
116
+ rule_name = $rule.Name
117
+ description = $rule.Description
118
+ displayname = $rule.DisplayName
119
+ group = $rule.Group
120
+ local_address = $addressFilter.LocalAddress
121
+ local_port = $portFilter.LocalPort
122
+ remote_address = $addressFilter.RemoteAddress
123
+ remote_port = $portFilter.RemotePort
124
+ direction = $rule.Direction.ToString()
125
+ protocol = $portFilter.Protocol
126
+ icmp_type = $portFilter.IcmpType
127
+ action = $rule.Action.ToString()
128
+ profile = $rule.Profile.ToString()
129
+ program = $applicationFilter.Program
130
+ service = $serviceFilter.Service
131
+ interface_type = $interfaceTypeFilter.InterfaceType.ToString()
132
+ enabled = [bool]::Parse($rule.Enabled.ToString())
133
+ }) | ConvertTo-Json
134
+ EOH
135
+ end
136
+ end
137
+ end