inspec-core 4.56.19 → 5.7.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/etc/deprecations.json +12 -11
- 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/plugin_types/streaming_reporter.rb +44 -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/cron.rb +49 -0
- data/lib/inspec/resources/ipfilter.rb +59 -0
- data/lib/inspec/resources/ipnat.rb +58 -0
- data/lib/inspec/resources/oracledb_session.rb +3 -7
- data/lib/inspec/resources/postgres_session.rb +2 -4
- 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 +1 -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-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-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-streaming-reporter-progress-bar/README.md +5 -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 +20 -3
@@ -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 },
|
data/lib/inspec/ui.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
module DeprecatedCloudResourcesList
|
2
|
+
CLOUD_RESOURCES_DEPRECATED = %i{
|
3
|
+
aws_billing_report
|
4
|
+
aws_billing_reports
|
5
|
+
aws_cloudtrail_trail
|
6
|
+
aws_cloudtrail_trails
|
7
|
+
aws_cloudwatch_alarm
|
8
|
+
aws_cloudwatch_log_metric_filter
|
9
|
+
aws_config_delivery_channel
|
10
|
+
aws_config_recorder
|
11
|
+
aws_ec2_instance
|
12
|
+
aws_ebs_volume
|
13
|
+
aws_ebs_volumes
|
14
|
+
aws_flow_log
|
15
|
+
aws_ec2_instances
|
16
|
+
aws_ecs_cluster
|
17
|
+
aws_eks_cluster
|
18
|
+
aws_elb
|
19
|
+
aws_elbs
|
20
|
+
aws_iam_access_key
|
21
|
+
aws_iam_access_keys
|
22
|
+
aws_iam_group
|
23
|
+
aws_iam_groups
|
24
|
+
aws_iam_password_policy
|
25
|
+
aws_iam_policies
|
26
|
+
aws_iam_policy
|
27
|
+
aws_iam_role
|
28
|
+
aws_iam_root_user
|
29
|
+
aws_iam_user
|
30
|
+
aws_iam_users
|
31
|
+
aws_kms_key
|
32
|
+
aws_kms_keys
|
33
|
+
aws_rds_instance
|
34
|
+
aws_route_table
|
35
|
+
aws_route_tables
|
36
|
+
aws_s3_bucket
|
37
|
+
aws_s3_bucket_object
|
38
|
+
aws_s3_buckets
|
39
|
+
aws_security_group
|
40
|
+
aws_security_groups
|
41
|
+
aws_sns_subscription
|
42
|
+
aws_sns_topic
|
43
|
+
aws_sns_topics
|
44
|
+
aws_sqs_queue
|
45
|
+
aws_subnet
|
46
|
+
aws_subnets
|
47
|
+
aws_vpc
|
48
|
+
aws_vpcs
|
49
|
+
azure_generic_resource
|
50
|
+
azure_resource_group
|
51
|
+
azure_virtual_machine
|
52
|
+
azure_virtual_machine_data_disk
|
53
|
+
}.freeze
|
54
|
+
end
|
data/lib/inspec/version.rb
CHANGED
data/lib/inspec.rb
CHANGED
@@ -81,6 +81,7 @@ module InspecPlugins
|
|
81
81
|
File.join("lib", "inspec-plugin-template.erb") => File.join("lib", plugin_name + ".rb"),
|
82
82
|
File.join("lib", "inspec-plugin-template", "cli_command.erb") => File.join("lib", plugin_name, "cli_command.rb"),
|
83
83
|
File.join("lib", "inspec-plugin-template", "reporter.erb") => File.join("lib", plugin_name, "reporter.rb"),
|
84
|
+
File.join("lib", "inspec-plugin-template", "streaming_reporter.erb") => File.join("lib", plugin_name, "streaming_reporter.rb"),
|
84
85
|
File.join("lib", "inspec-plugin-template", "plugin.erb") => File.join("lib", plugin_name, "plugin.rb"),
|
85
86
|
File.join("lib", "inspec-plugin-template", "version.erb") => File.join("lib", plugin_name, "version.rb"),
|
86
87
|
File.join("test", "functional", "inspec_plugin_template_test.erb") => File.join("test", "functional", snake_case + "_test.rb"),
|
@@ -183,6 +184,9 @@ module InspecPlugins
|
|
183
184
|
elsif activators_by_type.key?(:reporter)
|
184
185
|
vars[:reporter_name_dashes] = activators_by_type[:reporter].tr("_", "-")
|
185
186
|
vars[:reporter_name_snake] = activators_by_type[:reporter].tr("-", "_")
|
187
|
+
elsif activators_by_type.key?(:streaming_reporter)
|
188
|
+
vars[:streaming_reporter_name_dashes] = activators_by_type[:streaming_reporter].tr("_", "-")
|
189
|
+
vars[:streaming_reporter_name_snake] = activators_by_type[:streaming_reporter].tr("-", "_")
|
186
190
|
end
|
187
191
|
vars
|
188
192
|
end
|
@@ -267,6 +271,11 @@ module InspecPlugins
|
|
267
271
|
File.join("lib", "inspec-plugin-template", "reporter.erb"),
|
268
272
|
]
|
269
273
|
end
|
274
|
+
unless requested_activators.include?(:streaming_reporter)
|
275
|
+
skips += [
|
276
|
+
File.join("lib", "inspec-plugin-template", "streaming_reporter.erb"),
|
277
|
+
]
|
278
|
+
end
|
270
279
|
|
271
280
|
skips.uniq
|
272
281
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require_relative "renderer"
|
2
|
+
|
3
|
+
module InspecPlugins
|
4
|
+
module Init
|
5
|
+
class CLI < Inspec.plugin(2, :cli_command)
|
6
|
+
#-------------------------------------------------------------------#
|
7
|
+
# inspec init resource
|
8
|
+
#-------------------------------------------------------------------#
|
9
|
+
desc "resource RESOURCE_NAME [options]", "Generates an InSpec resource, which can extend the scope of InSpec resources support"
|
10
|
+
# General options
|
11
|
+
option :prompt, type: :boolean, default: true, desc: "Interactively prompt for information to put in your generated resource."
|
12
|
+
option :overwrite, type: :boolean, default: false, desc: "Overwrite existing files"
|
13
|
+
option :layout, type: :string, default: "resource-pack", desc: "File layout, either 'resource-pack' or 'core'"
|
14
|
+
option :template, type: :string, default: "basic", desc: "Which type of resource template to use"
|
15
|
+
|
16
|
+
# Templating vars
|
17
|
+
option :supports_platform, type: :string, default: "linux", desc: "the platform supported by this resource"
|
18
|
+
option :description, type: :string, default: "Resource description ...", desc: "the description of this resource"
|
19
|
+
option :class_name, type: :string, default: "MyCustomResource", desc: "Class Name for your resource."
|
20
|
+
option :path, type: :string, default: ".", desc: "Subdirectory under which to create files"
|
21
|
+
|
22
|
+
# Wishlist:
|
23
|
+
# Make make_rename_map_resource dynamic:
|
24
|
+
# + Add a --path option which defaults to ., which will create the tree under that path
|
25
|
+
# + Add a --layout option which changes all the tree to act as placing the files in core inspec (lib/inspec/resources, docs-chef-io/)
|
26
|
+
# - Add a --template=plural option which changes the templates to use a set of Filtertable based templates
|
27
|
+
# - Add a --template=inherit option which provides a template for inheriting from the core resources
|
28
|
+
# - Add a template=aws
|
29
|
+
# + Generate properties and matchers:
|
30
|
+
# + generate a has_bells? matcher => it { should have_bells }
|
31
|
+
# + generate a is_purple? matcher => it { should be_purple }
|
32
|
+
# + generate a shoe_size => its('shoe_size') { should cmp 10 }
|
33
|
+
# + Generate unit tests for above properties and matchers
|
34
|
+
# + Generate docs for properties and matchers
|
35
|
+
# + Add --overwrite option
|
36
|
+
|
37
|
+
def resource(resource_name)
|
38
|
+
resource_vars_from_opts_resource
|
39
|
+
template_vars = {
|
40
|
+
name: options[:path], # This is used for the path prefix
|
41
|
+
resource_name: resource_name,
|
42
|
+
}
|
43
|
+
template_vars.merge!(options)
|
44
|
+
template_path = File.join("resources", template_vars["template"])
|
45
|
+
|
46
|
+
render_opts = {
|
47
|
+
templates_path: TEMPLATES_PATH,
|
48
|
+
overwrite: options[:overwrite],
|
49
|
+
file_rename_map: make_rename_map_resource(template_vars),
|
50
|
+
}
|
51
|
+
renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
|
52
|
+
renderer.render_with_values(template_path, "resource", template_vars)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def make_rename_map_resource(vars)
|
58
|
+
if vars["layout"] == "resource-pack"
|
59
|
+
{
|
60
|
+
File.join("libraries", "inspec-resource-template.erb") => File.join("libraries", vars[:resource_name] + ".rb"),
|
61
|
+
File.join("docs", "resource-doc.erb") => File.join("docs", vars[:resource_name] + ".md"),
|
62
|
+
File.join("test", "unit", "inspec-resource-test-template.erb") => File.join("test", "unit", vars[:resource_name] + "_test.rb"),
|
63
|
+
}
|
64
|
+
elsif vars["layout"] == "core"
|
65
|
+
{
|
66
|
+
File.join("libraries", "inspec-resource-template.erb") => File.join("lib", "inspec", "resources", vars[:resource_name] + ".rb"),
|
67
|
+
File.join("docs", "resource-doc.erb") => File.join("docs-chef-io", "content", "inspec", "resources", vars[:resource_name] + ".md"),
|
68
|
+
File.join("test", "unit", "inspec-resource-test-template.erb") => File.join("test", "unit", "resources", vars[:resource_name] + "_test.rb"),
|
69
|
+
}
|
70
|
+
else
|
71
|
+
ui.error("Unrecognized value for 'layout' - please enter either 'resource-pack' or 'core'")
|
72
|
+
ui.exit(:usage_error)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def resource_vars_from_opts_resource
|
77
|
+
if options[:prompt] && ui.interactive?
|
78
|
+
options.dup.merge(prompt_for_options_resource)
|
79
|
+
elsif !options[:prompt]
|
80
|
+
# Nothing to do - unless we need to calculate dynamic defaults in the future
|
81
|
+
else
|
82
|
+
ui.error("You requested interactive prompting for the template variables, but this does not seem to be an interactive terminal.")
|
83
|
+
ui.exit(:usage_error)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def prompt_for_options_resource # rubocop: disable Metrics/AbcSize
|
88
|
+
option_defs = self.class.all_commands["resource"].options
|
89
|
+
options_order = {
|
90
|
+
path: {},
|
91
|
+
layout: {
|
92
|
+
mode: :select,
|
93
|
+
choices: [
|
94
|
+
{ name: "Resource Pack", value: "resource-pack", default: true },
|
95
|
+
{ name: "InSpec Core", value: "core" },
|
96
|
+
],
|
97
|
+
},
|
98
|
+
template: {
|
99
|
+
mode: :select,
|
100
|
+
choices: [
|
101
|
+
{ name: "Basic", value: "basic", default: true },
|
102
|
+
{ name: "Plural", value: "plural" },
|
103
|
+
],
|
104
|
+
},
|
105
|
+
supports_platform: {},
|
106
|
+
description: {},
|
107
|
+
class_name: {},
|
108
|
+
}
|
109
|
+
|
110
|
+
options_order.each do |opt_name, prompt_options|
|
111
|
+
opt_def = option_defs[opt_name]
|
112
|
+
|
113
|
+
case prompt_options[:mode]
|
114
|
+
when :select
|
115
|
+
options[opt_name] = ui.prompt.select("Choose " + opt_def.description + ":", prompt_options[:choices])
|
116
|
+
when :multiline
|
117
|
+
options[opt_name] = ui.prompt.multiline("Enter " + opt_def.description + ". Press Control-D to end.", default: options[opt_name])
|
118
|
+
else
|
119
|
+
# Assume plain ask
|
120
|
+
options[opt_name] = ui.prompt.ask("Enter " + opt_def.description + ":", default: options[opt_name])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -38,8 +38,8 @@ module InspecPlugins
|
|
38
38
|
full_destination_path = Pathname.new(Dir.pwd).join(relative_destination_path)
|
39
39
|
|
40
40
|
# check that the directory does not exist
|
41
|
-
if File.exist?(full_destination_path) && !overwrite_mode
|
42
|
-
ui.plain_line "#{ui.emphasis(full_destination_path)} exists already, use --overwrite"
|
41
|
+
if File.exist?(full_destination_path) && !overwrite_mode && template_values[:name] != "."
|
42
|
+
ui.plain_line "#{ui.emphasis(full_destination_path)} exists already, use --overwrite or move to #{ui.emphasis(full_destination_path)} to create the resource"
|
43
43
|
ui.exit(:usage_error)
|
44
44
|
end
|
45
45
|
|
@@ -57,18 +57,19 @@ module InspecPlugins
|
|
57
57
|
|
58
58
|
relative_destination_item_path = file_rename_map[relative_destination_item_path] || relative_destination_item_path
|
59
59
|
full_destination_item_path = Pathname.new(full_destination_path).join(relative_destination_item_path)
|
60
|
-
if File.
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
if File.file?(source_file)
|
61
|
+
# Be git-like and only create directories if they contain a file
|
62
|
+
containing_directory = full_destination_item_path.dirname
|
63
|
+
unless File.exist?(containing_directory)
|
64
|
+
ui.list_item "Creating directory #{ui.emphasis(containing_directory)}"
|
65
|
+
FileUtils.mkdir_p(containing_directory)
|
66
|
+
end
|
64
67
|
ui.list_item "Creating file #{ui.emphasis(relative_destination_item_path)}"
|
65
68
|
# read & render content
|
66
69
|
content = render(File.read(source_file), template_values)
|
67
70
|
# write file content
|
68
71
|
|
69
72
|
File.write(full_destination_item_path, content)
|
70
|
-
else
|
71
|
-
ui.warning "Ignoring #{ui.emphasis(source_file)}, because its not an file or directoy"
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
@@ -66,6 +66,22 @@ module InspecPlugins
|
|
66
66
|
InspecPlugins::<%= module_name %>::Reporter
|
67
67
|
end
|
68
68
|
<% end %>
|
69
|
+
|
70
|
+
<% if activators[:streaming_reporter] %>
|
71
|
+
# Define a new Streaming Reporter.
|
72
|
+
# The argument here will be used to match against the CLI --reporter option.
|
73
|
+
# `--reporter <%= streaming_reporter_name_snake %>` will load your streaming reporter and perform streaming real-time on each passing, failing or pending test.
|
74
|
+
streaming_reporter :<%= streaming_reporter_name_snake %> do
|
75
|
+
# Calling this activator doesn't mean the reporter is being executed - just
|
76
|
+
# that we should be ready to do so. So, load the file that defines the
|
77
|
+
# functionality.
|
78
|
+
require "<%= plugin_name %>/streaming_reporter"
|
79
|
+
|
80
|
+
# Having loaded our functionality, return a class that will let the
|
81
|
+
# reporting engine tap into it.
|
82
|
+
InspecPlugins::<%= module_name %>::StreamingReporter
|
83
|
+
end
|
84
|
+
<% end %>
|
69
85
|
end
|
70
86
|
end
|
71
87
|
end
|