inspec-core 4.20.10 → 4.21.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/inspec/resources/interface.rb +55 -0
- data/lib/inspec/resources/interfaces.rb +119 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-reporter-html2/README.md +53 -0
- data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2.rb +18 -0
- data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/reporter.rb +24 -0
- data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/version.rb +8 -0
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +46 -0
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +77 -0
- data/lib/plugins/inspec-reporter-html2/templates/default.css +107 -0
- data/lib/plugins/inspec-reporter-html2/templates/default.js +79 -0
- data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +20 -0
- data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +15 -0
- data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +8 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7be94754504cd2aeda9badd0701a9ac28691e0c62f76741db0c3b1b75988b2b
|
4
|
+
data.tar.gz: 314ce4bedf429cb87185f4903bcb5e47c93587fc1c125f9dcff112574f5dde72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa7f2ac74a61e23f82236d175b5adcc4236eb6b48316d7eaca9cfa85fe9f3917a22eb87fc56723ff56635348897e21f635816071fcf1c13a2f808df212388693
|
7
|
+
data.tar.gz: 17245f5ba3ca9aad13bba4870e52bf48c2e2cf2219b03ee569d0a1aa2e699bd2b6de447d30a2d23c44e01c72ded87c9dab596e564eb9206f7826ae72f6b4d366
|
data/Gemfile
CHANGED
@@ -47,10 +47,18 @@ module Inspec::Resources
|
|
47
47
|
ipv6_addresses && !ipv6_addresses.empty?
|
48
48
|
end
|
49
49
|
|
50
|
+
def ipv4_address
|
51
|
+
ipv4_addresses.first
|
52
|
+
end
|
53
|
+
|
50
54
|
def ipv4_addresses
|
51
55
|
ipv4_cidrs.map { |i| i.split("/")[0] }
|
52
56
|
end
|
53
57
|
|
58
|
+
def ipv6_address
|
59
|
+
ipv6_addresses.first
|
60
|
+
end
|
61
|
+
|
54
62
|
def ipv6_addresses
|
55
63
|
ipv6_cidrs.map { |i| i.split("/")[0] }
|
56
64
|
end
|
@@ -85,6 +93,7 @@ module Inspec::Resources
|
|
85
93
|
@cache ||= begin
|
86
94
|
provider = LinuxInterface.new(inspec) if inspec.os.linux?
|
87
95
|
provider = WindowsInterface.new(inspec) if inspec.os.windows?
|
96
|
+
provider = BsdInterface.new(inspec) if inspec.os.bsd? # includes macOS
|
88
97
|
Hash(provider && provider.interface_info(@iface))
|
89
98
|
end
|
90
99
|
end
|
@@ -98,6 +107,52 @@ module Inspec::Resources
|
|
98
107
|
end
|
99
108
|
end
|
100
109
|
|
110
|
+
class BsdInterface < InterfaceInfo
|
111
|
+
def interface_info(iface)
|
112
|
+
cmd = inspec.command("ifconfig #{iface}")
|
113
|
+
return nil if cmd.exit_status.to_i != 0
|
114
|
+
|
115
|
+
lines = cmd.stdout.split("\n")
|
116
|
+
iface_info = {
|
117
|
+
name: iface,
|
118
|
+
ipv4_addresses: [], # Actually CIDRs
|
119
|
+
ipv6_addresses: [], # are expected to go here
|
120
|
+
}
|
121
|
+
|
122
|
+
iface_info[:up] = lines[0].include?("UP")
|
123
|
+
lines.each do |line|
|
124
|
+
# IPv4 case
|
125
|
+
m = line.match(/^\s+inet\s+((?:\d{1,3}\.){3}\d{1,3})\s+netmask\s+(0x[a-f0-9]{8})/)
|
126
|
+
if m
|
127
|
+
ip = m[1]
|
128
|
+
hex_mask = m[2]
|
129
|
+
cidr = hex_mask.to_i(16).to_s(2).count("1")
|
130
|
+
iface_info[:ipv4_addresses] << "#{ip}/#{cidr}"
|
131
|
+
next
|
132
|
+
end
|
133
|
+
|
134
|
+
# IPv6 case
|
135
|
+
m = line.match(/^\s+inet6\s+([a-f0-9:]+)%#{iface}\s+prefixlen\s+(\d+)/)
|
136
|
+
if m
|
137
|
+
ip = m[1]
|
138
|
+
cidr = m[2]
|
139
|
+
iface_info[:ipv6_addresses] << "#{ip}/#{cidr}"
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
# Speed detect, crummy - can't detect wifi, finds any number in the string
|
144
|
+
# Ethernet autoselect (1000baseT <full-duplex>)
|
145
|
+
m = line.match(/^\s+media:\D+(\d+)/)
|
146
|
+
if m
|
147
|
+
iface_info[:speed] = m[1].to_i
|
148
|
+
next
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
iface_info
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
101
156
|
class LinuxInterface < InterfaceInfo
|
102
157
|
def interface_info(iface)
|
103
158
|
# will return "[mtu]\n1500\n[type]\n1"
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "inspec/utils/filter"
|
2
|
+
require "inspec/resources/command"
|
3
|
+
|
4
|
+
module Inspec::Resources
|
5
|
+
class Interfaces < Inspec.resource(1)
|
6
|
+
name "interfaces"
|
7
|
+
supports platform: "unix"
|
8
|
+
supports platform: "windows"
|
9
|
+
desc "Use the interfaces InSpec audit resource to test properties for multiple network interfaces installed on the system"
|
10
|
+
example <<~EXAMPLE
|
11
|
+
describe interfaces do
|
12
|
+
its('names') { should include 'eth0' }
|
13
|
+
end
|
14
|
+
EXAMPLE
|
15
|
+
|
16
|
+
attr_reader :iface_data
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"Interfaces"
|
20
|
+
end
|
21
|
+
|
22
|
+
filter = FilterTable.create
|
23
|
+
filter.register_column(:names, field: "name")
|
24
|
+
.install_filter_methods_on_resource(self, :scan_interfaces)
|
25
|
+
|
26
|
+
def ipv4_address
|
27
|
+
require "ipaddr"
|
28
|
+
|
29
|
+
# Loop over interface names
|
30
|
+
# Select those that are up and have an ipv4 address
|
31
|
+
interfaces = names.map { |n| inspec.interface(n) }.select do |i|
|
32
|
+
i.ipv4_address? && i.up?
|
33
|
+
end
|
34
|
+
|
35
|
+
addrs = interfaces.map(&:ipv4_addresses).flatten.map { |a| IPAddr.new(a) }
|
36
|
+
|
37
|
+
# Look for progressively "better" IP addresses
|
38
|
+
[
|
39
|
+
# Loopback and private IP ranges
|
40
|
+
IPAddr.new("127.0.0.0/8"),
|
41
|
+
IPAddr.new("192.168.0.0/16"),
|
42
|
+
IPAddr.new("172.16.0.0/12"),
|
43
|
+
IPAddr.new("10.0.0.0/8"),
|
44
|
+
].each do |private_range|
|
45
|
+
filtered_addrs = addrs.reject { |a| private_range.include?(a) }
|
46
|
+
if filtered_addrs.empty?
|
47
|
+
# Everything we had was a private or loopback IP. Return the "best" thing we were left with.
|
48
|
+
return addrs.first.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
addrs = filtered_addrs
|
52
|
+
end
|
53
|
+
addrs.first.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def scan_interfaces
|
59
|
+
@iface_data ||= begin
|
60
|
+
provider = LinuxInterfaceLister.new(inspec) if inspec.os.linux?
|
61
|
+
provider = WindowsInterfaceLister.new(inspec) if inspec.os.windows?
|
62
|
+
provider = BsdInterfaceLister.new(inspec) if inspec.os.bsd? # includes macOS
|
63
|
+
Array(provider && provider.scan_interfaces)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class InterfaceLister
|
68
|
+
attr_reader :inspec
|
69
|
+
def initialize(inspec)
|
70
|
+
@inspec = inspec
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class BsdInterfaceLister < InterfaceLister
|
75
|
+
def scan_interfaces
|
76
|
+
iface_data = []
|
77
|
+
cmd = inspec.command("ifconfig -a")
|
78
|
+
cmd.stdout.split("\n").each do |line|
|
79
|
+
# lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
|
80
|
+
m = line.match(/^(\S+):/)
|
81
|
+
if m
|
82
|
+
iface_data << { "name" => m[1] }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
iface_data
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class LinuxInterfaceLister < InterfaceLister
|
90
|
+
def scan_interfaces
|
91
|
+
iface_data = []
|
92
|
+
cmd = inspec.command("ls /sys/class/net")
|
93
|
+
cmd.stdout.split("\n").each do |iface|
|
94
|
+
iface_data << { "name" => iface }
|
95
|
+
end
|
96
|
+
iface_data
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class WindowsInterfaceLister < InterfaceLister
|
101
|
+
def scan_interfaces
|
102
|
+
iface_data = []
|
103
|
+
cmd = inspec.command("Get-NetAdapter | Select-Object -Property Name | ConvertTo-Json")
|
104
|
+
begin
|
105
|
+
adapter_info = JSON.parse(cmd.stdout)
|
106
|
+
# May be a Hash if only one, or Array if multiple - normalize to Array
|
107
|
+
adapter_info = [ adapter_info ] if adapter_info.is_a? Hash
|
108
|
+
rescue JSON::ParserError => _e
|
109
|
+
return nil
|
110
|
+
end
|
111
|
+
adapter_info.each do |info|
|
112
|
+
iface_data << { "name" => info["Name"] }
|
113
|
+
end
|
114
|
+
iface_data
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
data/lib/inspec/version.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
# inspec-reporter-html2 Plugin
|
2
|
+
|
3
|
+
An "improved" HTML output reporter specifically for Chef InSpec. Unlike the default `html` reporter, which is RSpec-based, this reporter knows about Chef InSpec structures like Controls and Profiles, and includes full metadata such as control tags, etc.
|
4
|
+
|
5
|
+
## To Install This Plugin
|
6
|
+
|
7
|
+
This plugin ships with Chef InSpec and requires no installation.
|
8
|
+
|
9
|
+
It should appear when you run:
|
10
|
+
|
11
|
+
```
|
12
|
+
you@machine $ inspec plugin list
|
13
|
+
```
|
14
|
+
|
15
|
+
## How to use this plugin
|
16
|
+
|
17
|
+
To generate an HTML report using this plugin and save the output to a file named `report.html`, run:
|
18
|
+
|
19
|
+
```
|
20
|
+
you@machine $ inspec exec some_profile --reporter html2:report.html
|
21
|
+
```
|
22
|
+
|
23
|
+
Note the `2` in the reporter name. If you omit it and run `--reporter html` instead, you will run the legacy RSpec HTML reporter.
|
24
|
+
|
25
|
+
## Configuring the Plugin
|
26
|
+
|
27
|
+
The `html2` reporter requires no configuration to function. However, two options--`alternate_css_file` and `alternate_js_file`--are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. For details, see [our configuration file documentation](https://www.inspec.io/docs/reference/config/).
|
28
|
+
|
29
|
+
For example:
|
30
|
+
|
31
|
+
```json
|
32
|
+
{
|
33
|
+
"version": "1.2",
|
34
|
+
"plugins": {
|
35
|
+
"inspec-reporter-html2": {
|
36
|
+
"alternate_js_file":"/var/www/js/my-javascript.js",
|
37
|
+
"alternate_css_file":"/var/www/css/my-style.css"
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
```
|
42
|
+
|
43
|
+
### alternate\_css\_file
|
44
|
+
|
45
|
+
Specifies the full path to the location of a CSS file that will be read and inlined into the HTML report. The default CSS will not be included.
|
46
|
+
|
47
|
+
### alternate\_js\_file
|
48
|
+
|
49
|
+
Specifies the full path to the location of a JavaScript file that will be read and inlined into the HTML report. The default JavaScript will not be included. The JavaScript file should implement at least a `pageLoaded()` function, which will be called by the `onload` event of the HTML `body` element.
|
50
|
+
|
51
|
+
## Developing This Plugin
|
52
|
+
|
53
|
+
This plugin is part of the Chef InSpec source code. While it has its own tests, the general contribution policy is dictated by the Chef InSpec project at https://github.com/inspec/inspec/blob/master/CONTRIBUTING.md
|
@@ -0,0 +1,18 @@
|
|
1
|
+
libdir = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
3
|
+
|
4
|
+
require "inspec-reporter-html2/version"
|
5
|
+
module InspecPlugins
|
6
|
+
module Html2Reporter
|
7
|
+
class Plugin < ::Inspec.plugin(2)
|
8
|
+
# Internal machine name of the plugin. InSpec will use this in errors, etc.
|
9
|
+
plugin_name :'inspec-reporter-html2'
|
10
|
+
|
11
|
+
# Define a new Reporter.
|
12
|
+
reporter :html2 do
|
13
|
+
require "inspec-reporter-html2/reporter"
|
14
|
+
InspecPlugins::Html2Reporter::Reporter
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "inspec/config"
|
3
|
+
|
4
|
+
module InspecPlugins::Html2Reporter
|
5
|
+
class Reporter < Inspec.plugin(2, :reporter)
|
6
|
+
def render
|
7
|
+
template_path = File.expand_path(__FILE__ + "../../../../templates")
|
8
|
+
|
9
|
+
# Read config data from the user's config file. Supports two settings, both of which are absolute filesystem paths:
|
10
|
+
# alternate_css_file - contents will be used instead of default CSS
|
11
|
+
# alternate_js_file - contents will be used instead of default JavaScript
|
12
|
+
cfg = Inspec::Config.cached.fetch_plugin_config("inspec-reporter-html2")
|
13
|
+
js_path = cfg[:alternate_js_file] || (template_path + "/default.js")
|
14
|
+
css_path = cfg[:alternate_css_file] || (template_path + "/default.css")
|
15
|
+
|
16
|
+
template = ERB.new(File.read(template_path + "/body.html.erb"))
|
17
|
+
output(template.result(binding))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.run_data_schema_constraints
|
21
|
+
"~> 0.0"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<!-- saved from url=(0014)about:internet -->
|
3
|
+
<!-- prior comment allows JS to execute on IE when saved as a local file, "MOTW" -->
|
4
|
+
<html lang="en">
|
5
|
+
<head>
|
6
|
+
<title><%= Inspec::Dist::PRODUCT_NAME %> Results</title>
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
8
|
+
<style type="text/css">
|
9
|
+
/* Must inline all CSS files, this is a single-file output that may be airgapped */
|
10
|
+
<%= ERB.new(File.read(css_path), nil, nil, "_css").result(binding) %>
|
11
|
+
</style>
|
12
|
+
<script type="text/javascript">
|
13
|
+
// <![CDATA[
|
14
|
+
/* Must inline all JavaScript files, this is a single-file output that may be airgapped */
|
15
|
+
<%= ERB.new(File.read(js_path), nil, nil, "_js").result(binding) %>
|
16
|
+
// ]]>
|
17
|
+
</script>
|
18
|
+
</head>
|
19
|
+
<body onload="pageLoaded()">
|
20
|
+
<%= ERB.new(File.read(template_path + "/selector.html.erb"), nil, nil, "_select").result(binding) %>
|
21
|
+
<div class="inspec-report">
|
22
|
+
<h1><%= Inspec::Dist::PRODUCT_NAME %> Report</h1>
|
23
|
+
<% run_data.profiles.each do |profile| %>
|
24
|
+
<%= ERB.new(File.read(template_path + "/profile.html.erb"), nil, nil, "_prof").result(binding) %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<div class="inspec-summary">
|
28
|
+
<table id="platform" class="info">
|
29
|
+
<tr><th colspan=2><h4 id="platform-label">Platform Information</h4></th></tr>
|
30
|
+
<tr class= "name"><th>Name:</th><td><%= run_data.platform.name %></td></tr>
|
31
|
+
<tr class= "release"><th>Release:</th><td><%= run_data.platform.release %></td></tr>
|
32
|
+
<tr class= "target"><th>Target:</th><td><%= run_data.platform.target %></td></tr>
|
33
|
+
</table>
|
34
|
+
<table id="statistics" class="info">
|
35
|
+
<tr><th colspan="2"><h4 id="statistics-label">Control Statistics</h4></th></tr>
|
36
|
+
<tr class= "passed"><th>Passed:</th><td><%= run_data.statistics.controls.passed.total %></td></tr>
|
37
|
+
<tr class= "skipped"><th>Skipped:</th><td><%= run_data.statistics.controls.skipped.total %></td></tr>
|
38
|
+
<tr class= "failed"><th>Failed:</th><td><%= run_data.statistics.controls.failed.total %></td></tr>
|
39
|
+
<tr class= "duration"><th>Duration:</th><td><%= run_data.statistics.duration %> seconds</td></tr>
|
40
|
+
<tr class= "date"><th>Time Finished:</th><td><%= Time.now %></td></tr>
|
41
|
+
</table>
|
42
|
+
<span id="inspec-version"><%= Inspec::Dist::PRODUCT_NAME %> version <%= run_data.version %></span>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</body>
|
46
|
+
</html>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
<% slugged_id = control.id.tr(" ", "_") %>
|
2
|
+
<%
|
3
|
+
# Determine status of control
|
4
|
+
status = "passed"
|
5
|
+
if control.results.any? { |r| r.status == "failed" }
|
6
|
+
status = "failed"
|
7
|
+
elsif control.results.any? { |r| r.status == "skipped" }
|
8
|
+
status = "skipped"
|
9
|
+
end
|
10
|
+
%>
|
11
|
+
|
12
|
+
<div class="control control-status-<%= status %>" id="control-<%= slugged_id %>">
|
13
|
+
|
14
|
+
<%
|
15
|
+
# Determine range of impact
|
16
|
+
i = control.impact || 0.0
|
17
|
+
impact_level = "none"
|
18
|
+
if i < 0.3
|
19
|
+
impact_level = "low"
|
20
|
+
elsif i < 0.7
|
21
|
+
impact_level = "medium"
|
22
|
+
else
|
23
|
+
impact_level = "high"
|
24
|
+
end
|
25
|
+
%>
|
26
|
+
|
27
|
+
<h3 class="control-title">Control <code><%= control.id %></code></h3>
|
28
|
+
<table class="control-metadata info" id="control-metadata-<%= slugged_id %>">
|
29
|
+
<tr class="status status-<%= status %>"><th>Status:</th><td><div><%= status.capitalize %></div></td></tr>
|
30
|
+
<% if control.title %><tr class="title"><th>Title:</th><td><%= control.title %></td></tr> <% end %>
|
31
|
+
<% if control.desc %><tr class="desc"><th>Description:</th><td><%= control.desc %></td></tr> <% end %>
|
32
|
+
<% if control.impact %><tr class="impact impact-<%= impact_level %>"><th>Impact:</th><td><%= control.impact %></td></tr> <% end %>
|
33
|
+
<% unless control.tags.empty? %>
|
34
|
+
<tr class="tags">
|
35
|
+
<th>Tags:</th>
|
36
|
+
<td>
|
37
|
+
<table class="tags">
|
38
|
+
<% control.tags.each do |tag_name, tag_text| %>
|
39
|
+
<tr><td><%= tag_name %></td><td><%= tag_text %></td></tr>
|
40
|
+
<% end %>
|
41
|
+
</table>
|
42
|
+
</td>
|
43
|
+
</tr>
|
44
|
+
<% end %>
|
45
|
+
<% unless control.refs.empty? %>
|
46
|
+
<tr class="refs">
|
47
|
+
<th>References:</th>
|
48
|
+
<td>
|
49
|
+
<ul>
|
50
|
+
<% control.refs.each do |r| %>
|
51
|
+
<li><a href="<%= r.url %>"><%= r.ref %></a></li>
|
52
|
+
<% end %>
|
53
|
+
</ul>
|
54
|
+
</td>
|
55
|
+
</tr>
|
56
|
+
<% end %>
|
57
|
+
<tr class="code">
|
58
|
+
<th>Source Code:</th>
|
59
|
+
<td>
|
60
|
+
<input type="button" class="show-source-code" id="show-code-<%= slugged_id %>" value="Show Source"/>
|
61
|
+
<input type="button" class="hide-source-code hidden" id="hide-code-<%= slugged_id %>" value="Hide Source"/>
|
62
|
+
<pre class="source-code hidden" id="source-code-<%= slugged_id %>">
|
63
|
+
<code>
|
64
|
+
<%= control.code %>
|
65
|
+
</code>
|
66
|
+
</pre>
|
67
|
+
</td>
|
68
|
+
</tr>
|
69
|
+
<!-- TODO waiver data -->
|
70
|
+
|
71
|
+
</table>
|
72
|
+
|
73
|
+
<% control.results.each do |result| %>
|
74
|
+
<%= ERB.new(File.read(template_path + "/result.html.erb"), nil, nil, "_rslt").result(binding) %>
|
75
|
+
<% end %>
|
76
|
+
|
77
|
+
</div>
|
@@ -0,0 +1,107 @@
|
|
1
|
+
body {
|
2
|
+
margin: 20px 10% 20px 10%;
|
3
|
+
padding: 0;
|
4
|
+
background: #fff;
|
5
|
+
}
|
6
|
+
h1, h2, h3, h4, h5 {
|
7
|
+
font-family: "Lucida Grande", Helvetica, sans-serif;
|
8
|
+
}
|
9
|
+
h1, h2 {
|
10
|
+
padding: 10px;
|
11
|
+
text-align: center
|
12
|
+
}
|
13
|
+
table.info th, table.info th {
|
14
|
+
padding: 2px;
|
15
|
+
}
|
16
|
+
table.info th {
|
17
|
+
text-align: right;
|
18
|
+
}
|
19
|
+
.hidden {
|
20
|
+
display: none;
|
21
|
+
}
|
22
|
+
pre code {
|
23
|
+
background-color: #eee;
|
24
|
+
border: 1px solid #999;
|
25
|
+
display: block;
|
26
|
+
padding: 20px;
|
27
|
+
}
|
28
|
+
|
29
|
+
.profile, .control, .profile-metadata {
|
30
|
+
border: 1px solid #ccc;
|
31
|
+
padding: 10px;
|
32
|
+
margin: 5px auto;
|
33
|
+
}
|
34
|
+
|
35
|
+
.resource-title {
|
36
|
+
margin-left: 2.5%;
|
37
|
+
}
|
38
|
+
|
39
|
+
.control-title code,
|
40
|
+
.resource-title code {
|
41
|
+
font-size: larger;
|
42
|
+
}
|
43
|
+
.control-metadata .status div,
|
44
|
+
.result-metadata .status div {
|
45
|
+
color: white;
|
46
|
+
font-weight: bold;
|
47
|
+
width: fit-content;
|
48
|
+
padding: 0px 2px;
|
49
|
+
}
|
50
|
+
|
51
|
+
.control-metadata .status-passed div,
|
52
|
+
.result-metadata .status-passed div {
|
53
|
+
background-color: darkgreen;
|
54
|
+
}
|
55
|
+
.control-metadata .status-failed div,
|
56
|
+
.result-metadata .status-failed div {
|
57
|
+
background-color: red;
|
58
|
+
}
|
59
|
+
.control-metadata .status-skipped div,
|
60
|
+
.result-metadata .status-skipped div {
|
61
|
+
background-color: grey;
|
62
|
+
}
|
63
|
+
.result-metadata,
|
64
|
+
.control-metadata {
|
65
|
+
margin: 0 0 0 5%;
|
66
|
+
}
|
67
|
+
|
68
|
+
.selector-panel {
|
69
|
+
position: fixed;
|
70
|
+
z-index: 100;
|
71
|
+
background-color: #ccc;
|
72
|
+
padding: 10px;
|
73
|
+
top: 0;
|
74
|
+
margin-left: -10%;
|
75
|
+
border-bottom-right-radius: 10px;
|
76
|
+
}
|
77
|
+
|
78
|
+
@media print {
|
79
|
+
.selector-panel {
|
80
|
+
visibility: hidden;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
.inspec-summary {
|
85
|
+
border: 1px solid #ccc;
|
86
|
+
padding: 10px;
|
87
|
+
margin: 5px auto;
|
88
|
+
width: fit-content
|
89
|
+
}
|
90
|
+
|
91
|
+
.inspec-summary h4 {
|
92
|
+
margin-bottom: 0px;
|
93
|
+
}
|
94
|
+
|
95
|
+
.inspec-summary #platform, .inspec-summary #statistics {
|
96
|
+
display: inline;
|
97
|
+
}
|
98
|
+
|
99
|
+
#statistics .date td {
|
100
|
+
width: 100px
|
101
|
+
}
|
102
|
+
|
103
|
+
#inspec-version {
|
104
|
+
display: block;
|
105
|
+
text-align: center;
|
106
|
+
font-style: italic;
|
107
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
/* CSS primitives */
|
3
|
+
function addCssClass(id, cls) {
|
4
|
+
document.getElementById(id).className += (" " + cls);
|
5
|
+
}
|
6
|
+
|
7
|
+
function removeCssClass(id, cls) {
|
8
|
+
var el = document.getElementById(id);
|
9
|
+
var classes = el.className.replace(cls,'');
|
10
|
+
el.className = classes;
|
11
|
+
}
|
12
|
+
|
13
|
+
function handleShowSource(evt) {
|
14
|
+
var control_id = evt.srcElement.id.replace("show-code-", "")
|
15
|
+
addCssClass(evt.srcElement.id, "hidden")
|
16
|
+
removeCssClass("hide-code-" + control_id, "hidden")
|
17
|
+
removeCssClass("source-code-" + control_id, "hidden")
|
18
|
+
}
|
19
|
+
|
20
|
+
function handleHideSource(evt) {
|
21
|
+
var control_id = evt.srcElement.id.replace("hide-code-", "")
|
22
|
+
addCssClass(evt.srcElement.id, "hidden")
|
23
|
+
addCssClass("source-code-" + control_id, "hidden")
|
24
|
+
removeCssClass("show-code-" + control_id, "hidden")
|
25
|
+
}
|
26
|
+
|
27
|
+
function handleSelectorChange(evt) {
|
28
|
+
var should_show = evt.srcElement.checked
|
29
|
+
var which_group = evt.srcElement.id.replace("-checkbox","")
|
30
|
+
var controls = document.getElementsByClassName("control-status-" + which_group)
|
31
|
+
var i;
|
32
|
+
if (should_show) {
|
33
|
+
for (i = 0; i < controls.length; i++) {
|
34
|
+
removeCssClass(controls[i].id, "hidden")
|
35
|
+
}
|
36
|
+
} else {
|
37
|
+
for (i = 0; i < controls.length; i++) {
|
38
|
+
addCssClass(controls[i].id, "hidden")
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
function handleChildProfileChange(evt) {
|
44
|
+
var should_show = evt.srcElement.checked
|
45
|
+
var child_profiles = document.getElementsByClassName("child-profile")
|
46
|
+
var i;
|
47
|
+
if (should_show) {
|
48
|
+
for (i = 0; i < child_profiles.length; i++) {
|
49
|
+
removeCssClass(child_profiles[i].id, "hidden")
|
50
|
+
}
|
51
|
+
} else {
|
52
|
+
for (i = 0; i < child_profiles.length; i++) {
|
53
|
+
addCssClass(child_profiles[i].id, "hidden")
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
/* Main entry point */
|
59
|
+
function pageLoaded() {
|
60
|
+
var i;
|
61
|
+
|
62
|
+
// wire up show source links
|
63
|
+
var show_links = document.getElementsByClassName("show-source-code");
|
64
|
+
for (i = 0; i < show_links.length; i++) {
|
65
|
+
show_links[i].onclick = handleShowSource;
|
66
|
+
}
|
67
|
+
// wire up hide source links
|
68
|
+
var hide_links = document.getElementsByClassName("hide-source-code");
|
69
|
+
for (i = 0; i < hide_links.length; i++) {
|
70
|
+
hide_links[i].onclick = handleHideSource;
|
71
|
+
}
|
72
|
+
// wire up selector checkboxes
|
73
|
+
var selectors = document.getElementsByClassName("selector-checkbox");
|
74
|
+
for (i = 0; i < selectors.length; i++) {
|
75
|
+
selectors[i].onchange = handleSelectorChange;
|
76
|
+
}
|
77
|
+
// wire up child profile checkbox
|
78
|
+
document.getElementById("child-profile-checkbox").onchange = handleChildProfileChange;
|
79
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<div class="profile <%= profile.parent_profile ? "child-profile hidden" : "" %>" id="profile-<%= profile.name %>">
|
2
|
+
<% display_name = profile.title || profile.name %>
|
3
|
+
<h2 class="profile_title">Profile <%= display_name %> (<%= profile.name %>)</h2>
|
4
|
+
|
5
|
+
<table class="profile-metadata info" id="profile-metadata-<%= profile.name %>">
|
6
|
+
<tr class="profile-version"><th>Version:</th><td><%= profile.version %></td></tr>
|
7
|
+
<% if profile.summary %>
|
8
|
+
<tr class="profile-summary"><th>Summary:</th><td><%= profile.summary %></td></tr>
|
9
|
+
<% end %>
|
10
|
+
<% if profile.skip_message %>
|
11
|
+
<tr class="profile-skip-message"><th>Skip Message:</th><td><%= profile.skip_message %></td></tr>
|
12
|
+
<% end %>
|
13
|
+
</table>
|
14
|
+
|
15
|
+
<% if profile.status == "loaded" %>
|
16
|
+
<% profile.controls.each do |control| %>
|
17
|
+
<%= ERB.new(File.read(template_path + "/control.html.erb"), nil, nil, "_ctl").result(binding) %>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% slugged_id = result.resource_title.to_s.gsub(/\W/, "_") %>
|
2
|
+
<div class="result" id="result-<%= slugged_id %>">
|
3
|
+
<h4 class="resource-title">Resource <code><%= result.resource_title.to_s %></code></h4>
|
4
|
+
<table class="result-metadata info">
|
5
|
+
<tr class="expectation_message"><th>Test:</th><td><code><%= result.expectation_message %></code></td></tr>
|
6
|
+
<tr class="status status-<%= result.status %>"><th>Status:</th><td><div><%= result.status.capitalize %></div></td></tr>
|
7
|
+
<% if result.status == "failed" %>
|
8
|
+
<tr class="fail_message"><th>Failure Message:</th><td><code><%= result.message %></code></td></tr>
|
9
|
+
<% end %>
|
10
|
+
<% if result.status == "skipped" %>
|
11
|
+
<tr class="skip_message"><th>Skip Message:</th><td><%= result.skip_message %></td></tr>
|
12
|
+
<% end %>
|
13
|
+
<tr class="duration"><th>Duration:</th><td><%= result.run_time %> seconds</td></tr>
|
14
|
+
</table>
|
15
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<div class="selector-panel">
|
2
|
+
<p id="selector-instructions">Display controls that are:</p>
|
3
|
+
<input class="selector-checkbox" id="passed-checkbox" type="checkbox" checked="checked"/><label for="passed-checkbox">Passed</label>
|
4
|
+
<input class="selector-checkbox" id="skipped-checkbox" type="checkbox" checked="checked"/><label for="skipped-checkbox">Skipped</label>
|
5
|
+
<input class="selector-checkbox" id="failed-checkbox" type="checkbox" checked="checked"/><label for="failed-checkbox">Failed</label>
|
6
|
+
<p id="selector-instructions">Display profiles that are:</p>
|
7
|
+
<input class="profile-selector-checkbox" id="child-profile-checkbox" type="checkbox" /><label for="child-profile-checkbox">Dependent Profiles</label>
|
8
|
+
</div>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.21.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chef InSpec Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef-telemetry
|
@@ -536,6 +536,7 @@ files:
|
|
536
536
|
- lib/inspec/resources/inetd_conf.rb
|
537
537
|
- lib/inspec/resources/ini.rb
|
538
538
|
- lib/inspec/resources/interface.rb
|
539
|
+
- lib/inspec/resources/interfaces.rb
|
539
540
|
- lib/inspec/resources/ip6tables.rb
|
540
541
|
- lib/inspec/resources/iptables.rb
|
541
542
|
- lib/inspec/resources/json.rb
|
@@ -724,6 +725,17 @@ files:
|
|
724
725
|
- lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli.rb
|
725
726
|
- lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb
|
726
727
|
- lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/plugin.rb
|
728
|
+
- lib/plugins/inspec-reporter-html2/README.md
|
729
|
+
- lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2.rb
|
730
|
+
- lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/reporter.rb
|
731
|
+
- lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/version.rb
|
732
|
+
- lib/plugins/inspec-reporter-html2/templates/body.html.erb
|
733
|
+
- lib/plugins/inspec-reporter-html2/templates/control.html.erb
|
734
|
+
- lib/plugins/inspec-reporter-html2/templates/default.css
|
735
|
+
- lib/plugins/inspec-reporter-html2/templates/default.js
|
736
|
+
- lib/plugins/inspec-reporter-html2/templates/profile.html.erb
|
737
|
+
- lib/plugins/inspec-reporter-html2/templates/result.html.erb
|
738
|
+
- lib/plugins/inspec-reporter-html2/templates/selector.html.erb
|
727
739
|
- lib/plugins/inspec-reporter-json-min/README.md
|
728
740
|
- lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min.rb
|
729
741
|
- lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min/reporter.rb
|