emasser 3.4.1 → 3.12.0

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 +39 -72
  13. data/.gitignore +19 -19
  14. data/.mergify.yml +25 -25
  15. data/.rubocop.yml +83 -80
  16. data/.rubocop_todo.yml +27 -27
  17. data/CHANGELOG.md +66 -16
  18. data/Dockerfile +44 -44
  19. data/Gemfile +8 -8
  20. data/Gemfile.lock +108 -104
  21. data/LICENSE.md +15 -15
  22. data/README.md +179 -178
  23. data/Rakefile +18 -18
  24. data/_config.yml +1 -1
  25. data/docs/features.md +1677 -1437
  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 +22 -26
  32. data/lib/emasser/delete.rb +210 -148
  33. data/lib/emasser/errors.rb +14 -14
  34. data/lib/emasser/get.rb +1401 -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 +125 -111
  57. data/lib/emasser/post.rb +830 -830
  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 +16 -10
data/lib/emasser/post.rb CHANGED
@@ -1,830 +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
- # 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
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