emasser 3.4.0 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
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