inspec-core 4.21.3 → 4.23.4

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