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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/etc/deprecations.json +12 -11
  3. data/inspec-core.gemspec +1 -1
  4. data/lib/inspec/base_cli.rb +14 -2
  5. data/lib/inspec/cli.rb +15 -0
  6. data/lib/inspec/dependency_installer.rb +74 -0
  7. data/lib/inspec/dependency_loader.rb +97 -0
  8. data/lib/inspec/dsl.rb +11 -2
  9. data/lib/inspec/errors.rb +7 -0
  10. data/lib/inspec/formatters/base.rb +23 -0
  11. data/lib/inspec/metadata.rb +36 -0
  12. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +44 -1
  13. data/lib/inspec/profile.rb +63 -0
  14. data/lib/inspec/reporters/automate.rb +1 -1
  15. data/lib/inspec/reporters/cli.rb +1 -1
  16. data/lib/inspec/reporters/json.rb +31 -11
  17. data/lib/inspec/resource.rb +6 -0
  18. data/lib/inspec/resources/cron.rb +49 -0
  19. data/lib/inspec/resources/ipfilter.rb +59 -0
  20. data/lib/inspec/resources/ipnat.rb +58 -0
  21. data/lib/inspec/resources/oracledb_session.rb +3 -7
  22. data/lib/inspec/resources/postgres_session.rb +2 -4
  23. data/lib/inspec/resources.rb +3 -16
  24. data/lib/inspec/runner.rb +18 -1
  25. data/lib/inspec/runner_rspec.rb +15 -0
  26. data/lib/inspec/schema/exec_json.rb +59 -58
  27. data/lib/inspec/schema/exec_json_min.rb +16 -16
  28. data/lib/inspec/schema/primitives.rb +68 -51
  29. data/lib/inspec/schema/profile_json.rb +27 -27
  30. data/lib/inspec/schema.rb +1 -0
  31. data/lib/inspec/ui.rb +1 -0
  32. data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
  33. data/lib/inspec/version.rb +1 -1
  34. data/lib/inspec.rb +3 -0
  35. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  36. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
  37. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
  38. data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
  39. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
  40. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
  41. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
  42. data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
  43. data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
  44. data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
  45. data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
  46. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +2 -0
  47. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +3 -0
  48. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -0
  49. data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
  50. data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
  51. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
  52. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +112 -0
  53. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
  54. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
  55. 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 = { "type" => "object", "additionalProperties" => true }.freeze
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 = { "type" => "object", "additionalProperties" => true }.freeze
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, "Total number of controls (in this category) for this inspec execution."),
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
- "passed" => STATISTIC_ITEM.ref,
137
- "skipped" => STATISTIC_ITEM.ref,
138
- "failed" => STATISTIC_ITEM.ref,
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 inspec exec ran for."),
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 of the software that generated this report."),
190
- "version" => desc(STRING, "The version of the software that generated this report."),
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 statement originates from"),
201
- "line" => desc(NUMBER, "The line at which this statement is located in the file"),
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 of the group"),
242
- "title" => desc({ type: %w{string null} }, "The name of the group"),
243
- "controls" => desc(array(STRING), "The control IDs in this group"),
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
- "aditionalProperties" => Primitives::STRING,
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 ID of this control"),
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
@@ -30,6 +30,7 @@ module Inspec
30
30
  EXIT_USAGE_ERROR = 1
31
31
  EXIT_PLUGIN_ERROR = 2
32
32
  EXIT_FATAL_DEPRECATION = 3
33
+ EXIT_GEM_DEPENDENCY_LOAD_ERROR = 4
33
34
  EXIT_LICENSE_NOT_ACCEPTED = 172
34
35
  EXIT_FAILED_TESTS = 100
35
36
  EXIT_SKIPPED_TESTS = 101
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = "4.56.19".freeze
2
+ VERSION = "5.7.9".freeze
3
3
  end
data/lib/inspec.rb CHANGED
@@ -28,3 +28,6 @@ require "inspec/base_cli"
28
28
  require "inspec/fetcher"
29
29
  require "inspec/source_reader"
30
30
  require "inspec/resource"
31
+
32
+ require "inspec/dependency_loader"
33
+ require "inspec/dependency_installer"
@@ -10,6 +10,7 @@ module InspecPlugins
10
10
 
11
11
  require_relative "cli_profile"
12
12
  require_relative "cli_plugin"
13
+ require_relative "cli_resource"
13
14
  end
14
15
  end
15
16
  end
@@ -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.directory?(source_file)
61
- ui.list_item "Creating directory #{ui.emphasis(relative_destination_item_path)}"
62
- FileUtils.mkdir_p(full_destination_item_path)
63
- elsif File.file?(source_file)
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