emasser 3.4.1 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +8 -8
  3. data/.env-example +12 -12
  4. data/.github/release-drafter.yml +15 -15
  5. data/.github/workflows/codeql-analysis.yml +70 -70
  6. data/.github/workflows/draft-release.yml +15 -15
  7. data/.github/workflows/gh-pages.yml +32 -32
  8. data/.github/workflows/push-to-docker-mail.yml +28 -28
  9. data/.github/workflows/push-to-docker.yml +35 -35
  10. data/.github/workflows/release.yml +42 -42
  11. data/.github/workflows/rubocop.yml +23 -23
  12. data/.github/workflows/test-cli.yml +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 +16 -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 +178 -178
  23. data/Rakefile +18 -18
  24. data/_config.yml +1 -1
  25. data/docs/features.md +1501 -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 +1194 -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 +115 -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