emasser 3.4.0 → 3.4.1

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +8 -8
  3. data/.env-example +12 -12
  4. data/.github/release-drafter.yml +15 -15
  5. data/.github/workflows/codeql-analysis.yml +70 -70
  6. data/.github/workflows/draft-release.yml +15 -15
  7. data/.github/workflows/gh-pages.yml +32 -32
  8. data/.github/workflows/push-to-docker-mail.yml +28 -28
  9. data/.github/workflows/push-to-docker.yml +35 -35
  10. data/.github/workflows/release.yml +42 -42
  11. data/.github/workflows/rubocop.yml +23 -23
  12. data/.github/workflows/test-cli.yml +72 -72
  13. data/.gitignore +19 -19
  14. data/.mergify.yml +25 -25
  15. data/.rubocop.yml +80 -80
  16. data/.rubocop_todo.yml +27 -27
  17. data/CHANGELOG.md +16 -16
  18. data/Dockerfile +44 -44
  19. data/Gemfile +8 -8
  20. data/Gemfile.lock +104 -104
  21. data/LICENSE.md +15 -15
  22. data/README.md +178 -178
  23. data/Rakefile +18 -18
  24. data/_config.yml +1 -1
  25. data/docs/features.md +1436 -1436
  26. data/docs/redoc/index.html +1230 -1230
  27. data/emasser.gemspec +44 -44
  28. data/exe/emasser +5 -5
  29. data/lib/emasser/cli.rb +37 -37
  30. data/lib/emasser/configuration.rb +49 -49
  31. data/lib/emasser/constants.rb +26 -26
  32. data/lib/emasser/delete.rb +148 -148
  33. data/lib/emasser/errors.rb +14 -14
  34. data/lib/emasser/get.rb +949 -949
  35. data/lib/emasser/help/approvalCac_post_mapper.md +20 -20
  36. data/lib/emasser/help/approvalPac_post_mapper.md +20 -20
  37. data/lib/emasser/help/artifacts_del_mapper.md +9 -9
  38. data/lib/emasser/help/artifacts_post_mapper.md +59 -59
  39. data/lib/emasser/help/artifacts_put_mapper.md +34 -34
  40. data/lib/emasser/help/cloudresource_post_mapper.md +62 -62
  41. data/lib/emasser/help/cmmc_get_mapper.md +4 -4
  42. data/lib/emasser/help/container_post_mapper.md +44 -44
  43. data/lib/emasser/help/controls_put_mapper.md +74 -74
  44. data/lib/emasser/help/milestone_del_mapper.md +11 -11
  45. data/lib/emasser/help/milestone_post_mapper.md +14 -14
  46. data/lib/emasser/help/milestone_put_mapper.md +23 -23
  47. data/lib/emasser/help/poam_del_mapper.md +5 -5
  48. data/lib/emasser/help/poam_post_mapper.md +93 -93
  49. data/lib/emasser/help/poam_put_mapper.md +107 -107
  50. data/lib/emasser/help/staticcode_clear_mapper.md +16 -16
  51. data/lib/emasser/help/staticcode_post_mapper.md +21 -21
  52. data/lib/emasser/help/testresults_post_mapper.md +21 -21
  53. data/lib/emasser/help.rb +11 -11
  54. data/lib/emasser/input_converters.rb +21 -21
  55. data/lib/emasser/options_parser.rb +20 -20
  56. data/lib/emasser/output_converters.rb +111 -111
  57. data/lib/emasser/post.rb +830 -802
  58. data/lib/emasser/put.rb +588 -588
  59. data/lib/emasser/version.rb +5 -5
  60. data/lib/emasser.rb +19 -19
  61. metadata +8 -8
data/lib/emasser/post.rb CHANGED
@@ -1,802 +1,830 @@
1
- # frozen_string_literal: true
2
-
3
- # Hack class that properly formats the CLI help
4
- class SubCommandBase < Thor
5
- include OptionsParser
6
- include InputConverters
7
- include OutputConverters
8
-
9
- # We do not control the method declaration for the banner
10
-
11
- # rubocop:disable Style/OptionalBooleanParameter
12
- def self.banner(command, _namespace = nil, subcommand = false)
13
- # Use the $thor_runner (declared by the Thor CLI framework)
14
- # to properly format the help text of sub-sub-commands.
15
-
16
- # rubocop:disable Style/GlobalVars
17
- if ancestors[0].to_s.include? '::Post'
18
- "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
19
- else
20
- "#{basename} post #{command.formatted_usage(self, $thor_runner, subcommand)}"
21
- end
22
- # rubocop:enable Style/GlobalVars
23
- end
24
- # rubocop:enable Style/OptionalBooleanParameter
25
- end
26
-
27
- # Override thor's long_desc identation behavior
28
- class Thor
29
- module Shell
30
- class Basic
31
- def print_wrapped(message, _options = {})
32
- message = "\n#{message}\n" unless message[0] == "\n"
33
- stdout.puts message
34
- end
35
- end
36
- end
37
- end
38
-
39
- module Emasser
40
- POAMS_POST_HELP_MESSAGE = "\nInvoke \"emasser post poams help add\" for additional help"
41
- SCAN_POST_HELP_MESSAGE = "\nInvoke \"emasser post scan_findings help clear\" for additional help"
42
- # The Test Results endpoints provide the ability to add test results for a
43
- # system's Assessment Procedures (CCIs) which determine Security Control compliance.
44
- #
45
- # Endpoint:
46
- # /api/systems/{systemId}/test-results - Add one or many test results for a system
47
- class TestResults < SubCommandBase
48
- def self.exit_on_failure?
49
- true
50
- end
51
-
52
- desc 'add', 'Post a test result for a system'
53
- long_desc Help.text(:testresults_post_mapper)
54
-
55
- # Required fields
56
- option :systemId, type: :numeric, required: true,
57
- desc: 'A numeric value representing the system identification'
58
- option :cci, type: :string, required: true, desc: 'The system CCI string numerical value'
59
- option :testedBy, type: :string, required: true, desc: 'The person that conducted the test (Last Name, First)'
60
- option :testDate, type: :numeric, required: true, desc: 'The date test was conducted, Unix time format.'
61
- option :description, type: :string, required: true, desc: 'The description of test result. 4000 Characters.'
62
- option :complianceStatus, type: :string, required: true, enum: ['Compliant', 'Non-Compliant', 'Not Applicable']
63
-
64
- def add
65
- body = EmassClient::TestResultsGet.new
66
- body.cci = options[:cci]
67
- body.tested_by = options[:testedBy]
68
- body.test_date = options[:testDate]
69
- body.description = options[:description]
70
- body.compliance_status = options[:complianceStatus]
71
-
72
- body_array = Array.new(1, body)
73
-
74
- begin
75
- result = EmassClient::TestResultsApi
76
- .new.add_test_results_by_system_id(options[:systemId], body_array)
77
- puts to_output_hash(result).green
78
- rescue EmassClient::ApiError => e
79
- puts 'Exception when calling TestResultsApi->add_test_results_by_system_id'.red
80
- puts to_output_hash(e)
81
- end
82
- end
83
- end
84
-
85
- # The POA&M endpoints provide the ability to add Plan of Action and Milestones (POA&M)
86
- # items to a system.
87
- #
88
- # Endpoint:
89
- # /api/systems/{systemId}/poams - Add one or many poa&m items in a system
90
- class Poams < SubCommandBase
91
- def self.exit_on_failure?
92
- true
93
- end
94
-
95
- # POAM --------------------------------------------------------------------
96
- #
97
- # The following fields are required based on the contents of the status field
98
- # status Required Fields
99
- # -------------------------------------------------------------------------
100
- # Risk Accepted comments, resources
101
- # Ongoing scheduledCompletionDate, resources, milestones (at least 1)
102
- # Completed scheduledCompletionDate, comments, resources,
103
- # completionDate, milestones (at least 1)
104
- # Not Applicable POAM can not be created
105
- #--------------------------------------------------------------------------
106
- #
107
- # If a POC email is supplied, the application will attempt to locate a user
108
- # already registered within the application and pre-populate any information
109
- # not explicitly supplied in the request. If no such user is found, these
110
- # fields are required within the request:
111
- # pocFirstName, pocLastName, pocPhoneNumber
112
-
113
- desc 'add', 'Add one or many POA&M items in a system'
114
- long_desc Help.text(:poam_post_mapper)
115
-
116
- # Required parameters/fields (the poamId and displayPoamId are generated by the PUT call)
117
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
118
- option :status, type: :string, required: true, enum: ['Ongoing', 'Risk Accepted', 'Completed', 'Not Applicable']
119
- option :vulnerabilityDescription, type: :string, required: true, desc: 'POA&M vulnerability description'
120
- option :sourceIdentVuln,
121
- type: :string, required: true, desc: 'Source that identifies the vulnerability'
122
- option :pocOrganization, type: :string, required: true, desc: 'Organization/Office represented'
123
- option :resources, type: :string, required: true, desc: 'List of resources used'
124
-
125
- # Conditional parameters/fields
126
- option :milestone, type: :hash, required: false, desc: 'key:values are: description and scheduledCompletionDate'
127
- option :pocFirstName, type: :string, required: false, desc: 'First name of POC'
128
- option :pocLastName, type: :string, required: false, desc: 'Last name of POC.'
129
- option :pocEmail, type: :string, required: false, desc: 'Email address of POC'
130
- option :pocPhoneNumber, type: :string, required: false, desc: 'Phone number of POC (area code) ***-**** format'
131
- option :severity, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
132
- option :scheduledCompletionDate,
133
- type: :numeric, required: false, desc: 'The scheduled completion date - Unix time format'
134
- option :completionDate,
135
- type: :numeric, required: false, desc: 'The schedule completion date - Unix time format'
136
- option :comments, type: :string, required: false, desc: 'Comments for completed and risk accepted POA&M items'
137
-
138
- # Optional parameters/fields
139
- option :externalUid, type: :string, required: false, desc: 'External ID associated with the POA&M'
140
- option :controlAcronym, type: :string, required: false, desc: 'The system acronym(s) e.g "AC-1, AC-2"'
141
- option :cci, type: :string, required: false, desc: 'The system CCIS string numerical value'
142
- option :securityChecks, type: :string, required: false, desc: 'Security Checks that are associated with the POA&M'
143
- option :rawSeverity, type: :string, required: false, enum: %w[I II III]
144
- option :relevanceOfThreat,
145
- type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
146
- option :likelihood, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
147
- option :impact, type: :string, required: false, desc: 'Description of Security Control’s impact'
148
- option :impactDescription, type: :string, required: false, desc: 'Description of the security control impact'
149
- option :residualRiskLevel,
150
- type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
151
- option :recommendations, type: :string, required: false, desc: 'Recomendations'
152
- option :mitigation, type: :string, required: false, desc: 'Mitigation explanation'
153
-
154
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
155
- def add
156
- # Required fields
157
- body = EmassClient::PoamGet.new
158
- body.status = options[:status]
159
- body.vulnerability_description = options[:vulnerabilityDescription]
160
- body.source_ident_vuln = options[:sourceIdentVuln]
161
- body.poc_organization = options[:pocOrganization]
162
- body.resources = options[:resources]
163
-
164
- process_business_logic(body)
165
-
166
- # Add conditional fields
167
- body.poc_first_name = options[:pocFirstName] if options[:pocFirstName]
168
- body.poc_last_name = options[:pocLastName] if options[:pocLastName]
169
- body.poc_email = options[:pocEmail] if options[:pocEmail]
170
- body.poc_phone_number = options[:pocPhoneNumber] if options[:pocPhoneNumber]
171
- body.severity = options[:severity] if options[:severity]
172
-
173
- # Add optional fields
174
- body.external_uid = options[:externalUid] if options[:externalUid]
175
- body.control_acronyms = options[:controlAcronym] if options[:controlAcronym]
176
- body.cci = options[:cci] if options[:cci]
177
- body.security_checks = options[:securityChecks] if options[:securityChecks]
178
- body.raw_severity = options[:rawSeverity] if options[:rawSeverity]
179
- body.relevance_of_threat = options[:relevanceOfThreat] if options[:relevanceOfThreat]
180
- body.likelihood = options[:likelihood] if options[:likelihood]
181
- body.impact = options[:impact] if options[:impact]
182
- body.impact_description = options[:impactDescription] if options[:impactDescription]
183
- body.residual_risk_level = options[:residualRiskLevel] if options[:residualRiskLevel]
184
- body.recommendations = options[:recommendations] if options[:recommendations]
185
- body.mitigation = options[:mitigation] if options[:mitigation]
186
-
187
- body_array = Array.new(1, body)
188
-
189
- begin
190
- result = EmassClient::POAMApi.new.add_poam_by_system_id(options[:systemId], body_array)
191
- puts to_output_hash(result).green
192
- rescue EmassClient::ApiError => e
193
- puts 'Exception when calling POAMApi->add_poam_by_system_id'.red
194
- puts to_output_hash(e)
195
- end
196
- end
197
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
198
-
199
- # rubocop:disable Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
200
- no_commands do
201
- def process_business_logic(body)
202
- #-----------------------------------------------------------------------------
203
- # Conditional fields based on the status field values
204
- # "Risk Accepted" comments, resources
205
- # "Ongoing" scheduledCompletionDate, resources, milestones (at least 1)
206
- # "Completed" scheduledCompletionDate, comments, resources,
207
- # completionDate, milestones (at least 1)
208
- # "Not Applicable" POAM can not be created
209
- #-----------------------------------------------------------------------------
210
- # rubocop:disable Style/CaseLikeIf, Style/StringLiterals
211
- if options[:status] == "Risk Accepted"
212
- if options[:comments].nil?
213
- puts 'When status = "Risk Accepted" the following parameters/fields are required:'.red
214
- puts ' comments'.red
215
- puts POAMS_POST_HELP_MESSAGE.yellow
216
- exit
217
- elsif !(options[:scheduledCompletionDate].nil? && options[:milestone].nil?)
218
- puts 'When status = "Risk Accepted" POA&M Item CAN NOT be saved with the following parameters/fields:'.red
219
- puts ' scheduledCompletionDate, or milestone'.red
220
- puts POAMS_PUT_HELP_MESSAGE.yellow
221
- exit
222
- else
223
- body.comments = options[:comments]
224
- end
225
- elsif options[:status] == "Ongoing"
226
- if options[:scheduledCompletionDate].nil? || options[:milestone].nil?
227
- puts 'When status = "Ongoing" the following parameters/fields are required:'.red
228
- puts ' scheduledCompletionDate, milestone'.red
229
- print_milestone_help
230
- puts POAMS_POST_HELP_MESSAGE.yellow
231
- exit
232
- elsif options[:milestone]["description"].nil? || options[:milestone]["scheduledCompletionDate"].nil?
233
- puts 'Missing milstone parameters/fields'.red
234
- print_milestone_help
235
- exit
236
- else
237
- body.scheduled_completion_date = options[:scheduledCompletionDate]
238
-
239
- milestone = EmassClient::MilestonesRequiredPost.new
240
- milestone.description = options[:milestone]["description"]
241
- milestone.scheduled_completion_date = options[:milestone]["scheduledCompletionDate"]
242
- milestone_array = Array.new(1, milestone)
243
- body.milestones = milestone_array
244
- end
245
- elsif options[:status] == "Completed"
246
- if options[:scheduledCompletionDate].nil? || options[:comments].nil? ||
247
- options[:completionDate].nil? || options[:milestone].nil?
248
- puts 'When status = "Completed" the following parameters/fields are required:'.red
249
- puts ' scheduledCompletionDate, comments, completionDate, or milestone'.red
250
- print_milestone_help
251
- puts POAMS_POST_HELP_MESSAGE.yellow
252
- exit
253
- else
254
- body.scheduled_completion_date = options[:scheduledCompletionDate]
255
- body.comments = options[:comments]
256
- body.completion_date = options[:completionDate]
257
-
258
- milestone = EmassClient::MilestonesRequiredPost.new
259
- milestone.description = options[:milestone]["description"]
260
- milestone.scheduled_completion_date = options[:milestone]["scheduledCompletionDate"]
261
- milestone_array = Array.new(1, milestone)
262
- body.milestones = milestone_array
263
- end
264
- end
265
-
266
- # POC checks: If any poc information is provided all POC fields are required
267
- if options[:pocFirstName]
268
- if options[:pocLastName].nil? || options[:pocEmail].nil? || options[:pocPhoneNumber].nil?
269
- puts 'If a POC first name is given, then all POC information must be entered:'.red
270
- puts ' pocLastName, pocEmail, pocPhoneNumber'.red
271
- puts POAMS_POST_HELP_MESSAGE.yellow
272
- exit
273
- end
274
- elsif options[:pocLastName]
275
- if options[:pocFirstName].nil? || options[:pocEmail].nil? || options[:pocPhoneNumber].nil?
276
- puts 'If a POC last name is given, then all POC information must be entered:'.red
277
- puts ' pocFirstName, pocEmail, pocPhoneNumber'.red
278
- puts POAMS_POST_HELP_MESSAGE.yellow
279
- exit
280
- end
281
- elsif options[:pocEmail]
282
- if options[:pocFirstName].nil? || options[:pocLastName].nil? || options[:pocPhoneNumber].nil?
283
- puts 'If a POC email is given, then all POC information must be entered:'.red
284
- puts ' pocFirstName, pocLastName, pocPhoneNumber'.red
285
- puts POAMS_POST_HELP_MESSAGE.yellow
286
- exit
287
- end
288
- elsif options[:pocPhoneNumber]
289
- if options[:pocFirstName].nil? || options[:pocLastName].nil? || options[:pocEmail].nil?
290
- puts 'If a POC phone number is given, then all POC information must be entered:'.red
291
- puts ' pocFirstName, pocLastName, pocEmail'.red
292
- puts POAMS_POST_HELP_MESSAGE.yellow
293
- exit
294
- end
295
- end
296
- # rubocop:enable Style/CaseLikeIf, Style/StringLiterals
297
- end
298
-
299
- def print_milestone_help
300
- puts 'Milestone format is:'.yellow
301
- puts ' --milestone description:"[value]" scheduledCompletionDate:"[value]"'.yellow
302
- end
303
- end
304
- # rubocop:enable Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
305
- end
306
-
307
- # The Milestones endpoints provide the ability add milestones that are associated with
308
- # Plan of Action and Milestones (POA&M) items for a system.
309
- #
310
- # Endpoint:
311
- # /api/systems/{systemId}/poams/{poamId}/milestones - Add milestones in one or many poa&m items in a system
312
- class Milestones < SubCommandBase
313
- def self.exit_on_failure?
314
- true
315
- end
316
-
317
- desc 'add', 'Add milestones to one or many POA&M items in a system'
318
- long_desc Help.text(:milestone_post_mapper)
319
-
320
- # Required parameters/fields
321
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
322
- option :poamId, type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
323
- option :description, type: :string, required: true, desc: 'The milestone description'
324
- option :scheduledCompletionDate,
325
- type: :numeric, required: true, desc: 'The scheduled completion date - Unix time format'
326
-
327
- def add
328
- body = EmassClient::MilestonesGet.new
329
- body.description = options[:description]
330
- body.scheduled_completion_date = options[:scheduledCompletionDate]
331
- body_array = Array.new(1, body)
332
-
333
- begin
334
- result = EmassClient::MilestonesApi
335
- .new.add_milestone_by_system_id_and_poam_id(options[:systemId], options[:poamId], body_array)
336
- puts to_output_hash(result).green
337
- rescue EmassClient::ApiError => e
338
- puts 'Exception when calling MilestonesApi->add_milestone_by_system_id_and_poam_id'.red
339
- puts to_output_hash(e)
340
- end
341
- end
342
- end
343
-
344
- # Add one or many artifacts for a system (delivery method must be a zip file)
345
- #
346
- # Endpoints:
347
- # /api/systems/{systemId}/artifacts - Post one or many artifacts to a system
348
- class Artifacts < SubCommandBase
349
- def self.exit_on_failure?
350
- true
351
- end
352
-
353
- desc 'upload SYSTEM_ID FILE [FILE ...]', 'Uploads [FILES] to the given [SYSTEM_ID] as artifacts'
354
- long_desc Help.text(:artifacts_post_mapper)
355
-
356
- # Required parameters/fields
357
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
358
- option :files, type: :array, required: true, desc: 'Artifact file(s) to post to the given system'
359
- option :type,
360
- type: :string, required: true,
361
- enum: ['Procedure', 'Diagram', 'Policy', 'Labor', 'Document',
362
- 'Image', 'Other', 'Scan Result', 'Auditor Report']
363
- option :category, type: :string, required: true, enum: ['Implementation Guidance', 'Evidence']
364
- option :isTemplate, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false.'
365
-
366
- # Optional parameters/fields
367
- option :description, type: :string, required: false, desc: 'Artifact description'
368
- option :refPageNumber, type: :string, required: false, desc: 'Artifact reference page number'
369
- option :ccis, type: :string, required: false, desc: 'The system CCIs string numerical value'
370
- option :controls,
371
- type: :string, required: false,
372
- desc: 'Control acronym associated with the artifact. NIST SP 800-53 Revision 4 defined'
373
- option :artifactExpirationDate,
374
- type: :numeric, required: false, desc: 'Date Artifact expires and requires review - Unix time format'
375
- option :lastReviewedDate,
376
- type: :numeric, required: false, desc: 'Date Artifact was last reviewed - Unix time format'
377
-
378
- def upload
379
- optional_options_keys = optional_options(@_initializer).keys
380
- optional_options = to_input_hash(optional_options_keys, options)
381
- # Remove the isTemplate as we can't use the required = true.
382
- optional_options.delete(:is_template)
383
-
384
- opts = {}
385
- opts[:type] = options[:type]
386
- opts[:category] = options[:category]
387
- opts[:is_template] = options[:is_template]
388
- opts[:form_params] = optional_options
389
-
390
- tempfile = Tempfile.create(['artifacts', '.zip'])
391
-
392
- Zip::OutputStream.open(tempfile.path) do |z|
393
- options[:files].each do |file|
394
- # Add file name to the archive: Don't use the full path
395
- z.put_next_entry(File.basename(file))
396
- # Add the file to the archive
397
- z.print File.read(file)
398
- end
399
- end
400
-
401
- begin
402
- result = EmassClient::ArtifactsApi
403
- .new
404
- .add_artifacts_by_system_id(options[:systemId], tempfile, opts)
405
- puts to_output_hash(result).green
406
- rescue EmassClient::ApiError => e
407
- puts 'Exception when calling ArtifactsApi->add_artifacts_by_system_id'.red
408
- puts to_output_hash(e)
409
- ensure
410
- # Delete the temp file
411
- unless File.exist? tempfile
412
- tempfile.close
413
- FileUtils.remove_file(tempfile, true)
414
- end
415
- end
416
- end
417
- end
418
-
419
- # Add a Control Approval Chain (CAC)
420
- #
421
- # Endpoints:
422
- # /api/systems/{systemId}/approval/cac - Submit control to second stage of CAC
423
- class CAC < SubCommandBase
424
- def self.exit_on_failure?
425
- true
426
- end
427
-
428
- desc 'add', 'Submit control to second stage of CAC'
429
- long_desc Help.text(:approvalCac_post_mapper)
430
-
431
- # Required parameters/fields
432
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
433
- option :controlAcronym, type: :string, required: true, desc: 'The system acronym "AC-1, AC-2"'
434
-
435
- # Conditional parameters/fields
436
- option :comments, type: :string, required: false, desc: 'The control approval chain comments'
437
-
438
- def add
439
- body = EmassClient::CacGet.new
440
- body.control_acronym = options[:controlAcronym]
441
- body.comments = options[:comments]
442
-
443
- body_array = Array.new(1, body)
444
-
445
- begin
446
- # Get location of one or many controls in CAC
447
- result = EmassClient::CACApi.new.add_system_cac(options[:systemId], body_array)
448
- puts to_output_hash(result).green
449
- rescue EmassClient::ApiError => e
450
- puts 'Exception when calling ApprovalChainApi->add_system_cac'.red
451
- puts to_output_hash(e)
452
- end
453
- end
454
- end
455
-
456
- # Add a Package Approval Chain (PAC)
457
- #
458
- # Endpoints:
459
- # /api/systems/{systemId}/approval/pac - Initiate system workflow for review
460
- class PAC < SubCommandBase
461
- def self.exit_on_failure?
462
- true
463
- end
464
-
465
- desc 'add', 'Initiate system workflow for review'
466
- long_desc Help.text(:approvalPac_post_mapper)
467
-
468
- # Required parameters/fields
469
- option :systemId, type: :numeric, required: true,
470
- desc: 'A numeric value representing the system identification'
471
- option :workflow, type: :string, required: true,
472
- enum: ['Assess and Authorize', 'Assess Only', 'Security Plan Approval']
473
- option :name, type: :string, required: true, desc: 'The control package name'
474
- option :comments, type: :string, required: true,
475
- desc: 'Comments submitted upon initiation of the indicated workflow'
476
-
477
- def add
478
- body = EmassClient::PacGet.new
479
- body.name = options[:name]
480
- body.workflow = options[:workflow]
481
- body.comments = options[:comments]
482
-
483
- body_array = Array.new(1, body)
484
-
485
- result = EmassClient::PACApi.new.add_system_pac(options[:systemId], body_array)
486
- puts to_output_hash(result).green
487
- rescue EmassClient::ApiError => e
488
- puts 'Exception when calling ApprovalChainApi->add_system_pac'.red
489
- puts to_output_hash(e)
490
- end
491
- end
492
-
493
- # The Static Code Scans endpoint provides the ability to upload application
494
- # scan findings into a system's assets module.
495
- #
496
- # Application findings can also be cleared from the system.
497
- #
498
- # Endpoint:
499
- # /api/systems/{systemId}/static-code-scans - Upload static code scans
500
- class ScanFindings < SubCommandBase
501
- def self.exit_on_failure?
502
- true
503
- end
504
-
505
- desc 'add', 'Upload static code scans'
506
- long_desc Help.text(:staticcode_post_mapper)
507
-
508
- # Required parameters/fields
509
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
510
- option :applicationName, type: :string, required: true, desc: 'Name of the software application that was assessed'
511
- option :version, type: :string, required: true, desc: 'The version of the application'
512
- option :codeCheckName, type: :string, required: true, desc: 'Name of the software vulnerability or weakness'
513
- option :scanDate, type: :numeric, required: true, desc: 'The findings scan date - Unix time format'
514
- option :cweId, type: :string, required: true, desc: 'The Common Weakness Enumerator (CWE) identifier'
515
- option :count, type: :numeric, required: true, desc: 'Number of instances observed for a specified finding'
516
- # Optional parameter/fields
517
- option :rawSeverity, type: :string, required: false, enum: %w[Low Medium Moderate High Critical]
518
-
519
- def add
520
- application = EmassClient::StaticCodeRequestPostBodyApplication.new
521
- application.application_name = options[:applicationName]
522
- application.version = options[:version]
523
-
524
- application_findings = EmassClient::StaticCodeApplication.new
525
- application_findings.code_check_name = options[:codeCheckName]
526
- application_findings.scan_date = options[:scanDate]
527
- application_findings.cwe_id = options[:cweId]
528
- application_findings.count = options[:count]
529
- application_findings.raw_severity = options[:rawSeverity] if options[:rawSeverity]
530
-
531
- app_findings_array = Array.new(1, application_findings)
532
-
533
- body = EmassClient::StaticCodeRequestPostBody.new
534
- body.application = application
535
- body.application_findings = app_findings_array
536
-
537
- body_array = Array.new(1, body)
538
-
539
- begin
540
- result = EmassClient::StaticCodeScansApi
541
- .new.add_static_code_scans_by_system_id(options[:systemId], body_array)
542
- puts to_output_hash(result).green
543
- rescue EmassClient::ApiError => e
544
- puts 'Exception when calling StaticCodeScansApi->add_static_code_scans_by_system_id'.red
545
- puts to_output_hash(e)
546
- end
547
- end
548
-
549
- # CLEAR ------------------------------------------------------------------------------------
550
- desc 'clear', 'Clear an application findings'
551
- long_desc Help.text(:staticcode_clear_mapper)
552
-
553
- # Required parameters/fields
554
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
555
- option :applicationName, type: :string, required: true, desc: 'Name of the software application that was assessed'
556
- option :version, type: :string, required: true, desc: 'The version of the application'
557
- option :clearFindings, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false'
558
- # NOTE: clearFindings is a required parameter to clear an application's findings, however Thor does not allow
559
- # a boolean type to be required because it automatically creates a --no-clearFindings option for clearFindings=false
560
-
561
- def clear
562
- unless options[:clearFindings]
563
- puts 'To clear an application findings, the field clearFindings (--clearFindings) is required'.red
564
- puts SCAN_POST_HELP_MESSAGE.yellow
565
- exit
566
- end
567
-
568
- application = EmassClient::StaticCodeRequestPostBodyApplication.new
569
- application.application_name = options[:applicationName]
570
- application.version = options[:version]
571
-
572
- application_findings = EmassClient::StaticCodeApplication.new
573
- application_findings.clear_findings = options[:clearFindings]
574
-
575
- app_findings_array = Array.new(1, application_findings)
576
-
577
- body = EmassClient::StaticCodeRequestPostBody.new
578
- body.application = application
579
- body.application_findings = app_findings_array
580
-
581
- body_array = Array.new(1, body)
582
-
583
- begin
584
- result = EmassClient::StaticCodeScansApi
585
- .new.add_static_code_scans_by_system_id(options[:systemId], body_array)
586
- puts to_output_hash(result).green
587
- rescue EmassClient::ApiError => e
588
- puts 'Exception when calling StaticCodeScansApi->add_static_code_scans_by_system_id'.red
589
- puts to_output_hash(e)
590
- end
591
- end
592
- end
593
-
594
- # The Cloud Resources endpoint provides the ability to upload (add)
595
- # cloud resources and their scan results in the assets module for a system.
596
- #
597
- #
598
- # Endpoint:
599
- # /api/systems/{systemId}/cloud-resources-results - Upload cloud resources and their scan results
600
- class CloudResource < SubCommandBase
601
- def self.exit_on_failure?
602
- true
603
- end
604
-
605
- desc 'add', 'Upload cloud resources and their scan results'
606
- long_desc Help.text(:cloudresource_post_mapper)
607
-
608
- # Required parameters/fields
609
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
610
- option :provider, type: :string, required: true, desc: 'Cloud service provider name'
611
- option :resourceId, type: :string, required: true, desc: 'Unique identifier/resource namespace for policy compliance result'
612
- option :resourceName, type: :string, required: true, desc: 'Friendly name of Cloud resource'
613
- option :resourceType, type: :string, required: true, desc: 'Type of Cloud resource'
614
- # ComplianceResults Array Objects
615
- option :cspPolicyDefinitionId, type: :string, required: true, desc: 'Unique identifier/compliance namespace for CSP/Resource\'s policy definition/compliance check'
616
- option :isCompliant, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false'
617
- option :policyDefinitionTitle, type: :string, required: true, desc: 'Friendly policy/compliance check title. Recommend short title'
618
-
619
- # Optional parameter/fields
620
- option :initiatedBy, type: :string, required: false, desc: 'Email of POC'
621
- option :cspAccountId, type: :string, required: false, desc: 'System/owner\'s CSP account ID/number'
622
- option :cspRegion, type: :string, required: false, desc: 'CSP region of system'
623
- option :isBaseline, type: :boolean, required: false, default: true, desc: 'BOOLEAN - true or false'
624
- # Tags Object
625
- option :test, type: :string, required: false, desc: 'The test tag'
626
- # ComplianceResults Array Objects
627
- option :assessmentProcedure, type: :string, required: false, desc: 'Comma separated correlation to Assessment Procedure (i.e. CCI number for DoD Control Set)'
628
- option :complianceCheckTimestamp, type: :numeric, required: false, desc: 'The compliance timestamp Unix date format.'
629
- option :complianceReason, type: :string, required: false, desc: 'Reason/comments for compliance result'
630
- option :control, type: :string, required: false, desc: 'Comma separated correlation to Security Control (e.g. exact NIST Control acronym)'
631
- option :policyDeploymentName, type: :string, required: false, desc: 'Name of policy deployment'
632
- option :policyDeploymentVersion, type: :string, required: false, desc: 'policyDeploymentVersion'
633
- option :severity, type: :string, required: false, enum: %w[Low Medium Moderate High Critical]
634
-
635
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
636
- def add
637
- # Required and Optional main fields
638
- body = {}
639
- body[:provider] = options[:provider]
640
- body[:resourceId] = options[:resourceId]
641
- body[:resourceName] = options[:resourceName]
642
- body[:resourceType] = options[:resourceType]
643
-
644
- body[:initiatedBy] = options[:initiatedBy] if options[:initiatedBy]
645
- body[:cspAccountId] = options[:cspAccountId] if options[:cspAccountId]
646
- body[:cspRegion] = options[:cspRegion] if options[:cspRegion]
647
- body[:isBaseline] = options[:isBaseline] if options[:isBaseline]
648
-
649
- # Optional tags field
650
- tags = {}
651
- tags[:test] = options[:test] if options[:test]
652
-
653
- # Required and Optional compliances results fields
654
- compliance_results = {}
655
- compliance_results[:cspPolicyDefinitionId] = options[:cspPolicyDefinitionId]
656
- compliance_results[:isCompliant] = options[:isCompliant]
657
- compliance_results[:policyDefinitionTitle] = options[:policyDefinitionTitle]
658
- # Optional fields
659
- compliance_results[:assessmentProcedure] = options[:assessmentProcedure] if options[:assessmentProcedure]
660
- compliance_results[:complianceCheckTimestamp] = options[:complianceCheckTimestamp] if options[:complianceCheckTimestamp]
661
- compliance_results[:complianceReason] = options[:complianceReason] if options[:complianceReason]
662
- compliance_results[:control] = options[:control] if options[:control]
663
- compliance_results[:policyDeploymentName] = options[:policyDeploymentName] if options[:policyDeploymentName]
664
- compliance_results[:policyDeploymentVersion] = options[:policyDeploymentVersion] if options[:policyDeploymentVersion]
665
- compliance_results[:severity] = options[:severity] if options[:severity]
666
-
667
- compliance_results_array = Array.new(1, compliance_results)
668
-
669
- body[:tags] = tags
670
- body[:complianceResults] = compliance_results_array
671
-
672
- body_array = Array.new(1, body)
673
-
674
- begin
675
- result = EmassClient::CloudResourcesApi
676
- .new.add_cloud_resources_by_system_id(options[:systemId], body_array)
677
- puts to_output_hash(result).green
678
- rescue EmassClient::ApiError => e
679
- puts 'Exception when calling StaticCodeScansApi->add_cloud_resources_by_system_id'.red
680
- puts to_output_hash(e)
681
- end
682
- end
683
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
684
- end
685
-
686
- # The Containers endpoint provides the ability to upload (add)
687
- # containers and their scan results in the assets module for a system.
688
- #
689
- #
690
- # Endpoint:
691
- # /api/systems/{systemId}/container-scan-results - Upload containers and their scan results
692
- class Container < SubCommandBase
693
- def self.exit_on_failure?
694
- true
695
- end
696
-
697
- desc 'add', 'Upload containers and their scan results'
698
- long_desc Help.text(:container_post_mapper)
699
-
700
- # Required parameters/fields
701
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
702
- option :containerId, type: :string, required: true, desc: 'Unique identifier of the container'
703
- option :containerName, type: :string, required: true, desc: 'Friendly name of the container'
704
- option :time, type: :numeric, required: true, desc: 'Datetime of scan/result. Unix date format'
705
- # Benchmarks Array Objects
706
- option :benchmark, type: :string, required: true, desc: 'Identifier of the benchmark/grouping of compliance results'
707
- # Benchmarks.Results Array Objects
708
- option :lastSeen, type: :numeric, required: true, desc: 'Date last seen, Unix date format'
709
- option :ruleId, type: :string, required: true, desc: 'Identifier for the compliance result, vulnerability, etc. the result is for'
710
- option :status, type: :string, required: true, enum: ['Pass', 'Fail', 'Other', 'Not Reviewed', 'Not Checked', 'Not Applicable']
711
-
712
- # Optional parameter/fields
713
- option :namespace, type: :string, required: false, desc: 'Namespace of container in container orchestration'
714
- option :podIp, type: :string, required: false, desc: 'IP address of the pod'
715
- option :podName, type: :string, required: false, desc: 'Name of pod (e.g. Kubernetes pod)'
716
- # Tags Object
717
- option :test, type: :string, required: false, desc: 'The test tag'
718
- # Benchmarks Array Objects
719
- option :isBaseline, type: :boolean, required: false, default: true, desc: 'BOOLEAN - true or false'
720
- # Benchmarks.Results Array Objects
721
- option :message, type: :string, required: false, desc: 'Benchmark result comments'
722
-
723
- # rubocop:disable Metrics/CyclomaticComplexity
724
- def add
725
- # Required and Optional main fields
726
- body = {}
727
- body[:containerId] = options[:containerId]
728
- body[:containerName] = options[:containerName]
729
- body[:time] = options[:time]
730
- body[:namespace] = options[:namespace] if options[:namespace]
731
- body[:podIp] = options[:podIp] if options[:podIp]
732
- body[:podName] = options[:podName] if options[:podName]
733
-
734
- # Optional tags field
735
- tags = {}
736
- tags[:test] = options[:test] if options[:test]
737
-
738
- # Required and Optional Benchmarks fields
739
- benchmarks = {}
740
- benchmarks[:benchmark] = options[:benchmark]
741
- # Optional fields
742
- benchmarks[:isBaseline] = options[:isBaseline] if options[:isBaseline]
743
-
744
- # Required and Optional Benchmarks.Results
745
- benchmarks_results = {}
746
- benchmarks_results[:lastSeen] = options[:lastSeen]
747
- benchmarks_results[:ruleId] = options[:ruleId]
748
- benchmarks_results[:status] = options[:status]
749
- benchmarks_results[:message] = options[:message] if options[:message]
750
-
751
- # Add Benchmark results to an array and add array to benchmarks object
752
- benchmarks_results_array = Array.new(1, benchmarks_results)
753
- benchmarks[:results] = benchmarks_results_array # = Array.new(1, benchmarks_results)
754
- # Add benchmarks object to an array
755
- benchmarks_array = Array.new(1, benchmarks)
756
- # Add tags and benchmark ojects to body object
757
- body[:tags] = tags
758
- body[:benchmarks] = benchmarks_array
759
-
760
- body_array = Array.new(1, body)
761
-
762
- begin
763
- result = EmassClient::ContainersApi
764
- .new.add_container_sans_by_system_id(options[:systemId], body_array)
765
- puts to_output_hash(result).green
766
- rescue EmassClient::ApiError => e
767
- puts 'Exception when calling StaticCodeScansApi->add_container_sans_by_system_id'.red
768
- puts to_output_hash(e)
769
- end
770
- end
771
- # rubocop:enable Metrics/CyclomaticComplexity
772
- end
773
-
774
- class Post < SubCommandBase
775
- desc 'test_results', 'Add system Test Results'
776
- subcommand 'test_results', TestResults
777
-
778
- desc 'poams', 'Add Plan of Action and Milestones (POA&M) items to a system'
779
- subcommand 'poams', Poams
780
-
781
- desc 'milestones', 'Add milestone(s) to one or many POA&M items in a system'
782
- subcommand 'milestones', Milestones
783
-
784
- desc 'artifacts', 'Add system Artifacts'
785
- subcommand 'artifacts', Artifacts
786
-
787
- desc 'cac', 'Add Control Approval Chain (CAC) security content'
788
- subcommand 'cac', CAC
789
-
790
- desc 'pac', 'Add Package Approval Chain (PAC) security content'
791
- subcommand 'pac', PAC
792
-
793
- desc 'scan_findings', 'Upload static code scans'
794
- subcommand 'scan_findings', ScanFindings
795
-
796
- desc 'cloud_resource', 'Upload cloud resource and their scan results'
797
- subcommand 'cloud_resource', CloudResource
798
-
799
- desc 'container', 'Upload container and their scan results'
800
- subcommand 'container', Container
801
- end
802
- end
1
+ # frozen_string_literal: true
2
+
3
+ # Hack class that properly formats the CLI help
4
+ class SubCommandBase < Thor
5
+ include OptionsParser
6
+ include InputConverters
7
+ include OutputConverters
8
+
9
+ # We do not control the method declaration for the banner
10
+
11
+ # rubocop:disable Style/OptionalBooleanParameter
12
+ def self.banner(command, _namespace = nil, subcommand = false)
13
+ # Use the $thor_runner (declared by the Thor CLI framework)
14
+ # to properly format the help text of sub-sub-commands.
15
+
16
+ # rubocop:disable Style/GlobalVars
17
+ if ancestors[0].to_s.include? '::Post'
18
+ "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
19
+ else
20
+ "#{basename} post #{command.formatted_usage(self, $thor_runner, subcommand)}"
21
+ end
22
+ # rubocop:enable Style/GlobalVars
23
+ end
24
+ # rubocop:enable Style/OptionalBooleanParameter
25
+ end
26
+
27
+ # Override thor's long_desc identation behavior
28
+ class Thor
29
+ module Shell
30
+ class Basic
31
+ def print_wrapped(message, _options = {})
32
+ message = "\n#{message}\n" unless message[0] == "\n"
33
+ stdout.puts message
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ module Emasser
40
+ # Common static messages
41
+ POAMS_POST_HELP_MESSAGE = "\nInvoke \"emasser post poams help add\" for additional help"
42
+ SCAN_POST_HELP_MESSAGE = "\nInvoke \"emasser post scan_findings help clear\" for additional help"
43
+
44
+ # The Registration endpoint provides the ability to register a certificate & obtain an API-key.
45
+ #
46
+ # Endpoint:
47
+ # /api/api-key - Register certificate and obtain API key
48
+ class Register < SubCommandBase
49
+ def self.exit_on_failure?
50
+ true
51
+ end
52
+
53
+ desc 'cert', 'Register a certificate & obtain an API-key'
54
+ # rubocop:disable Style/RedundantBegin
55
+ def cert
56
+ begin
57
+ result = EmassClient::RegistrationApi.new.register_user({})
58
+ puts to_output_hash(result).green
59
+ rescue EmassClient::ApiError => e
60
+ puts 'Exception when calling RegistrationApi->register_user'.red
61
+ puts to_output_hash(e)
62
+ end
63
+ end
64
+ # rubocop:enable Style/RedundantBegin
65
+ end
66
+
67
+ # The Test Results endpoints provide the ability to add test results for a
68
+ # system's Assessment Procedures (CCIs) which determine Security Control compliance.
69
+ #
70
+ # Endpoint:
71
+ # /api/systems/{systemId}/test-results - Add one or many test results for a system
72
+ class TestResults < SubCommandBase
73
+ def self.exit_on_failure?
74
+ true
75
+ end
76
+
77
+ desc 'add', 'Post a test result for a system'
78
+ long_desc Help.text(:testresults_post_mapper)
79
+
80
+ # Required fields
81
+ option :systemId, type: :numeric, required: true,
82
+ desc: 'A numeric value representing the system identification'
83
+ option :cci, type: :string, required: true, desc: 'The system CCI string numerical value'
84
+ option :testedBy, type: :string, required: true, desc: 'The person that conducted the test (Last Name, First)'
85
+ option :testDate, type: :numeric, required: true, desc: 'The date test was conducted, Unix time format.'
86
+ option :description, type: :string, required: true, desc: 'The description of test result. 4000 Characters.'
87
+ option :complianceStatus, type: :string, required: true, enum: ['Compliant', 'Non-Compliant', 'Not Applicable']
88
+
89
+ def add
90
+ body = EmassClient::TestResultsGet.new
91
+ body.cci = options[:cci]
92
+ body.tested_by = options[:testedBy]
93
+ body.test_date = options[:testDate]
94
+ body.description = options[:description]
95
+ body.compliance_status = options[:complianceStatus]
96
+
97
+ body_array = Array.new(1, body)
98
+
99
+ begin
100
+ result = EmassClient::TestResultsApi
101
+ .new.add_test_results_by_system_id(options[:systemId], body_array)
102
+ puts to_output_hash(result).green
103
+ rescue EmassClient::ApiError => e
104
+ puts 'Exception when calling TestResultsApi->add_test_results_by_system_id'.red
105
+ puts to_output_hash(e)
106
+ end
107
+ end
108
+ end
109
+
110
+ # The POA&M endpoints provide the ability to add Plan of Action and Milestones (POA&M)
111
+ # items to a system.
112
+ #
113
+ # Endpoint:
114
+ # /api/systems/{systemId}/poams - Add one or many poa&m items in a system
115
+ class Poams < SubCommandBase
116
+ def self.exit_on_failure?
117
+ true
118
+ end
119
+
120
+ # POAM --------------------------------------------------------------------
121
+ #
122
+ # The following fields are required based on the contents of the status field
123
+ # status Required Fields
124
+ # -------------------------------------------------------------------------
125
+ # Risk Accepted comments, resources
126
+ # Ongoing scheduledCompletionDate, resources, milestones (at least 1)
127
+ # Completed scheduledCompletionDate, comments, resources,
128
+ # completionDate, milestones (at least 1)
129
+ # Not Applicable POAM can not be created
130
+ #--------------------------------------------------------------------------
131
+ #
132
+ # If a POC email is supplied, the application will attempt to locate a user
133
+ # already registered within the application and pre-populate any information
134
+ # not explicitly supplied in the request. If no such user is found, these
135
+ # fields are required within the request:
136
+ # pocFirstName, pocLastName, pocPhoneNumber
137
+
138
+ desc 'add', 'Add one or many POA&M items in a system'
139
+ long_desc Help.text(:poam_post_mapper)
140
+
141
+ # Required parameters/fields (the poamId and displayPoamId are generated by the PUT call)
142
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
143
+ option :status, type: :string, required: true, enum: ['Ongoing', 'Risk Accepted', 'Completed', 'Not Applicable']
144
+ option :vulnerabilityDescription, type: :string, required: true, desc: 'POA&M vulnerability description'
145
+ option :sourceIdentVuln,
146
+ type: :string, required: true, desc: 'Source that identifies the vulnerability'
147
+ option :pocOrganization, type: :string, required: true, desc: 'Organization/Office represented'
148
+ option :resources, type: :string, required: true, desc: 'List of resources used'
149
+
150
+ # Conditional parameters/fields
151
+ option :milestone, type: :hash, required: false, desc: 'key:values are: description and scheduledCompletionDate'
152
+ option :pocFirstName, type: :string, required: false, desc: 'First name of POC'
153
+ option :pocLastName, type: :string, required: false, desc: 'Last name of POC.'
154
+ option :pocEmail, type: :string, required: false, desc: 'Email address of POC'
155
+ option :pocPhoneNumber, type: :string, required: false, desc: 'Phone number of POC (area code) ***-**** format'
156
+ option :severity, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
157
+ option :scheduledCompletionDate,
158
+ type: :numeric, required: false, desc: 'The scheduled completion date - Unix time format'
159
+ option :completionDate,
160
+ type: :numeric, required: false, desc: 'The schedule completion date - Unix time format'
161
+ option :comments, type: :string, required: false, desc: 'Comments for completed and risk accepted POA&M items'
162
+
163
+ # Optional parameters/fields
164
+ option :externalUid, type: :string, required: false, desc: 'External ID associated with the POA&M'
165
+ option :controlAcronym, type: :string, required: false, desc: 'The system acronym(s) e.g "AC-1, AC-2"'
166
+ option :cci, type: :string, required: false, desc: 'The system CCIS string numerical value'
167
+ option :securityChecks, type: :string, required: false, desc: 'Security Checks that are associated with the POA&M'
168
+ option :rawSeverity, type: :string, required: false, enum: %w[I II III]
169
+ option :relevanceOfThreat,
170
+ type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
171
+ option :likelihood, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
172
+ option :impact, type: :string, required: false, desc: 'Description of Security Control’s impact'
173
+ option :impactDescription, type: :string, required: false, desc: 'Description of the security control impact'
174
+ option :residualRiskLevel,
175
+ type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
176
+ option :recommendations, type: :string, required: false, desc: 'Recomendations'
177
+ option :mitigation, type: :string, required: false, desc: 'Mitigation explanation'
178
+
179
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
180
+ def add
181
+ # Required fields
182
+ body = EmassClient::PoamGet.new
183
+ body.status = options[:status]
184
+ body.vulnerability_description = options[:vulnerabilityDescription]
185
+ body.source_ident_vuln = options[:sourceIdentVuln]
186
+ body.poc_organization = options[:pocOrganization]
187
+ body.resources = options[:resources]
188
+
189
+ process_business_logic(body)
190
+
191
+ # Add conditional fields
192
+ body.poc_first_name = options[:pocFirstName] if options[:pocFirstName]
193
+ body.poc_last_name = options[:pocLastName] if options[:pocLastName]
194
+ body.poc_email = options[:pocEmail] if options[:pocEmail]
195
+ body.poc_phone_number = options[:pocPhoneNumber] if options[:pocPhoneNumber]
196
+ body.severity = options[:severity] if options[:severity]
197
+
198
+ # Add optional fields
199
+ body.external_uid = options[:externalUid] if options[:externalUid]
200
+ body.control_acronyms = options[:controlAcronym] if options[:controlAcronym]
201
+ body.cci = options[:cci] if options[:cci]
202
+ body.security_checks = options[:securityChecks] if options[:securityChecks]
203
+ body.raw_severity = options[:rawSeverity] if options[:rawSeverity]
204
+ body.relevance_of_threat = options[:relevanceOfThreat] if options[:relevanceOfThreat]
205
+ body.likelihood = options[:likelihood] if options[:likelihood]
206
+ body.impact = options[:impact] if options[:impact]
207
+ body.impact_description = options[:impactDescription] if options[:impactDescription]
208
+ body.residual_risk_level = options[:residualRiskLevel] if options[:residualRiskLevel]
209
+ body.recommendations = options[:recommendations] if options[:recommendations]
210
+ body.mitigation = options[:mitigation] if options[:mitigation]
211
+
212
+ body_array = Array.new(1, body)
213
+
214
+ begin
215
+ result = EmassClient::POAMApi.new.add_poam_by_system_id(options[:systemId], body_array)
216
+ puts to_output_hash(result).green
217
+ rescue EmassClient::ApiError => e
218
+ puts 'Exception when calling POAMApi->add_poam_by_system_id'.red
219
+ puts to_output_hash(e)
220
+ end
221
+ end
222
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
223
+
224
+ # rubocop:disable Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
225
+ no_commands do
226
+ def process_business_logic(body)
227
+ #-----------------------------------------------------------------------------
228
+ # Conditional fields based on the status field values
229
+ # "Risk Accepted" comments, resources
230
+ # "Ongoing" scheduledCompletionDate, resources, milestones (at least 1)
231
+ # "Completed" scheduledCompletionDate, comments, resources,
232
+ # completionDate, milestones (at least 1)
233
+ # "Not Applicable" POAM can not be created
234
+ #-----------------------------------------------------------------------------
235
+ # rubocop:disable Style/CaseLikeIf, Style/StringLiterals
236
+ if options[:status] == "Risk Accepted"
237
+ if options[:comments].nil?
238
+ puts 'When status = "Risk Accepted" the following parameters/fields are required:'.red
239
+ puts ' comments'.red
240
+ puts POAMS_POST_HELP_MESSAGE.yellow
241
+ exit
242
+ elsif !(options[:scheduledCompletionDate].nil? && options[:milestone].nil?)
243
+ puts 'When status = "Risk Accepted" POA&M Item CAN NOT be saved with the following parameters/fields:'.red
244
+ puts ' scheduledCompletionDate, or milestone'.red
245
+ puts POAMS_PUT_HELP_MESSAGE.yellow
246
+ exit
247
+ else
248
+ body.comments = options[:comments]
249
+ end
250
+ elsif options[:status] == "Ongoing"
251
+ if options[:scheduledCompletionDate].nil? || options[:milestone].nil?
252
+ puts 'When status = "Ongoing" the following parameters/fields are required:'.red
253
+ puts ' scheduledCompletionDate, milestone'.red
254
+ print_milestone_help
255
+ puts POAMS_POST_HELP_MESSAGE.yellow
256
+ exit
257
+ elsif options[:milestone]["description"].nil? || options[:milestone]["scheduledCompletionDate"].nil?
258
+ puts 'Missing milstone parameters/fields'.red
259
+ print_milestone_help
260
+ exit
261
+ else
262
+ body.scheduled_completion_date = options[:scheduledCompletionDate]
263
+
264
+ milestone = EmassClient::MilestonesRequiredPost.new
265
+ milestone.description = options[:milestone]["description"]
266
+ milestone.scheduled_completion_date = options[:milestone]["scheduledCompletionDate"]
267
+ milestone_array = Array.new(1, milestone)
268
+ body.milestones = milestone_array
269
+ end
270
+ elsif options[:status] == "Completed"
271
+ if options[:scheduledCompletionDate].nil? || options[:comments].nil? ||
272
+ options[:completionDate].nil? || options[:milestone].nil?
273
+ puts 'When status = "Completed" the following parameters/fields are required:'.red
274
+ puts ' scheduledCompletionDate, comments, completionDate, or milestone'.red
275
+ print_milestone_help
276
+ puts POAMS_POST_HELP_MESSAGE.yellow
277
+ exit
278
+ else
279
+ body.scheduled_completion_date = options[:scheduledCompletionDate]
280
+ body.comments = options[:comments]
281
+ body.completion_date = options[:completionDate]
282
+
283
+ milestone = EmassClient::MilestonesRequiredPost.new
284
+ milestone.description = options[:milestone]["description"]
285
+ milestone.scheduled_completion_date = options[:milestone]["scheduledCompletionDate"]
286
+ milestone_array = Array.new(1, milestone)
287
+ body.milestones = milestone_array
288
+ end
289
+ end
290
+
291
+ # POC checks: If any poc information is provided all POC fields are required
292
+ if options[:pocFirstName]
293
+ if options[:pocLastName].nil? || options[:pocEmail].nil? || options[:pocPhoneNumber].nil?
294
+ puts 'If a POC first name is given, then all POC information must be entered:'.red
295
+ puts ' pocLastName, pocEmail, pocPhoneNumber'.red
296
+ puts POAMS_POST_HELP_MESSAGE.yellow
297
+ exit
298
+ end
299
+ elsif options[:pocLastName]
300
+ if options[:pocFirstName].nil? || options[:pocEmail].nil? || options[:pocPhoneNumber].nil?
301
+ puts 'If a POC last name is given, then all POC information must be entered:'.red
302
+ puts ' pocFirstName, pocEmail, pocPhoneNumber'.red
303
+ puts POAMS_POST_HELP_MESSAGE.yellow
304
+ exit
305
+ end
306
+ elsif options[:pocEmail]
307
+ if options[:pocFirstName].nil? || options[:pocLastName].nil? || options[:pocPhoneNumber].nil?
308
+ puts 'If a POC email is given, then all POC information must be entered:'.red
309
+ puts ' pocFirstName, pocLastName, pocPhoneNumber'.red
310
+ puts POAMS_POST_HELP_MESSAGE.yellow
311
+ exit
312
+ end
313
+ elsif options[:pocPhoneNumber]
314
+ if options[:pocFirstName].nil? || options[:pocLastName].nil? || options[:pocEmail].nil?
315
+ puts 'If a POC phone number is given, then all POC information must be entered:'.red
316
+ puts ' pocFirstName, pocLastName, pocEmail'.red
317
+ puts POAMS_POST_HELP_MESSAGE.yellow
318
+ exit
319
+ end
320
+ end
321
+ # rubocop:enable Style/CaseLikeIf, Style/StringLiterals
322
+ end
323
+
324
+ def print_milestone_help
325
+ puts 'Milestone format is:'.yellow
326
+ puts ' --milestone description:"[value]" scheduledCompletionDate:"[value]"'.yellow
327
+ end
328
+ end
329
+ # rubocop:enable Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
330
+ end
331
+
332
+ # The Milestones endpoints provide the ability add milestones that are associated with
333
+ # Plan of Action and Milestones (POA&M) items for a system.
334
+ #
335
+ # Endpoint:
336
+ # /api/systems/{systemId}/poams/{poamId}/milestones - Add milestones in one or many poa&m items in a system
337
+ class Milestones < SubCommandBase
338
+ def self.exit_on_failure?
339
+ true
340
+ end
341
+
342
+ desc 'add', 'Add milestones to one or many POA&M items in a system'
343
+ long_desc Help.text(:milestone_post_mapper)
344
+
345
+ # Required parameters/fields
346
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
347
+ option :poamId, type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
348
+ option :description, type: :string, required: true, desc: 'The milestone description'
349
+ option :scheduledCompletionDate,
350
+ type: :numeric, required: true, desc: 'The scheduled completion date - Unix time format'
351
+
352
+ def add
353
+ body = EmassClient::MilestonesGet.new
354
+ body.description = options[:description]
355
+ body.scheduled_completion_date = options[:scheduledCompletionDate]
356
+ body_array = Array.new(1, body)
357
+
358
+ begin
359
+ result = EmassClient::MilestonesApi
360
+ .new.add_milestone_by_system_id_and_poam_id(options[:systemId], options[:poamId], body_array)
361
+ puts to_output_hash(result).green
362
+ rescue EmassClient::ApiError => e
363
+ puts 'Exception when calling MilestonesApi->add_milestone_by_system_id_and_poam_id'.red
364
+ puts to_output_hash(e)
365
+ end
366
+ end
367
+ end
368
+
369
+ # Add one or many artifacts for a system (delivery method must be a zip file)
370
+ #
371
+ # Endpoints:
372
+ # /api/systems/{systemId}/artifacts - Post one or many artifacts to a system
373
+ class Artifacts < SubCommandBase
374
+ def self.exit_on_failure?
375
+ true
376
+ end
377
+
378
+ desc 'upload SYSTEM_ID FILE [FILE ...]', 'Uploads [FILES] to the given [SYSTEM_ID] as artifacts'
379
+ long_desc Help.text(:artifacts_post_mapper)
380
+
381
+ # Required parameters/fields
382
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
383
+ option :files, type: :array, required: true, desc: 'Artifact file(s) to post to the given system'
384
+ option :type,
385
+ type: :string, required: true,
386
+ enum: ['Procedure', 'Diagram', 'Policy', 'Labor', 'Document',
387
+ 'Image', 'Other', 'Scan Result', 'Auditor Report']
388
+ option :category, type: :string, required: true, enum: ['Implementation Guidance', 'Evidence']
389
+ option :isTemplate, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false.'
390
+
391
+ # Optional parameters/fields
392
+ option :description, type: :string, required: false, desc: 'Artifact description'
393
+ option :refPageNumber, type: :string, required: false, desc: 'Artifact reference page number'
394
+ option :ccis, type: :string, required: false, desc: 'The system CCIs string numerical value'
395
+ option :controls,
396
+ type: :string, required: false,
397
+ desc: 'Control acronym associated with the artifact. NIST SP 800-53 Revision 4 defined'
398
+ option :artifactExpirationDate,
399
+ type: :numeric, required: false, desc: 'Date Artifact expires and requires review - Unix time format'
400
+ option :lastReviewedDate,
401
+ type: :numeric, required: false, desc: 'Date Artifact was last reviewed - Unix time format'
402
+
403
+ def upload
404
+ optional_options_keys = optional_options(@_initializer).keys
405
+ optional_options = to_input_hash(optional_options_keys, options)
406
+ # Remove the isTemplate as we can't use the required = true.
407
+ optional_options.delete(:is_template)
408
+
409
+ opts = {}
410
+ opts[:type] = options[:type]
411
+ opts[:category] = options[:category]
412
+ opts[:is_template] = options[:is_template]
413
+ opts[:form_params] = optional_options
414
+
415
+ tempfile = Tempfile.create(['artifacts', '.zip'])
416
+
417
+ Zip::OutputStream.open(tempfile.path) do |z|
418
+ options[:files].each do |file|
419
+ # Add file name to the archive: Don't use the full path
420
+ z.put_next_entry(File.basename(file))
421
+ # Add the file to the archive
422
+ z.print File.read(file)
423
+ end
424
+ end
425
+
426
+ begin
427
+ result = EmassClient::ArtifactsApi
428
+ .new
429
+ .add_artifacts_by_system_id(options[:systemId], tempfile, opts)
430
+ puts to_output_hash(result).green
431
+ rescue EmassClient::ApiError => e
432
+ puts 'Exception when calling ArtifactsApi->add_artifacts_by_system_id'.red
433
+ puts to_output_hash(e)
434
+ ensure
435
+ # Delete the temp file
436
+ unless File.exist? tempfile
437
+ tempfile.close
438
+ FileUtils.remove_file(tempfile, true)
439
+ end
440
+ end
441
+ end
442
+ end
443
+
444
+ # Add a Control Approval Chain (CAC)
445
+ #
446
+ # Endpoints:
447
+ # /api/systems/{systemId}/approval/cac - Submit control to second stage of CAC
448
+ class CAC < SubCommandBase
449
+ def self.exit_on_failure?
450
+ true
451
+ end
452
+
453
+ desc 'add', 'Submit control to second stage of CAC'
454
+ long_desc Help.text(:approvalCac_post_mapper)
455
+
456
+ # Required parameters/fields
457
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
458
+ option :controlAcronym, type: :string, required: true, desc: 'The system acronym "AC-1, AC-2"'
459
+
460
+ # Conditional parameters/fields
461
+ option :comments, type: :string, required: false, desc: 'The control approval chain comments'
462
+
463
+ def add
464
+ body = EmassClient::CacGet.new
465
+ body.control_acronym = options[:controlAcronym]
466
+ body.comments = options[:comments]
467
+
468
+ body_array = Array.new(1, body)
469
+
470
+ begin
471
+ # Get location of one or many controls in CAC
472
+ result = EmassClient::CACApi.new.add_system_cac(options[:systemId], body_array)
473
+ puts to_output_hash(result).green
474
+ rescue EmassClient::ApiError => e
475
+ puts 'Exception when calling ApprovalChainApi->add_system_cac'.red
476
+ puts to_output_hash(e)
477
+ end
478
+ end
479
+ end
480
+
481
+ # Add a Package Approval Chain (PAC)
482
+ #
483
+ # Endpoints:
484
+ # /api/systems/{systemId}/approval/pac - Initiate system workflow for review
485
+ class PAC < SubCommandBase
486
+ def self.exit_on_failure?
487
+ true
488
+ end
489
+
490
+ desc 'add', 'Initiate system workflow for review'
491
+ long_desc Help.text(:approvalPac_post_mapper)
492
+
493
+ # Required parameters/fields
494
+ option :systemId, type: :numeric, required: true,
495
+ desc: 'A numeric value representing the system identification'
496
+ option :workflow, type: :string, required: true,
497
+ enum: ['Assess and Authorize', 'Assess Only', 'Security Plan Approval']
498
+ option :name, type: :string, required: true, desc: 'The control package name'
499
+ option :comments, type: :string, required: true,
500
+ desc: 'Comments submitted upon initiation of the indicated workflow'
501
+
502
+ def add
503
+ body = EmassClient::PacGet.new
504
+ body.name = options[:name]
505
+ body.workflow = options[:workflow]
506
+ body.comments = options[:comments]
507
+
508
+ body_array = Array.new(1, body)
509
+
510
+ result = EmassClient::PACApi.new.add_system_pac(options[:systemId], body_array)
511
+ puts to_output_hash(result).green
512
+ rescue EmassClient::ApiError => e
513
+ puts 'Exception when calling ApprovalChainApi->add_system_pac'.red
514
+ puts to_output_hash(e)
515
+ end
516
+ end
517
+
518
+ # The Static Code Scans endpoint provides the ability to upload application
519
+ # scan findings into a system's assets module.
520
+ #
521
+ # Application findings can also be cleared from the system.
522
+ #
523
+ # Endpoint:
524
+ # /api/systems/{systemId}/static-code-scans - Upload static code scans
525
+ class ScanFindings < SubCommandBase
526
+ def self.exit_on_failure?
527
+ true
528
+ end
529
+
530
+ desc 'add', 'Upload static code scans'
531
+ long_desc Help.text(:staticcode_post_mapper)
532
+
533
+ # Required parameters/fields
534
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
535
+ option :applicationName, type: :string, required: true, desc: 'Name of the software application that was assessed'
536
+ option :version, type: :string, required: true, desc: 'The version of the application'
537
+ option :codeCheckName, type: :string, required: true, desc: 'Name of the software vulnerability or weakness'
538
+ option :scanDate, type: :numeric, required: true, desc: 'The findings scan date - Unix time format'
539
+ option :cweId, type: :string, required: true, desc: 'The Common Weakness Enumerator (CWE) identifier'
540
+ option :count, type: :numeric, required: true, desc: 'Number of instances observed for a specified finding'
541
+ # Optional parameter/fields
542
+ option :rawSeverity, type: :string, required: false, enum: %w[Low Medium Moderate High Critical]
543
+
544
+ def add
545
+ application = EmassClient::StaticCodeRequestPostBodyApplication.new
546
+ application.application_name = options[:applicationName]
547
+ application.version = options[:version]
548
+
549
+ application_findings = EmassClient::StaticCodeApplication.new
550
+ application_findings.code_check_name = options[:codeCheckName]
551
+ application_findings.scan_date = options[:scanDate]
552
+ application_findings.cwe_id = options[:cweId]
553
+ application_findings.count = options[:count]
554
+ application_findings.raw_severity = options[:rawSeverity] if options[:rawSeverity]
555
+
556
+ app_findings_array = Array.new(1, application_findings)
557
+
558
+ body = EmassClient::StaticCodeRequestPostBody.new
559
+ body.application = application
560
+ body.application_findings = app_findings_array
561
+
562
+ body_array = Array.new(1, body)
563
+
564
+ begin
565
+ result = EmassClient::StaticCodeScansApi
566
+ .new.add_static_code_scans_by_system_id(options[:systemId], body_array)
567
+ puts to_output_hash(result).green
568
+ rescue EmassClient::ApiError => e
569
+ puts 'Exception when calling StaticCodeScansApi->add_static_code_scans_by_system_id'.red
570
+ puts to_output_hash(e)
571
+ end
572
+ end
573
+
574
+ # CLEAR ------------------------------------------------------------------------------------
575
+ desc 'clear', 'Clear an application findings'
576
+ long_desc Help.text(:staticcode_clear_mapper)
577
+
578
+ # Required parameters/fields
579
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
580
+ option :applicationName, type: :string, required: true, desc: 'Name of the software application that was assessed'
581
+ option :version, type: :string, required: true, desc: 'The version of the application'
582
+ option :clearFindings, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false'
583
+ # NOTE: clearFindings is a required parameter to clear an application's findings, however Thor does not allow
584
+ # a boolean type to be required because it automatically creates a --no-clearFindings option for clearFindings=false
585
+
586
+ def clear
587
+ unless options[:clearFindings]
588
+ puts 'To clear an application findings, the field clearFindings (--clearFindings) is required'.red
589
+ puts SCAN_POST_HELP_MESSAGE.yellow
590
+ exit
591
+ end
592
+
593
+ application = EmassClient::StaticCodeRequestPostBodyApplication.new
594
+ application.application_name = options[:applicationName]
595
+ application.version = options[:version]
596
+
597
+ application_findings = EmassClient::StaticCodeApplication.new
598
+ application_findings.clear_findings = options[:clearFindings]
599
+
600
+ app_findings_array = Array.new(1, application_findings)
601
+
602
+ body = EmassClient::StaticCodeRequestPostBody.new
603
+ body.application = application
604
+ body.application_findings = app_findings_array
605
+
606
+ body_array = Array.new(1, body)
607
+
608
+ begin
609
+ result = EmassClient::StaticCodeScansApi
610
+ .new.add_static_code_scans_by_system_id(options[:systemId], body_array)
611
+ puts to_output_hash(result).green
612
+ rescue EmassClient::ApiError => e
613
+ puts 'Exception when calling StaticCodeScansApi->add_static_code_scans_by_system_id'.red
614
+ puts to_output_hash(e)
615
+ end
616
+ end
617
+ end
618
+
619
+ # The Cloud Resources endpoint provides the ability to upload (add)
620
+ # cloud resources and their scan results in the assets module for a system.
621
+ #
622
+ #
623
+ # Endpoint:
624
+ # /api/systems/{systemId}/cloud-resources-results - Upload cloud resources and their scan results
625
+ class CloudResource < SubCommandBase
626
+ def self.exit_on_failure?
627
+ true
628
+ end
629
+
630
+ desc 'add', 'Upload cloud resources and their scan results'
631
+ long_desc Help.text(:cloudresource_post_mapper)
632
+
633
+ # Required parameters/fields
634
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
635
+ option :provider, type: :string, required: true, desc: 'Cloud service provider name'
636
+ option :resourceId, type: :string, required: true, desc: 'Unique identifier/resource namespace for policy compliance result'
637
+ option :resourceName, type: :string, required: true, desc: 'Friendly name of Cloud resource'
638
+ option :resourceType, type: :string, required: true, desc: 'Type of Cloud resource'
639
+ # ComplianceResults Array Objects
640
+ option :cspPolicyDefinitionId, type: :string, required: true, desc: 'Unique identifier/compliance namespace for CSP/Resource\'s policy definition/compliance check'
641
+ option :isCompliant, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false'
642
+ option :policyDefinitionTitle, type: :string, required: true, desc: 'Friendly policy/compliance check title. Recommend short title'
643
+
644
+ # Optional parameter/fields
645
+ option :initiatedBy, type: :string, required: false, desc: 'Email of POC'
646
+ option :cspAccountId, type: :string, required: false, desc: 'System/owner\'s CSP account ID/number'
647
+ option :cspRegion, type: :string, required: false, desc: 'CSP region of system'
648
+ option :isBaseline, type: :boolean, required: false, default: true, desc: 'BOOLEAN - true or false'
649
+ # Tags Object
650
+ option :test, type: :string, required: false, desc: 'The test tag'
651
+ # ComplianceResults Array Objects
652
+ option :assessmentProcedure, type: :string, required: false, desc: 'Comma separated correlation to Assessment Procedure (i.e. CCI number for DoD Control Set)'
653
+ option :complianceCheckTimestamp, type: :numeric, required: false, desc: 'The compliance timestamp Unix date format.'
654
+ option :complianceReason, type: :string, required: false, desc: 'Reason/comments for compliance result'
655
+ option :control, type: :string, required: false, desc: 'Comma separated correlation to Security Control (e.g. exact NIST Control acronym)'
656
+ option :policyDeploymentName, type: :string, required: false, desc: 'Name of policy deployment'
657
+ option :policyDeploymentVersion, type: :string, required: false, desc: 'policyDeploymentVersion'
658
+ option :severity, type: :string, required: false, enum: %w[Low Medium Moderate High Critical]
659
+
660
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
661
+ def add
662
+ # Required and Optional main fields
663
+ body = {}
664
+ body[:provider] = options[:provider]
665
+ body[:resourceId] = options[:resourceId]
666
+ body[:resourceName] = options[:resourceName]
667
+ body[:resourceType] = options[:resourceType]
668
+
669
+ body[:initiatedBy] = options[:initiatedBy] if options[:initiatedBy]
670
+ body[:cspAccountId] = options[:cspAccountId] if options[:cspAccountId]
671
+ body[:cspRegion] = options[:cspRegion] if options[:cspRegion]
672
+ body[:isBaseline] = options[:isBaseline] if options[:isBaseline]
673
+
674
+ # Optional tags field
675
+ tags = {}
676
+ tags[:test] = options[:test] if options[:test]
677
+
678
+ # Required and Optional compliances results fields
679
+ compliance_results = {}
680
+ compliance_results[:cspPolicyDefinitionId] = options[:cspPolicyDefinitionId]
681
+ compliance_results[:isCompliant] = options[:isCompliant]
682
+ compliance_results[:policyDefinitionTitle] = options[:policyDefinitionTitle]
683
+ # Optional fields
684
+ compliance_results[:assessmentProcedure] = options[:assessmentProcedure] if options[:assessmentProcedure]
685
+ compliance_results[:complianceCheckTimestamp] = options[:complianceCheckTimestamp] if options[:complianceCheckTimestamp]
686
+ compliance_results[:complianceReason] = options[:complianceReason] if options[:complianceReason]
687
+ compliance_results[:control] = options[:control] if options[:control]
688
+ compliance_results[:policyDeploymentName] = options[:policyDeploymentName] if options[:policyDeploymentName]
689
+ compliance_results[:policyDeploymentVersion] = options[:policyDeploymentVersion] if options[:policyDeploymentVersion]
690
+ compliance_results[:severity] = options[:severity] if options[:severity]
691
+
692
+ compliance_results_array = Array.new(1, compliance_results)
693
+
694
+ body[:tags] = tags
695
+ body[:complianceResults] = compliance_results_array
696
+
697
+ body_array = Array.new(1, body)
698
+
699
+ begin
700
+ result = EmassClient::CloudResourcesApi
701
+ .new.add_cloud_resources_by_system_id(options[:systemId], body_array)
702
+ puts to_output_hash(result).green
703
+ rescue EmassClient::ApiError => e
704
+ puts 'Exception when calling StaticCodeScansApi->add_cloud_resources_by_system_id'.red
705
+ puts to_output_hash(e)
706
+ end
707
+ end
708
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
709
+ end
710
+
711
+ # The Containers endpoint provides the ability to upload (add)
712
+ # containers and their scan results in the assets module for a system.
713
+ #
714
+ #
715
+ # Endpoint:
716
+ # /api/systems/{systemId}/container-scan-results - Upload containers and their scan results
717
+ class Container < SubCommandBase
718
+ def self.exit_on_failure?
719
+ true
720
+ end
721
+
722
+ desc 'add', 'Upload containers and their scan results'
723
+ long_desc Help.text(:container_post_mapper)
724
+
725
+ # Required parameters/fields
726
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
727
+ option :containerId, type: :string, required: true, desc: 'Unique identifier of the container'
728
+ option :containerName, type: :string, required: true, desc: 'Friendly name of the container'
729
+ option :time, type: :numeric, required: true, desc: 'Datetime of scan/result. Unix date format'
730
+ # Benchmarks Array Objects
731
+ option :benchmark, type: :string, required: true, desc: 'Identifier of the benchmark/grouping of compliance results'
732
+ # Benchmarks.Results Array Objects
733
+ option :lastSeen, type: :numeric, required: true, desc: 'Date last seen, Unix date format'
734
+ option :ruleId, type: :string, required: true, desc: 'Identifier for the compliance result, vulnerability, etc. the result is for'
735
+ option :status, type: :string, required: true, enum: ['Pass', 'Fail', 'Other', 'Not Reviewed', 'Not Checked', 'Not Applicable']
736
+
737
+ # Optional parameter/fields
738
+ option :namespace, type: :string, required: false, desc: 'Namespace of container in container orchestration'
739
+ option :podIp, type: :string, required: false, desc: 'IP address of the pod'
740
+ option :podName, type: :string, required: false, desc: 'Name of pod (e.g. Kubernetes pod)'
741
+ # Tags Object
742
+ option :test, type: :string, required: false, desc: 'The test tag'
743
+ # Benchmarks Array Objects
744
+ option :isBaseline, type: :boolean, required: false, default: true, desc: 'BOOLEAN - true or false'
745
+ # Benchmarks.Results Array Objects
746
+ option :message, type: :string, required: false, desc: 'Benchmark result comments'
747
+
748
+ # rubocop:disable Metrics/CyclomaticComplexity
749
+ def add
750
+ # Required and Optional main fields
751
+ body = {}
752
+ body[:containerId] = options[:containerId]
753
+ body[:containerName] = options[:containerName]
754
+ body[:time] = options[:time]
755
+ body[:namespace] = options[:namespace] if options[:namespace]
756
+ body[:podIp] = options[:podIp] if options[:podIp]
757
+ body[:podName] = options[:podName] if options[:podName]
758
+
759
+ # Optional tags field
760
+ tags = {}
761
+ tags[:test] = options[:test] if options[:test]
762
+
763
+ # Required and Optional Benchmarks fields
764
+ benchmarks = {}
765
+ benchmarks[:benchmark] = options[:benchmark]
766
+ # Optional fields
767
+ benchmarks[:isBaseline] = options[:isBaseline] if options[:isBaseline]
768
+
769
+ # Required and Optional Benchmarks.Results
770
+ benchmarks_results = {}
771
+ benchmarks_results[:lastSeen] = options[:lastSeen]
772
+ benchmarks_results[:ruleId] = options[:ruleId]
773
+ benchmarks_results[:status] = options[:status]
774
+ benchmarks_results[:message] = options[:message] if options[:message]
775
+
776
+ # Add Benchmark results to an array and add array to benchmarks object
777
+ benchmarks_results_array = Array.new(1, benchmarks_results)
778
+ benchmarks[:results] = benchmarks_results_array # = Array.new(1, benchmarks_results)
779
+ # Add benchmarks object to an array
780
+ benchmarks_array = Array.new(1, benchmarks)
781
+ # Add tags and benchmark ojects to body object
782
+ body[:tags] = tags
783
+ body[:benchmarks] = benchmarks_array
784
+
785
+ body_array = Array.new(1, body)
786
+
787
+ begin
788
+ result = EmassClient::ContainersApi
789
+ .new.add_container_sans_by_system_id(options[:systemId], body_array)
790
+ puts to_output_hash(result).green
791
+ rescue EmassClient::ApiError => e
792
+ puts 'Exception when calling StaticCodeScansApi->add_container_sans_by_system_id'.red
793
+ puts to_output_hash(e)
794
+ end
795
+ end
796
+ # rubocop:enable Metrics/CyclomaticComplexity
797
+ end
798
+
799
+ class Post < SubCommandBase
800
+ desc 'register', 'Register a certificate & obtain an API-key'
801
+ subcommand 'register', Register
802
+
803
+ desc 'test_results', 'Add system Test Results'
804
+ subcommand 'test_results', TestResults
805
+
806
+ desc 'poams', 'Add Plan of Action and Milestones (POA&M) items to a system'
807
+ subcommand 'poams', Poams
808
+
809
+ desc 'milestones', 'Add milestone(s) to one or many POA&M items in a system'
810
+ subcommand 'milestones', Milestones
811
+
812
+ desc 'artifacts', 'Add system Artifacts'
813
+ subcommand 'artifacts', Artifacts
814
+
815
+ desc 'cac', 'Add Control Approval Chain (CAC) security content'
816
+ subcommand 'cac', CAC
817
+
818
+ desc 'pac', 'Add Package Approval Chain (PAC) security content'
819
+ subcommand 'pac', PAC
820
+
821
+ desc 'scan_findings', 'Upload static code scans'
822
+ subcommand 'scan_findings', ScanFindings
823
+
824
+ desc 'cloud_resource', 'Upload cloud resource and their scan results'
825
+ subcommand 'cloud_resource', CloudResource
826
+
827
+ desc 'container', 'Upload container and their scan results'
828
+ subcommand 'container', Container
829
+ end
830
+ end