inspec-core 4.56.17 → 5.10.5
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.
- checksums.yaml +4 -4
- data/etc/deprecations.json +12 -16
- data/inspec-core.gemspec +1 -1
- data/lib/inspec/base_cli.rb +14 -2
- data/lib/inspec/cli.rb +15 -0
- data/lib/inspec/dependency_installer.rb +74 -0
- data/lib/inspec/dependency_loader.rb +97 -0
- data/lib/inspec/dsl.rb +11 -2
- data/lib/inspec/errors.rb +7 -0
- data/lib/inspec/formatters/base.rb +23 -0
- data/lib/inspec/metadata.rb +36 -0
- data/lib/inspec/plugin/v2/installer.rb +9 -2
- data/lib/inspec/plugin/v2/loader.rb +13 -0
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +44 -1
- data/lib/inspec/plugin/v2/status.rb +2 -1
- data/lib/inspec/profile.rb +63 -0
- data/lib/inspec/reporters/automate.rb +1 -1
- data/lib/inspec/reporters/cli.rb +1 -1
- data/lib/inspec/reporters/json.rb +31 -11
- data/lib/inspec/resource.rb +6 -0
- data/lib/inspec/resources/apt.rb +12 -6
- data/lib/inspec/resources/cgroup.rb +101 -0
- data/lib/inspec/resources/cron.rb +49 -0
- data/lib/inspec/resources/ipfilter.rb +59 -0
- data/lib/inspec/resources/ipnat.rb +58 -0
- data/lib/inspec/resources/lxc.rb +57 -0
- data/lib/inspec/resources/oracledb_session.rb +7 -3
- data/lib/inspec/resources/postgres_session.rb +4 -2
- data/lib/inspec/resources/virtualization.rb +9 -3
- data/lib/inspec/resources.rb +3 -16
- data/lib/inspec/runner.rb +18 -1
- data/lib/inspec/runner_rspec.rb +15 -0
- data/lib/inspec/schema/exec_json.rb +59 -58
- data/lib/inspec/schema/exec_json_min.rb +16 -16
- data/lib/inspec/schema/primitives.rb +68 -51
- data/lib/inspec/schema/profile_json.rb +27 -27
- data/lib/inspec/schema.rb +1 -0
- data/lib/inspec/ui.rb +10 -0
- data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec.rb +3 -0
- data/lib/plugins/inspec-artifact/inspec-artifact.gemspec +9 -0
- data/lib/plugins/inspec-compliance/inspec-compliance.gemspec +9 -0
- data/lib/plugins/inspec-habitat/inspec-habitat.gemspec +9 -0
- data/lib/plugins/inspec-init/inspec-init.gemspec +9 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
- data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
- data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
- data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
- data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
- data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
- data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
- data/lib/plugins/inspec-plugin-manager-cli/inspec-plugin-manager-cli.gemspec +10 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +15 -11
- data/lib/plugins/inspec-reporter-html2/inspec-reporter-html2.gemspec +9 -0
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +2 -0
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +3 -0
- data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -0
- data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
- data/lib/plugins/inspec-reporter-json-min/inspec-reporter-json-min.gemspec +9 -0
- data/lib/plugins/inspec-reporter-junit/inspec-reporter-junit.gemspec +9 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/inspec-streaming-reporter-progress-bar.gemspec +9 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +112 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
- metadata +31 -3
data/lib/inspec/runner.rb
CHANGED
@@ -105,6 +105,7 @@ module Inspec
|
|
105
105
|
|
106
106
|
write_lockfile(profile) if @create_lockfile
|
107
107
|
profile.locked_dependencies
|
108
|
+
profile.load_gem_dependencies
|
108
109
|
profile_context = profile.load_libraries
|
109
110
|
|
110
111
|
profile_context.dependencies.list.values.each do |requirement|
|
@@ -126,9 +127,25 @@ module Inspec
|
|
126
127
|
end
|
127
128
|
end
|
128
129
|
|
130
|
+
controls_count = 0
|
131
|
+
control_checks_count_map = {}
|
132
|
+
|
129
133
|
all_controls.each do |rule|
|
130
|
-
|
134
|
+
unless rule.nil?
|
135
|
+
register_rule(rule)
|
136
|
+
checks = ::Inspec::Rule.prepare_checks(rule)
|
137
|
+
unless checks.empty?
|
138
|
+
# controls with empty tests are avoided
|
139
|
+
# checks represent tests within control
|
140
|
+
controls_count += 1
|
141
|
+
control_checks_count_map[rule.to_s] = checks.count
|
142
|
+
end
|
143
|
+
end
|
131
144
|
end
|
145
|
+
|
146
|
+
# this sets data via runner-rspec into base RSpec formatter object, which gets used up within streaming plugins
|
147
|
+
@test_collector.set_controls_count(controls_count)
|
148
|
+
@test_collector.set_control_checks_count_map(control_checks_count_map)
|
132
149
|
end
|
133
150
|
|
134
151
|
def run(with = nil)
|
data/lib/inspec/runner_rspec.rb
CHANGED
@@ -42,6 +42,21 @@ module Inspec
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
# These control count related methods are called from load logic of runner library of inspec
|
46
|
+
######### Start of control count related methods
|
47
|
+
def set_controls_count(controls_count)
|
48
|
+
formatters.each do |fmt|
|
49
|
+
fmt.set_controls_count(controls_count)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_control_checks_count_map(mapping)
|
54
|
+
formatters.each do |fmt|
|
55
|
+
fmt.set_control_checks_count_map(mapping)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
######### end of control count related methods
|
59
|
+
|
45
60
|
def backend
|
46
61
|
formatters.first.backend
|
47
62
|
end
|
@@ -11,16 +11,16 @@ module Inspec
|
|
11
11
|
"additionalProperties" => true,
|
12
12
|
"required" => %w{label data},
|
13
13
|
"properties" => {
|
14
|
-
"label" => Primitives::STRING,
|
15
|
-
"data" => Primitives::STRING,
|
14
|
+
"label" => Primitives.desc(Primitives::STRING, "The type of description. Examples: 'fix' or 'check'."),
|
15
|
+
"data" => Primitives.desc(Primitives::STRING, "The text of the description."),
|
16
16
|
},
|
17
|
-
}, [])
|
17
|
+
}, [], "A description for a control.")
|
18
18
|
|
19
19
|
# Lists the potential values for a control result
|
20
20
|
CONTROL_RESULT_STATUS = Primitives::SchemaType.new("Control Result Status", {
|
21
21
|
"type" => "string",
|
22
22
|
"enum" => %w{passed failed skipped error},
|
23
|
-
}, [])
|
23
|
+
}, [], "The status of a control. Should be one of 'passed', 'failed', 'skipped', or 'error'.")
|
24
24
|
|
25
25
|
# Represents the statistics/result of a control"s execution
|
26
26
|
CONTROL_RESULT = Primitives::SchemaType.new("Control Result", {
|
@@ -28,24 +28,26 @@ module Inspec
|
|
28
28
|
"additionalProperties" => true,
|
29
29
|
"required" => %w{code_desc run_time start_time},
|
30
30
|
"properties" => {
|
31
|
-
"status" => CONTROL_RESULT_STATUS.ref,
|
32
|
-
"code_desc" => Primitives::STRING,
|
33
|
-
"run_time" => Primitives::NUMBER,
|
34
|
-
"start_time" => Primitives::STRING,
|
31
|
+
"status" => Primitives.desc(CONTROL_RESULT_STATUS.ref, "The status of this test within the control. Example: 'failed'."),
|
32
|
+
"code_desc" => Primitives.desc(Primitives::STRING, "A description of this test. Example: 'limits.conf * is expected to include ['hard', 'maxlogins', '10']."),
|
33
|
+
"run_time" => Primitives.desc(Primitives::NUMBER, "The execution time in seconds for the test."),
|
34
|
+
"start_time" => Primitives.desc(Primitives::STRING, "The time at which the test started."),
|
35
35
|
|
36
36
|
# All optional
|
37
|
-
"resource" => Primitives::STRING,
|
38
|
-
"message" => Primitives::STRING,
|
39
|
-
"skip_message" => Primitives::STRING,
|
40
|
-
"exception" => Primitives::STRING,
|
37
|
+
"resource" => Primitives.desc(Primitives::STRING, "The resource used in the test. Example: in Inspec, you can use the 'File' resource."),
|
38
|
+
"message" => Primitives.desc(Primitives::STRING, "An explanation of the test status - usually only provided when the test fails."),
|
39
|
+
"skip_message" => Primitives.desc(Primitives::STRING, "An explanation of the test status if the status was 'skipped."),
|
40
|
+
"exception" => Primitives.desc(Primitives::STRING, "The type of exception if an exception was thrown."),
|
41
|
+
"resource_id" => Primitives.desc(Primitives::STRING, "The unique identifier of the resource."),
|
41
42
|
"backtrace" => {
|
42
43
|
"anyOf" => [
|
43
44
|
Primitives.array(Primitives::STRING),
|
44
45
|
Primitives::NULL,
|
45
46
|
],
|
47
|
+
"description" => "The stacktrace/backtrace of the exception if one occurred.",
|
46
48
|
},
|
47
49
|
},
|
48
|
-
}, [CONTROL_RESULT_STATUS])
|
50
|
+
}, [CONTROL_RESULT_STATUS], "A test within a control and its results and findings such as how long it took to run.")
|
49
51
|
|
50
52
|
# Represents a control produced
|
51
53
|
CONTROL = Primitives::SchemaType.new("Exec JSON Control", {
|
@@ -53,26 +55,25 @@ module Inspec
|
|
53
55
|
"additionalProperties" => true,
|
54
56
|
"required" => %w{id title desc impact refs tags code source_location results},
|
55
57
|
"properties" => {
|
56
|
-
"id" => Primitives.desc(Primitives::STRING, "The
|
57
|
-
"title" => { "type" => %w{string null} }, # Nullable string
|
58
|
-
"desc" => { "type" => %w{string null} },
|
59
|
-
"descriptions" => Primitives.array(CONTROL_DESCRIPTION.ref),
|
60
|
-
"impact" => Primitives::IMPACT,
|
61
|
-
"refs" => Primitives.array(Primitives::REFERENCE.ref),
|
62
|
-
"tags" => Primitives::TAGS,
|
63
|
-
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code"),
|
64
|
-
"source_location" => Primitives::SOURCE_LOCATION.ref,
|
65
|
-
"results" => Primitives.desc(Primitives.array(CONTROL_RESULT.ref), %q
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
}),
|
58
|
+
"id" => Primitives.desc(Primitives::STRING, "The id."),
|
59
|
+
"title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."), # Nullable string
|
60
|
+
"desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."),
|
61
|
+
"descriptions" => Primitives.desc(Primitives.array(CONTROL_DESCRIPTION.ref), "A set of additional descriptions. Example: the 'fix' text."),
|
62
|
+
"impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."),
|
63
|
+
"refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."),
|
64
|
+
"tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."),
|
65
|
+
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code."),
|
66
|
+
"source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."),
|
67
|
+
"results" => Primitives.desc(Primitives.array(CONTROL_RESULT.ref), %q(
|
68
|
+
The set of all tests within the control and their results and findings. Example:
|
69
|
+
For Chef Inspec, if in the control's code we had the following:
|
70
|
+
describe sshd_config do
|
71
|
+
its('Port') { should cmp 22 }
|
72
|
+
end
|
73
|
+
The findings from this block would be appended to the results, as well as those of any other blocks within the control.
|
74
|
+
)),
|
74
75
|
},
|
75
|
-
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT])
|
76
|
+
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.")
|
76
77
|
|
77
78
|
# Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019
|
78
79
|
# However, concessions were made to the reality of current reporters, specifically
|
@@ -86,30 +87,30 @@ module Inspec
|
|
86
87
|
# sha256, status, status_message
|
87
88
|
"properties" => {
|
88
89
|
# These are provided in inspec.yml
|
89
|
-
"name" => Primitives::STRING,
|
90
|
-
"title" => Primitives::STRING,
|
91
|
-
"maintainer" => Primitives::STRING,
|
92
|
-
"copyright" => Primitives::STRING,
|
93
|
-
"copyright_email" => Primitives::STRING,
|
94
|
-
"depends" => Primitives.array(Primitives::DEPENDENCY.ref),
|
95
|
-
"parent_profile" => Primitives::STRING,
|
96
|
-
"license" => Primitives::STRING,
|
97
|
-
"summary" => Primitives::STRING,
|
98
|
-
"version" => Primitives::STRING,
|
99
|
-
"supports" => Primitives.array(Primitives::SUPPORT.ref),
|
100
|
-
"description" => Primitives::STRING,
|
101
|
-
"inspec_version" => Primitives::STRING,
|
90
|
+
"name" => Primitives.desc(Primitives::STRING, "The name - must be unique."),
|
91
|
+
"title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."),
|
92
|
+
"maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."),
|
93
|
+
"copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."),
|
94
|
+
"copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contact information of the copyright holder(s)."),
|
95
|
+
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."),
|
96
|
+
"parent_profile" => Primitives.desc(Primitives::STRING, "The name of the parent profile if the profile is a dependency of another."),
|
97
|
+
"license" => Primitives.desc(Primitives::STRING, "The copyright license. Example: the full text or the name, such as 'Apache License, Version 2.0'."),
|
98
|
+
"summary" => Primitives.desc(Primitives::STRING, "The summary. Example: the Security Technical Implementation Guide (STIG) header."),
|
99
|
+
"version" => Primitives.desc(Primitives::STRING, "The version of the profile."),
|
100
|
+
"supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."),
|
101
|
+
"description" => Primitives.desc(Primitives::STRING, "The description - should be more detailed than the summary."),
|
102
|
+
"inspec_version" => Primitives.desc(Primitives::STRING, "The version of Inspec."),
|
102
103
|
|
103
104
|
# These are generated at runtime, and all except status_message and skip_message are guaranteed
|
104
|
-
"sha256" => Primitives::STRING,
|
105
|
-
"status" => Primitives::STRING,
|
106
|
-
"status_message" => Primitives::STRING,
|
107
|
-
"skip_message" => Primitives::STRING,
|
108
|
-
"controls" => Primitives.array(CONTROL.ref),
|
109
|
-
"groups" => Primitives.array(Primitives::CONTROL_GROUP.ref),
|
110
|
-
"attributes" => Primitives.array(Primitives::INPUT),
|
105
|
+
"sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
|
106
|
+
"status" => Primitives.desc(Primitives::STRING, "The status. Example: loaded."), # enum? loaded, failed, skipped
|
107
|
+
"status_message" => Primitives.desc(Primitives::STRING, "The reason for the status. Example: why it was skipped or failed to load."),
|
108
|
+
"skip_message" => Primitives.desc(Primitives::STRING, "The reason for skipping if it was skipped."), # Deprecated field - status_message should be used instead.
|
109
|
+
"controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls including any findings."),
|
110
|
+
"groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."),
|
111
|
+
"attributes" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used in the run."),
|
111
112
|
},
|
112
|
-
}, [CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT])
|
113
|
+
}, [CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT], "Information on the set of controls assessed. Example: it can include the name of the Inspec profile and any findings.")
|
113
114
|
|
114
115
|
# Result of exec json. Top level value
|
115
116
|
# TODO: Include the format of top level controls. This was omitted for lack of sufficient examples
|
@@ -118,12 +119,12 @@ module Inspec
|
|
118
119
|
"additionalProperties" => true,
|
119
120
|
"required" => %w{platform profiles statistics version},
|
120
121
|
"properties" => {
|
121
|
-
"platform" => Primitives::PLATFORM.ref,
|
122
|
-
"profiles" => Primitives.array(PROFILE.ref),
|
123
|
-
"statistics" => Primitives::STATISTICS.ref,
|
124
|
-
"version" => Primitives::STRING,
|
122
|
+
"platform" => Primitives.desc(Primitives::PLATFORM.ref, "Information on the platform the run from the tool that generated the findings was from. Example: the name of the operating system."),
|
123
|
+
"profiles" => Primitives.desc(Primitives.array(PROFILE.ref), "Information on the run(s) from the tool that generated the findings. Example: the findings."),
|
124
|
+
"statistics" => Primitives.desc(Primitives::STATISTICS.ref, "Statistics for the run(s) from the tool that generated the findings. Example: the runtime duration."),
|
125
|
+
"version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."),
|
125
126
|
},
|
126
|
-
}, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS])
|
127
|
+
}, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.")
|
127
128
|
end
|
128
129
|
end
|
129
130
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "inspec/schema/primitives"
|
2
2
|
|
3
|
-
# These type occur only when running "exec --reporter
|
3
|
+
# These type occur only when running "exec --reporter exec-jsonmin <file>".
|
4
4
|
|
5
5
|
module Inspec
|
6
6
|
module Schema
|
@@ -11,28 +11,28 @@ module Inspec
|
|
11
11
|
"additionalProperties" => true,
|
12
12
|
"required" => %w{id profile_id profile_sha256 status code_desc},
|
13
13
|
"properties" => {
|
14
|
-
"id" => Primitives::STRING,
|
15
|
-
"profile_id" => { "type" => %w{string null} },
|
16
|
-
"profile_sha256" => Primitives::STRING,
|
17
|
-
"status" => Primitives::STRING,
|
18
|
-
"code_desc" => Primitives::STRING,
|
19
|
-
"
|
20
|
-
"
|
21
|
-
"
|
22
|
-
"exception" => Primitives::STRING,
|
23
|
-
"backtrace" => Primitives.array(Primitives::STRING),
|
14
|
+
"id" => Primitives.desc(Primitives::STRING, "The id."),
|
15
|
+
"profile_id" => Primitives.desc({ "type" => %w{string null} }, "The name of the profile that can uniquely identify it - is nullable."),
|
16
|
+
"profile_sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
|
17
|
+
"status" => Primitives.desc(Primitives::STRING, "The status of the control. Example: 'failed'."),
|
18
|
+
"code_desc" => Primitives.desc(Primitives::STRING, "A description of the control. Example: 'limits.conf * is expected to include ['hard', 'maxlogins', '10']."),
|
19
|
+
"message" => Primitives.desc(Primitives::STRING, "An explanation of the control status - usually only provided when the control fails."),
|
20
|
+
"skip_message" => Primitives.desc(Primitives::STRING, "An explanation of the status if the status was 'skipped'."),
|
21
|
+
"resource" => Primitives.desc(Primitives::STRING, "The resource used in the test. Example: in Inspec, you can use the 'File' resource."),
|
22
|
+
"exception" => Primitives.desc(Primitives::STRING, "The type of exception if an exception was thrown."),
|
23
|
+
"backtrace" => Primitives.desc(Primitives.array(Primitives::STRING), "The stacktrace/backtrace of the exception if one occurred."), # shouldn't this also be an anyOf similar to the CONTROL_RESULT from exec_json?
|
24
24
|
},
|
25
|
-
}, [])
|
25
|
+
}, [], "The set of all tests within the control and their results and findings.")
|
26
26
|
|
27
27
|
# Result of exec jsonmin. Top level value
|
28
|
-
OUTPUT = Primitives::SchemaType.new("Exec JSON-MIN
|
28
|
+
OUTPUT = Primitives::SchemaType.new("Exec JSON-MIN Output", {
|
29
29
|
"type" => "object",
|
30
30
|
"additionalProperties" => true,
|
31
31
|
"required" => %w{statistics controls version},
|
32
32
|
"properties" => {
|
33
|
-
"statistics" => Primitives::STATISTICS.ref,
|
34
|
-
"version" => Primitives::STRING,
|
35
|
-
"controls" => Primitives.array(CONTROL.ref),
|
33
|
+
"statistics" => Primitives.desc(Primitives::STATISTICS.ref, "Statistics for the run(s) from the tool that generated the findings. Example: the runtime duration."),
|
34
|
+
"version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef Inspec."),
|
35
|
+
"controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls including any findings as reported by the tool."),
|
36
36
|
},
|
37
37
|
}, [Primitives::STATISTICS, CONTROL])
|
38
38
|
end
|
@@ -35,7 +35,7 @@ module Inspec
|
|
35
35
|
# Use this class to quickly add/use object types to/in a definition block
|
36
36
|
class SchemaType
|
37
37
|
attr_accessor :name, :depends
|
38
|
-
def initialize(name, body, dependencies)
|
38
|
+
def initialize(name, body, dependencies, description = nil)
|
39
39
|
# Validate the schema
|
40
40
|
Primitives.validate_schema(body)
|
41
41
|
# The title of the type
|
@@ -44,14 +44,17 @@ module Inspec
|
|
44
44
|
@body = body
|
45
45
|
# What SchemaType[]s it depends on. In essence, any thing that you .ref in the body
|
46
46
|
@depends = Set.new(dependencies)
|
47
|
+
# The description of the type
|
48
|
+
@description = description
|
47
49
|
end
|
48
50
|
|
49
51
|
# Produce this schema types generated body.
|
50
52
|
# Use to actually define the ref!
|
51
53
|
def body
|
52
54
|
@body.merge({
|
55
|
+
"description" => @description,
|
53
56
|
"title" => @name,
|
54
|
-
})
|
57
|
+
}).compact
|
55
58
|
end
|
56
59
|
|
57
60
|
# Formats this to have a JSON pointer compatible title
|
@@ -89,22 +92,32 @@ module Inspec
|
|
89
92
|
URL = { "type" => "string" }.freeze
|
90
93
|
|
91
94
|
# A controls tags can have any number of properties, and any sorts of values
|
92
|
-
TAGS = {
|
95
|
+
TAGS = {
|
96
|
+
"type" => "object",
|
97
|
+
"additionalProperties" => true,
|
98
|
+
"description" => "A set of any number of tags - they can have any sort of value and are usually metadata. Example: 'nist' => ['AC-10'].",
|
99
|
+
}.freeze
|
93
100
|
|
94
101
|
# Attributes/Inputs specify the inputs to a profile.
|
95
|
-
INPUT = {
|
102
|
+
INPUT = {
|
103
|
+
"type" => "object",
|
104
|
+
"additionalProperties" => true,
|
105
|
+
"description" => "An input or attribute used in the run.",
|
106
|
+
}.freeze
|
96
107
|
|
97
|
-
# Within a control file, impacts can be numeric 0-1 or string in [none,low,medium,high,critical]
|
98
|
-
# However, when they are output, the string types are automatically converted as follows:
|
99
|
-
# none -> 0 to 0.01
|
100
|
-
# low -> 0.01 to 0.4
|
101
|
-
# medium -> 0.4 to 0.7
|
102
|
-
# high -> 0.7 to 0.9
|
103
|
-
# Critical -> 0.9 to 1.0 (inclusive)
|
104
108
|
IMPACT = {
|
105
109
|
"type" => "number",
|
106
110
|
"minimum" => 0.0,
|
107
111
|
"maximum" => 1.0,
|
112
|
+
"description" => %q{
|
113
|
+
Within a control file, impacts can be a decimal number in the range [0,1] or a string that is one of [none,low,medium,high,critical].
|
114
|
+
However, the string will be automatically converted as follows:
|
115
|
+
none -> 0 to 0.01
|
116
|
+
low -> 0.01 to 0.4
|
117
|
+
medium -> 0.4 to 0.7
|
118
|
+
high -> 0.7 to 0.9
|
119
|
+
critical -> 0.9 to 1.0
|
120
|
+
},
|
108
121
|
}.freeze
|
109
122
|
|
110
123
|
# A status for a control
|
@@ -123,9 +136,9 @@ module Inspec
|
|
123
136
|
"additionalProperties" => true,
|
124
137
|
"required" => ["total"],
|
125
138
|
"properties" => {
|
126
|
-
"total" => desc(NUMBER, "
|
139
|
+
"total" => desc(NUMBER, "The total. Example: the total number of controls in a given category for a run."),
|
127
140
|
},
|
128
|
-
}, [])
|
141
|
+
}, [], "Statistics for a given item, such as the total.")
|
129
142
|
|
130
143
|
# Bundles several results statistics, representing distinct groups of controls
|
131
144
|
STATISTIC_GROUPING = SchemaType.new("Statistic Hash", {
|
@@ -133,11 +146,11 @@ module Inspec
|
|
133
146
|
"additionalProperties" => true,
|
134
147
|
"required" => [],
|
135
148
|
"properties" => {
|
136
|
-
|
137
|
-
|
138
|
-
|
149
|
+
"passed" => desc(STATISTIC_ITEM.ref, "Statistics for the controls that passed."),
|
150
|
+
"skipped" => desc(STATISTIC_ITEM.ref, "Statistics for the controls that were skipped."),
|
151
|
+
"failed" => desc(STATISTIC_ITEM.ref, "Statistics for the controls that failed."),
|
139
152
|
},
|
140
|
-
}, [STATISTIC_ITEM])
|
153
|
+
}, [STATISTIC_ITEM], "Statistics for the control results.")
|
141
154
|
|
142
155
|
# Contains statistics of an exec run.
|
143
156
|
STATISTICS = SchemaType.new("Statistics", {
|
@@ -145,10 +158,10 @@ module Inspec
|
|
145
158
|
"additionalProperties" => true,
|
146
159
|
"required" => ["duration"],
|
147
160
|
"properties" => {
|
148
|
-
"duration" => desc(NUMBER, "How long (in seconds) this
|
161
|
+
"duration" => desc(NUMBER, "How long (in seconds) this run by the tool was."),
|
149
162
|
"controls" => desc(STATISTIC_GROUPING.ref, "Breakdowns of control statistics by result"),
|
150
163
|
},
|
151
|
-
}, [STATISTIC_GROUPING])
|
164
|
+
}, [STATISTIC_GROUPING], "Statistics for the run(s) such as how long it took.")
|
152
165
|
|
153
166
|
# Profile dependencies
|
154
167
|
DEPENDENCY = SchemaType.new("Dependency", {
|
@@ -156,17 +169,17 @@ module Inspec
|
|
156
169
|
"additionalProperties" => true, # Weird stuff shows up here sometimes
|
157
170
|
"required" => [], # Mysteriously even in a run profile we can have no status
|
158
171
|
"properties" => {
|
159
|
-
"name" => STRING,
|
160
|
-
"url" => URL,
|
161
|
-
"branch" => STRING,
|
162
|
-
"path" => STRING,
|
163
|
-
"status_message" => STRING,
|
164
|
-
"status" => STRING,
|
165
|
-
"git" => URL,
|
166
|
-
"supermarket" => STRING,
|
167
|
-
"compliance" => STRING,
|
172
|
+
"name" => desc(STRING, "The name/assigned alias."),
|
173
|
+
"url" => desc(URL, "The address of the dependency."),
|
174
|
+
"branch" => desc(STRING, "The branch name for a git repo."),
|
175
|
+
"path" => desc(STRING, "The relative path if the dependency is locally available."),
|
176
|
+
"status_message" => desc(STRING, "The reason for the status if it is 'failed' or 'skipped'."),
|
177
|
+
"status" => desc(STRING, "The status. Should be: 'loaded', 'failed', or 'skipped'."), # NOTE: enum?
|
178
|
+
"git" => desc(URL, "The location of the git repo. Example: 'https://github.com/mitre/canonical-ubuntu-18.04-lts-stig-baseline.git'."),
|
179
|
+
"supermarket" => desc(STRING, "The 'user/profilename' attribute for a Supermarket server."),
|
180
|
+
"compliance" => desc(STRING, "The 'user/profilename' attribute for an Automate server."),
|
168
181
|
},
|
169
|
-
}, [])
|
182
|
+
}, [], "A dependency for a profile. Can include relative paths or urls for where to find the dependency.")
|
170
183
|
|
171
184
|
# Represents the platform the test was run on
|
172
185
|
PLATFORM = SchemaType.new("Platform", {
|
@@ -176,9 +189,9 @@ module Inspec
|
|
176
189
|
"properties" => {
|
177
190
|
"name" => desc(STRING, "The name of the platform this was run on."),
|
178
191
|
"release" => desc(STRING, "The version of the platform this was run on."),
|
179
|
-
"target_id" => STRING,
|
192
|
+
"target_id" => desc(STRING, "The id of the target. Example: the name and version of the operating system were not sufficient to identify the platform so a release identifier can additionally be provided like '21H2' for the release version of MS Windows 10."),
|
180
193
|
},
|
181
|
-
}, [])
|
194
|
+
}, [], "Platform information such as its name.")
|
182
195
|
|
183
196
|
# Explains what software ran the inspec profile/made this file. Typically inspec but could in theory be a different software
|
184
197
|
GENERATOR = SchemaType.new("Generator", {
|
@@ -186,10 +199,10 @@ module Inspec
|
|
186
199
|
"additionalProperties" => true,
|
187
200
|
"required" => %w{name version},
|
188
201
|
"properties" => {
|
189
|
-
"name" => desc(STRING, "The name
|
190
|
-
"version" => desc(STRING, "The version
|
202
|
+
"name" => desc(STRING, "The name. Example: Chef Inspec."),
|
203
|
+
"version" => desc(STRING, "The version. Example: 4.18.108."),
|
191
204
|
},
|
192
|
-
}, [])
|
205
|
+
}, [], "The tool that generated this file. Example: Chef Inspec.")
|
193
206
|
|
194
207
|
# Occurs from "exec --reporter json" and "inspec json"
|
195
208
|
# Denotes what file this control comes from, and where within
|
@@ -197,11 +210,11 @@ module Inspec
|
|
197
210
|
"type" => "object",
|
198
211
|
"additionalProperties" => true,
|
199
212
|
"properties" => {
|
200
|
-
"ref" => desc(STRING, "Path to the file that this
|
201
|
-
"line" => desc(NUMBER, "The line
|
213
|
+
"ref" => desc(STRING, "Path to the file that this control originates from."),
|
214
|
+
"line" => desc(NUMBER, "The line on which this control is located."),
|
202
215
|
},
|
203
216
|
"required" => %w{ref line},
|
204
|
-
}, [])
|
217
|
+
}, [], "The explicit location of the control.")
|
205
218
|
|
206
219
|
# References an external document
|
207
220
|
REFERENCE = SchemaType.new("Reference", {
|
@@ -211,26 +224,30 @@ module Inspec
|
|
211
224
|
"type" => "object",
|
212
225
|
"required" => ["ref"],
|
213
226
|
"properties" => { "ref" => STRING },
|
227
|
+
"description" => "A human readable/meaningful reference. Example: a book title.",
|
214
228
|
},
|
215
229
|
{
|
216
230
|
"type" => "object",
|
217
231
|
"required" => ["url"],
|
218
|
-
"properties" => { "url" => STRING },
|
232
|
+
"properties" => { "url" => STRING }, # NOTE: should this be a URL?
|
233
|
+
"description" => "A url pointing at the reference.",
|
219
234
|
},
|
220
235
|
{
|
221
236
|
"type" => "object",
|
222
237
|
"required" => ["uri"],
|
223
|
-
"properties" => { "uri" => STRING },
|
238
|
+
"properties" => { "uri" => STRING }, # NOTE: should this be a URL?
|
239
|
+
"description" => "A uri pointing at the reference.",
|
224
240
|
},
|
225
241
|
# I am of the opinion that this is just an error in the codebase itself. See profiles/wrapper-override to uncover new mysteries!
|
226
242
|
{
|
227
243
|
"type" => "object",
|
228
244
|
"required" => ["ref"],
|
229
245
|
"properties" => { "ref" => array(OBJECT) },
|
246
|
+
"description" => "", # TODO: I'm not sure what goes here. Maybe it's supposed to be objects similar to { "title" => "blah", "text" => "blah" }?
|
230
247
|
|
231
248
|
},
|
232
249
|
],
|
233
|
-
}, [])
|
250
|
+
}, [], "A reference to an external document.")
|
234
251
|
|
235
252
|
# Represents a group of controls within a profile/.rb file
|
236
253
|
CONTROL_GROUP = SchemaType.new("Control Group", {
|
@@ -238,11 +255,11 @@ module Inspec
|
|
238
255
|
"additionalProperties" => true,
|
239
256
|
"required" => %w{id controls},
|
240
257
|
"properties" => {
|
241
|
-
"id" => desc(STRING, "The unique identifier
|
242
|
-
"title" => desc({ type: %w{string null} }, "The
|
243
|
-
"controls" => desc(array(STRING), "The
|
258
|
+
"id" => desc(STRING, "The unique identifier for the group. Example: the relative path to the file specifying the controls."),
|
259
|
+
"title" => desc({ type: %w{string null} }, "The title of the group - should be human readable."), # NOTE: pretty unique type like this - wouldn't it be better to have it be a STRING and then continue to not make it required?
|
260
|
+
"controls" => desc(array(STRING), "The set of controls as specified by their ids in this group. Example: 'V-75443'."),
|
244
261
|
},
|
245
|
-
}, [])
|
262
|
+
}, [], "Descriptions for controls in a group, such as the list of all the controls.")
|
246
263
|
|
247
264
|
# Occurs from "inspec exec --reporter json" and "inspec json"
|
248
265
|
# Represents a platfrom or group of platforms that this profile supports
|
@@ -251,15 +268,15 @@ module Inspec
|
|
251
268
|
"additionalProperties" => true, # NOTE: This should really be false, and inspec should validate profiles to ensure people don't make dumb mistakes like platform_family
|
252
269
|
"required" => [],
|
253
270
|
"properties" => {
|
254
|
-
"platform-family" => STRING,
|
255
|
-
"platform-name" => STRING,
|
256
|
-
"platform" => STRING,
|
257
|
-
"release" => STRING,
|
271
|
+
"platform-family" => desc(STRING, "The platform family. Example: 'redhat'."),
|
272
|
+
"platform-name" => desc(STRING, "The platform name - can include wildcards. Example: 'debian'."),
|
273
|
+
"platform" => desc(STRING, "The location of the platform. Can be: 'os', 'aws', 'azure', or 'gcp'."), # NOTE: enum?
|
274
|
+
"release" => desc(STRING, "The release of the platform. Example: '20.04' for 'ubuntu'."),
|
258
275
|
# os-* supports are being deprecated
|
259
|
-
"os-family" => STRING,
|
260
|
-
"os-name" => STRING,
|
276
|
+
"os-family" => desc(STRING, "Deprecated in favor of platform-family."),
|
277
|
+
"os-name" => desc(STRING, "Deprecated in favor of platform-name."),
|
261
278
|
},
|
262
|
-
}, [])
|
279
|
+
}, [], "A supported platform target. Example: the platform name being 'ubuntu'.")
|
263
280
|
|
264
281
|
end
|
265
282
|
end
|
@@ -8,9 +8,9 @@ module Inspec
|
|
8
8
|
# Represents descriptions. Can have any string => string pairing
|
9
9
|
CONTROL_DESCRIPTIONS = Primitives::SchemaType.new("Profile JSON Control Descriptions", {
|
10
10
|
"type" => "object",
|
11
|
-
"
|
11
|
+
"additionalProperties" => Primitives::STRING,
|
12
12
|
"required" => [],
|
13
|
-
}, [])
|
13
|
+
}, [], "An arbitrary set of descriptions for a control.")
|
14
14
|
|
15
15
|
# Represents a control that hasn't been run
|
16
16
|
# Differs slightly from a normal control, in that it lacks results, and its descriptions are different
|
@@ -19,17 +19,17 @@ module Inspec
|
|
19
19
|
"additionalProperties" => true,
|
20
20
|
"required" => %w{id title desc impact tags code},
|
21
21
|
"properties" => {
|
22
|
-
"id" => Primitives.desc(Primitives::STRING, "The
|
23
|
-
"title" => { "type" => %w{string null} },
|
24
|
-
"desc" => { "type" => %w{string null} },
|
25
|
-
"descriptions" => CONTROL_DESCRIPTIONS.ref,
|
26
|
-
"impact" => Primitives::IMPACT,
|
27
|
-
"refs" => Primitives.array(Primitives::REFERENCE.ref),
|
28
|
-
"tags" => Primitives::TAGS,
|
29
|
-
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code"),
|
30
|
-
"source_location" => Primitives::SOURCE_LOCATION.ref,
|
22
|
+
"id" => Primitives.desc(Primitives::STRING, "The id."),
|
23
|
+
"title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."),
|
24
|
+
"desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."),
|
25
|
+
"descriptions" => Primitives.desc(CONTROL_DESCRIPTIONS.ref, "A set of additional descriptions. Example: the 'fix' text."),
|
26
|
+
"impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."),
|
27
|
+
"refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."),
|
28
|
+
"tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."),
|
29
|
+
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code."),
|
30
|
+
"source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."),
|
31
31
|
},
|
32
|
-
}, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION])
|
32
|
+
}, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.")
|
33
33
|
|
34
34
|
# A profile that has not been run.
|
35
35
|
PROFILE = Primitives::SchemaType.new("Profile JSON Profile", {
|
@@ -37,24 +37,24 @@ module Inspec
|
|
37
37
|
"additionalProperties" => true, # Anything in the yaml will be put in here. LTTODO: Make this stricter!
|
38
38
|
"required" => %w{name supports controls groups sha256},
|
39
39
|
"properties" => {
|
40
|
-
"name" => Primitives::STRING,
|
41
|
-
"supports" => Primitives.array(Primitives::SUPPORT.ref),
|
42
|
-
"controls" => Primitives.array(CONTROL.ref),
|
43
|
-
"groups" => Primitives.array(Primitives::CONTROL_GROUP.ref),
|
44
|
-
"inputs" => Primitives.array(Primitives::INPUT),
|
45
|
-
"sha256" => Primitives::STRING,
|
46
|
-
"status" => Primitives::STRING,
|
47
|
-
"generator" => Primitives::GENERATOR.ref,
|
48
|
-
"version" => Primitives::STRING,
|
40
|
+
"name" => Primitives.desc(Primitives::STRING, "The name - must be unique."),
|
41
|
+
"supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."),
|
42
|
+
"controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls - contains no findings as the assessment has not yet occurred."),
|
43
|
+
"groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."),
|
44
|
+
"inputs" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used to be in the run."),
|
45
|
+
"sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
|
46
|
+
"status" => Primitives.desc(Primitives::STRING, "The status. Example: skipped."),
|
47
|
+
"generator" => Primitives.desc(Primitives::GENERATOR.ref, "The tool that generated this file. Example: Chef Inspec."),
|
48
|
+
"version" => Primitives.desc(Primitives::STRING, "The version of the profile."),
|
49
49
|
|
50
50
|
# Other properties possible in inspec docs, but that aren"t guaranteed
|
51
|
-
"title" => Primitives::STRING,
|
52
|
-
"maintainer" => Primitives::STRING,
|
53
|
-
"copyright" => Primitives::STRING,
|
54
|
-
"copyright_email" => Primitives::STRING,
|
55
|
-
"depends" => Primitives.array(Primitives::DEPENDENCY.ref), # Can have depends, but NOT a parentprofile
|
51
|
+
"title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."),
|
52
|
+
"maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."),
|
53
|
+
"copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."),
|
54
|
+
"copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contract information of the copyright holder(s)."),
|
55
|
+
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."), # Can have depends, but NOT a parentprofile
|
56
56
|
},
|
57
|
-
}, [Primitives::SUPPORT, CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR])
|
57
|
+
}, [Primitives::SUPPORT, CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.")
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
data/lib/inspec/schema.rb
CHANGED
@@ -57,6 +57,7 @@ module Inspec
|
|
57
57
|
"run_time" => { "type" => "number" },
|
58
58
|
"start_time" => { "type" => "string" },
|
59
59
|
"resource_class" => { "type" => "string", "optional" => true },
|
60
|
+
"resource_id" => { "type" => "string", "optional" => true },
|
60
61
|
"skip_message" => { "type" => "string", "optional" => true },
|
61
62
|
"resource" => { "type" => "string", "optional" => true },
|
62
63
|
"message" => { "type" => "string", "optional" => true },
|