emasser 1.0.3 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.env-example +2 -0
  3. data/.github/workflows/gh-pages.yml +4 -5
  4. data/.github/workflows/release.yml +9 -9
  5. data/Dockerfile +6 -4
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +25 -32
  8. data/README.md +87 -78
  9. data/docs/features.md +455 -213
  10. data/docs/images/emasser_architecture.jpg +0 -0
  11. data/emasser.gemspec +5 -3
  12. data/images/emasser_architecture.jpg +0 -0
  13. data/images/emasser_diagram-Page-3.jpg +0 -0
  14. data/lib/emasser/cli.rb +2 -2
  15. data/lib/emasser/configuration.rb +1 -0
  16. data/lib/emasser/constants.rb +11 -3
  17. data/lib/emasser/delete.rb +9 -7
  18. data/lib/emasser/get.rb +323 -49
  19. data/lib/emasser/help/cloudresource_post_mapper.md +62 -0
  20. data/lib/emasser/help/container_post_mapper.md +44 -0
  21. data/lib/emasser/output_converters.rb +101 -4
  22. data/lib/emasser/post.rb +231 -38
  23. data/lib/emasser/put.rb +23 -16
  24. data/lib/emasser/version.rb +1 -1
  25. metadata +15 -27
  26. data/.github/workflows/generate_docs.yml +0 -33
  27. data/docs/developers.md +0 -115
  28. data/docs/swagger/dist/favicon-16x16.png +0 -0
  29. data/docs/swagger/dist/favicon-32x32.png +0 -0
  30. data/docs/swagger/dist/oauth2-redirect.html +0 -75
  31. data/docs/swagger/dist/swagger-ui-bundle.js +0 -3
  32. data/docs/swagger/dist/swagger-ui-bundle.js.map +0 -1
  33. data/docs/swagger/dist/swagger-ui-es-bundle-core.js +0 -3
  34. data/docs/swagger/dist/swagger-ui-es-bundle-core.js.map +0 -1
  35. data/docs/swagger/dist/swagger-ui-es-bundle.js +0 -3
  36. data/docs/swagger/dist/swagger-ui-es-bundle.js.map +0 -1
  37. data/docs/swagger/dist/swagger-ui-standalone-preset.js +0 -3
  38. data/docs/swagger/dist/swagger-ui-standalone-preset.js.map +0 -1
  39. data/docs/swagger/dist/swagger-ui.css +0 -4
  40. data/docs/swagger/dist/swagger-ui.css.map +0 -1
  41. data/docs/swagger/dist/swagger-ui.js +0 -3
  42. data/docs/swagger/dist/swagger-ui.js.map +0 -1
  43. data/docs/swagger/index.html +0 -60
@@ -0,0 +1,44 @@
1
+ Add cloud resource and scan results in the assets module for a system
2
+
3
+ Endpoint request parameters/fields
4
+
5
+ Field Data Type Details
6
+ -------------------------------------------------------------------------------------------------
7
+ systemId Integer [Required] Unique eMASS identifier. Will need to provide correct number.
8
+ containerId String [Required] Unique identifier of the container.
9
+ containerName String [Required] Friendly name of the container.
10
+ time Date [Required] Datetime of scan/result. Unix date format.
11
+
12
+ podName String [Optional] Name of pod (e.g. Kubernetes pod).
13
+ podIp String [Optional] IP address of pod.
14
+ namespace String [Optional] Namespace of container in container orchestration (e.g. Kubernetes namespace).
15
+
16
+ tags Object [Optional] Informational tags associated to results for other metadata.
17
+ text String [Optional] Tag metadata information.
18
+
19
+ benchmarks Object
20
+ benchmark String [Required] Identifier of the benchmark/grouping of compliance results.
21
+ (e.g. for STIG results, provide the benchmark id for the STIG technology).
22
+ isBaseline Boolean [Optional] True/false flag for providing results as baseline. If true, all existing
23
+ compliance results for the provided benchmark within the container will be replaced
24
+ by results in the current call.
25
+ results Object
26
+ ruleId String [Required] Identifier for the compliance result, vulnerability, etc.
27
+ status String [Required] Benchmark result status
28
+ lastSeen Date [Required] Date last seen, Unix date format
29
+ message String [Optional] Comments for the result
30
+
31
+ The following Container parameters/fields have the following character limitations:
32
+ - Fields that can not exceed 100 characters:
33
+ - STIG Benchmark ID (`benchmark`)
34
+ - Container Namespace (`namespace`)
35
+ - Kubernetes assigned IP (`podIp`)
36
+ - Kubernetes Pod Name) (`podName`)
37
+ - Fields that can not exceed 500 characters:
38
+ - Container ID (`containerId`)
39
+ - Friendly Container Name (`containerName`)
40
+ - Fields that can not exceed 1000 characters:
41
+ - Result Comments (`message`)
42
+
43
+ Example:
44
+ bundle exec ruby exe/emasser post container add --systemId [value] --containerId [value] --containerName [value] --time [value] --benchmark [value] --lastSeen [value] --ruleId [value] --status [value]
@@ -1,14 +1,111 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OutputConverters
4
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Style/TernaryParentheses
5
+ # rubocop:disable Style/IfWithBooleanLiteralBranches, Style/RescueStandardError, Metrics/BlockNesting
4
6
  def to_output_hash(obj)
7
+ diplay_nulls = (ENV.fetch('EMASSER_CLI_DISPLAY_NULL', 'true').eql? 'true') ? true : false
8
+ diplay_datetime = (ENV.fetch('EMASSER_EPOCH_TO_DATETIME', 'false').eql? 'true') ? true : false
9
+
5
10
  if obj.to_s.include? 'Error message'
6
- obj
11
+ begin
12
+ index = obj.to_s.index('Response body')+14
13
+ arr_obj = obj.to_s[index, obj.to_s.size - index]
14
+ JSON.pretty_generate(JSON.parse(arr_obj))
15
+ rescue
16
+ obj
17
+ end
7
18
  else
8
- hash = obj.instance_variables.each_with_object({}) do |var, h|
9
- h[var.to_s.delete('@')] = obj.instance_variable_get(var)
19
+ obj = obj.to_body if obj.respond_to?(:to_body)
20
+ if !diplay_nulls
21
+ clean_obj = {}
22
+ data_obj = {}
23
+ obj.each do |key, value|
24
+ if key.to_s.include?('meta')
25
+ obj_entry = {}
26
+ obj_entry[:meta] = value
27
+ clean_obj.merge!(obj_entry)
28
+ elsif key.to_s.include?('data')
29
+ if value.is_a?(Array)
30
+ hash_array = []
31
+ value.each do |elements|
32
+ hash_array << elements.compact
33
+ end
34
+ data_obj['data'] = hash_array
35
+ else
36
+ data_obj['data'] = value.nil? ? value : value.compact
37
+ end
38
+ elsif key.to_s.include?('pagination')
39
+ pg_obj = {}
40
+ pg_obj[:pagination] = value
41
+ data_obj.merge!(pg_obj)
42
+ end
43
+ clean_obj.merge!(data_obj)
44
+ end
45
+ obj = clean_obj
46
+ end
47
+
48
+ if diplay_datetime
49
+ clean_obj = {}
50
+ data_obj = {}
51
+ obj.each do |key, value|
52
+ if key.to_s.include?('meta')
53
+ obj_entry = {}
54
+ obj_entry[:meta] = value
55
+ clean_obj.merge!(obj_entry)
56
+ elsif key.to_s.include?('data')
57
+ if value.is_a?(Array)
58
+ hash_array = []
59
+ value.each do |element|
60
+ datetime_obj = change_to_datetime(element)
61
+ hash_array << datetime_obj
62
+ end
63
+ data_obj['data'] = hash_array
64
+ else
65
+ data_obj['data'] = change_to_datetime(value)
66
+ end
67
+ elsif key.to_s.include?('pagination')
68
+ pg_obj = {}
69
+ pg_obj[:pagination] = value
70
+ data_obj.merge!(pg_obj)
71
+ end
72
+ clean_obj.merge!(data_obj)
73
+ end
74
+ obj = clean_obj
75
+ end
76
+ JSON.pretty_generate(obj)
77
+ end
78
+ end
79
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Style/TernaryParentheses
80
+ # rubocop:enable Style/IfWithBooleanLiteralBranches, Style/RescueStandardError, Metrics/BlockNesting
81
+
82
+ # rubocop:disable Style/IdenticalConditionalBranches
83
+ def change_to_datetime(obj)
84
+ if obj.nil?
85
+ return obj
86
+ end
87
+
88
+ data_obj = {}
89
+ obj.each do |key, value|
90
+ obj_entry = {}
91
+ if value.is_a?(Array)
92
+ hash_array = []
93
+ value.each do |element|
94
+ hash_array << change_to_datetime(element)
95
+ end
96
+ obj_entry[key] = hash_array
97
+ data_obj.merge!(obj_entry)
98
+ else
99
+ if /(date|Date)/.match?(key.to_s)
100
+ value = value.nil? ? value : Time.at(value.to_i)
101
+ end
102
+ obj_entry[key] = value
103
+ data_obj.merge!(obj_entry)
10
104
  end
11
- JSON.pretty_generate(hash)
12
105
  end
106
+ # rubocop:disable Style/RedundantReturn
107
+ return data_obj
108
+ # rubocop:enable Style/RedundantReturn
13
109
  end
110
+ # rubocop:enable Style/IdenticalConditionalBranches
14
111
  end
data/lib/emasser/post.rb CHANGED
@@ -37,7 +37,8 @@ class Thor
37
37
  end
38
38
 
39
39
  module Emasser
40
- POAMS_POST_HELP_MESSAGE = "\nInvoke \"bundle exec exe/emasser post poams help add\" for additional help"
40
+ POAMS_POST_HELP_MESSAGE = "\nInvoke \"emasser post poams help add\" for additional help"
41
+ SCAN_POST_HELP_MESSAGE = "\nInvoke \"emasser post scan_findings help clear\" for additional help"
41
42
  # The Test Results endpoints provide the ability to add test results for a
42
43
  # system's Assessment Procedures (CCIs) which determine Security Control compliance.
43
44
  #
@@ -61,7 +62,7 @@ module Emasser
61
62
  option :complianceStatus, type: :string, required: true, enum: ['Compliant', 'Non-Compliant', 'Not Applicable']
62
63
 
63
64
  def add
64
- body = EmassClient::TestResultsRequestPostBody.new
65
+ body = EmassClient::TestResultsGet.new
65
66
  body.cci = options[:cci]
66
67
  body.tested_by = options[:testedBy]
67
68
  body.test_date = options[:testDate]
@@ -72,7 +73,7 @@ module Emasser
72
73
 
73
74
  begin
74
75
  result = EmassClient::TestResultsApi
75
- .new.add_test_results_by_system_id(body_array, options[:systemId])
76
+ .new.add_test_results_by_system_id(options[:systemId], body_array)
76
77
  puts to_output_hash(result).green
77
78
  rescue EmassClient::ApiError => e
78
79
  puts 'Exception when calling TestResultsApi->add_test_results_by_system_id'.red
@@ -153,7 +154,7 @@ module Emasser
153
154
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
154
155
  def add
155
156
  # Required fields
156
- body = EmassClient::PoamRequiredPost.new
157
+ body = EmassClient::PoamGet.new
157
158
  body.status = options[:status]
158
159
  body.vulnerability_description = options[:vulnerabilityDescription]
159
160
  body.source_ident_vuln = options[:sourceIdentVuln]
@@ -186,7 +187,7 @@ module Emasser
186
187
  body_array = Array.new(1, body)
187
188
 
188
189
  begin
189
- result = EmassClient::POAMApi.new.add_poam_by_system_id(body_array, options[:systemId])
190
+ result = EmassClient::POAMApi.new.add_poam_by_system_id(options[:systemId], body_array)
190
191
  puts to_output_hash(result).green
191
192
  rescue EmassClient::ApiError => e
192
193
  puts 'Exception when calling POAMApi->add_poam_by_system_id'.red
@@ -195,7 +196,7 @@ module Emasser
195
196
  end
196
197
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
197
198
 
198
- # rubocop:disable Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
199
+ # rubocop:disable Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
199
200
  no_commands do
200
201
  def process_business_logic(body)
201
202
  #-----------------------------------------------------------------------------
@@ -213,13 +214,18 @@ module Emasser
213
214
  puts ' comments'.red
214
215
  puts POAMS_POST_HELP_MESSAGE.yellow
215
216
  exit
217
+ elsif !(options[:scheduledCompletionDate].nil? && options[:milestone].nil?)
218
+ puts 'When status = "Risk Accepted" POA&M Item CAN NOT be saved with the following parameters/fields:'.red
219
+ puts ' scheduledCompletionDate, or milestone'.red
220
+ puts POAMS_PUT_HELP_MESSAGE.yellow
221
+ exit
216
222
  else
217
223
  body.comments = options[:comments]
218
224
  end
219
225
  elsif options[:status] == "Ongoing"
220
226
  if options[:scheduledCompletionDate].nil? || options[:milestone].nil?
221
227
  puts 'When status = "Ongoing" the following parameters/fields are required:'.red
222
- puts ' scheduledCompletionDate, or milestone'.red
228
+ puts ' scheduledCompletionDate, milestone'.red
223
229
  print_milestone_help
224
230
  puts POAMS_POST_HELP_MESSAGE.yellow
225
231
  exit
@@ -295,7 +301,7 @@ module Emasser
295
301
  puts ' --milestone description:"[value]" scheduledCompletionDate:"[value]"'.yellow
296
302
  end
297
303
  end
298
- # rubocop:enable Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
304
+ # rubocop:enable Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
299
305
  end
300
306
 
301
307
  # The Milestones endpoints provide the ability add milestones that are associated with
@@ -316,18 +322,17 @@ module Emasser
316
322
  option :poamId, type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
317
323
  option :description, type: :string, required: true, desc: 'The milestone description'
318
324
  option :scheduledCompletionDate,
319
- type: :numeric, required: false, desc: 'The scheduled completion date - Unix time format'
325
+ type: :numeric, required: true, desc: 'The scheduled completion date - Unix time format'
320
326
 
321
327
  def add
322
- body = EmassClient::MilestonesRequestPostBody.new
323
- body.poam_id = options[:poamId]
328
+ body = EmassClient::MilestonesGet.new
324
329
  body.description = options[:description]
325
330
  body.scheduled_completion_date = options[:scheduledCompletionDate]
326
331
  body_array = Array.new(1, body)
327
332
 
328
333
  begin
329
334
  result = EmassClient::MilestonesApi
330
- .new.add_milestone_by_system_id_and_poam_id(body_array, options[:systemId], options[:poamId])
335
+ .new.add_milestone_by_system_id_and_poam_id(options[:systemId], options[:poamId], body_array)
331
336
  puts to_output_hash(result).green
332
337
  rescue EmassClient::ApiError => e
333
338
  puts 'Exception when calling MilestonesApi->add_milestone_by_system_id_and_poam_id'.red
@@ -357,8 +362,6 @@ module Emasser
357
362
  'Image', 'Other', 'Scan Result', 'Auditor Report']
358
363
  option :category, type: :string, required: true, enum: ['Implementation Guidance', 'Evidence']
359
364
  option :isTemplate, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false.'
360
- # NOTE: compress is a required parameter, however Thor does not allow a boolean type to be required because it
361
- # automatically creates a --no-isTemplate option for isTemplate=false
362
365
 
363
366
  # Optional parameters/fields
364
367
  option :description, type: :string, required: false, desc: 'Artifact description'
@@ -379,6 +382,9 @@ module Emasser
379
382
  optional_options.delete(:is_template)
380
383
 
381
384
  opts = {}
385
+ opts[:type] = options[:type]
386
+ opts[:category] = options[:category]
387
+ opts[:is_template] = options[:is_template]
382
388
  opts[:form_params] = optional_options
383
389
 
384
390
  tempfile = Tempfile.create(['artifacts', '.zip'])
@@ -395,8 +401,7 @@ module Emasser
395
401
  begin
396
402
  result = EmassClient::ArtifactsApi
397
403
  .new
398
- .add_artifacts_by_system_id(options[:isTemplate], options[:type],
399
- options[:category], tempfile, options[:systemId], opts)
404
+ .add_artifacts_by_system_id(options[:systemId], tempfile, opts)
400
405
  puts to_output_hash(result).green
401
406
  rescue EmassClient::ApiError => e
402
407
  puts 'Exception when calling ArtifactsApi->add_artifacts_by_system_id'.red
@@ -431,7 +436,7 @@ module Emasser
431
436
  option :comments, type: :string, required: false, desc: 'The control approval chain comments'
432
437
 
433
438
  def add
434
- body = EmassClient::CacRequestPostBody.new
439
+ body = EmassClient::CacGet.new
435
440
  body.control_acronym = options[:controlAcronym]
436
441
  body.comments = options[:comments]
437
442
 
@@ -439,10 +444,10 @@ module Emasser
439
444
 
440
445
  begin
441
446
  # Get location of one or many controls in CAC
442
- result = EmassClient::CacApi.new.add_s_ystem_c_ac(body_array, options[:systemId])
447
+ result = EmassClient::CACApi.new.add_system_cac(options[:systemId], body_array)
443
448
  puts to_output_hash(result).green
444
449
  rescue EmassClient::ApiError => e
445
- puts 'Exception when calling ApprovalChainApi->add_s_ystem_c_ac'.red
450
+ puts 'Exception when calling ApprovalChainApi->add_system_cac'.red
446
451
  puts to_output_hash(e)
447
452
  end
448
453
  end
@@ -470,22 +475,22 @@ module Emasser
470
475
  desc: 'Comments submitted upon initiation of the indicated workflow'
471
476
 
472
477
  def add
473
- body = EmassClient::PacRequestBodyPost.new
478
+ body = EmassClient::PacGet.new
474
479
  body.name = options[:name]
475
- body.type = options[:type]
480
+ body.workflow = options[:workflow]
476
481
  body.comments = options[:comments]
477
482
 
478
483
  body_array = Array.new(1, body)
479
484
 
480
- result = EmassClient::PacApi.new.add_s_ystem_p_ac(body_array, options[:systemId])
485
+ result = EmassClient::PACApi.new.add_system_pac(options[:systemId], body_array)
481
486
  puts to_output_hash(result).green
482
487
  rescue EmassClient::ApiError => e
483
- puts 'Exception when calling ApprovalChainApi->add_s_ystem_c_ac'.red
488
+ puts 'Exception when calling ApprovalChainApi->add_system_pac'.red
484
489
  puts to_output_hash(e)
485
490
  end
486
491
  end
487
492
 
488
- # TThe Static Code Scans endpoint provides the ability to upload application
493
+ # The Static Code Scans endpoint provides the ability to upload application
489
494
  # scan findings into a system's assets module.
490
495
  #
491
496
  # Application findings can also be cleared from the system.
@@ -505,15 +510,14 @@ module Emasser
505
510
  option :applicationName, type: :string, required: true, desc: 'Name of the software application that was assessed'
506
511
  option :version, type: :string, required: true, desc: 'The version of the application'
507
512
  option :codeCheckName, type: :string, required: true, desc: 'Name of the software vulnerability or weakness'
508
- option :scanDate, type: :numeric, required: false, desc: 'The findings scan date - Unix time format'
513
+ option :scanDate, type: :numeric, required: true, desc: 'The findings scan date - Unix time format'
509
514
  option :cweId, type: :string, required: true, desc: 'The Common Weakness Enumerator (CWE) identifier'
510
-
515
+ option :count, type: :numeric, required: true, desc: 'Number of instances observed for a specified finding'
511
516
  # Optional parameter/fields
512
517
  option :rawSeverity, type: :string, required: false, enum: %w[Low Medium Moderate High Critical]
513
- option :count, type: :numeric, required: false, desc: 'Number of instances observed for a specified finding'
514
518
 
515
519
  def add
516
- application = EmassClient::StaticCodeRequiredPostApplication.new
520
+ application = EmassClient::StaticCodeRequestPostBodyApplication.new
517
521
  application.application_name = options[:applicationName]
518
522
  application.version = options[:version]
519
523
 
@@ -521,19 +525,20 @@ module Emasser
521
525
  application_findings.code_check_name = options[:codeCheckName]
522
526
  application_findings.scan_date = options[:scanDate]
523
527
  application_findings.cwe_id = options[:cweId]
524
-
528
+ application_findings.count = options[:count]
525
529
  application_findings.raw_severity = options[:rawSeverity] if options[:rawSeverity]
526
- application_findings.count = options[:count] if options[:count]
527
530
 
528
- body = EmassClient::StaticCodeRequiredPost.new
531
+ app_findings_array = Array.new(1, application_findings)
532
+
533
+ body = EmassClient::StaticCodeRequestPostBody.new
529
534
  body.application = application
530
- body.application_findings = application_findings
535
+ body.application_findings = app_findings_array
531
536
 
532
537
  body_array = Array.new(1, body)
533
538
 
534
539
  begin
535
540
  result = EmassClient::StaticCodeScansApi
536
- .new.add_static_code_scans_by_system_id(body_array, options[:systemId])
541
+ .new.add_static_code_scans_by_system_id(options[:systemId], body_array)
537
542
  puts to_output_hash(result).green
538
543
  rescue EmassClient::ApiError => e
539
544
  puts 'Exception when calling StaticCodeScansApi->add_static_code_scans_by_system_id'.red
@@ -556,26 +561,28 @@ module Emasser
556
561
  def clear
557
562
  unless options[:clearFindings]
558
563
  puts 'To clear an application findings, the field clearFindings (--clearFindings) is required'.red
559
- puts NEW_LINE + 'Invoke "bundle exec exe/emasser post scan_findings help clear" for additional help'.yellow
564
+ puts SCAN_POST_HELP_MESSAGE.yellow
560
565
  exit
561
566
  end
562
567
 
563
- application = EmassClient::StaticCodeRequiredPostApplication.new
568
+ application = EmassClient::StaticCodeRequestPostBodyApplication.new
564
569
  application.application_name = options[:applicationName]
565
570
  application.version = options[:version]
566
571
 
567
572
  application_findings = EmassClient::StaticCodeApplication.new
568
573
  application_findings.clear_findings = options[:clearFindings]
569
574
 
570
- body = EmassClient::StaticCodeRequiredPost.new
575
+ app_findings_array = Array.new(1, application_findings)
576
+
577
+ body = EmassClient::StaticCodeRequestPostBody.new
571
578
  body.application = application
572
- body.application_findings = application_findings
579
+ body.application_findings = app_findings_array
573
580
 
574
581
  body_array = Array.new(1, body)
575
582
 
576
583
  begin
577
584
  result = EmassClient::StaticCodeScansApi
578
- .new.add_static_code_scans_by_system_id(body_array, options[:systemId])
585
+ .new.add_static_code_scans_by_system_id(options[:systemId], body_array)
579
586
  puts to_output_hash(result).green
580
587
  rescue EmassClient::ApiError => e
581
588
  puts 'Exception when calling StaticCodeScansApi->add_static_code_scans_by_system_id'.red
@@ -584,6 +591,186 @@ module Emasser
584
591
  end
585
592
  end
586
593
 
594
+ # The Cloud Resources endpoint provides the ability to upload (add)
595
+ # cloud resources and their scan results in the assets module for a system.
596
+ #
597
+ #
598
+ # Endpoint:
599
+ # /api/systems/{systemId}/cloud-resources-results - Upload cloud resources and their scan results
600
+ class CloudResource < SubCommandBase
601
+ def self.exit_on_failure?
602
+ true
603
+ end
604
+
605
+ desc 'add', 'Upload cloud resources and their scan results'
606
+ long_desc Help.text(:cloudresource_post_mapper)
607
+
608
+ # Required parameters/fields
609
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
610
+ option :provider, type: :string, required: true, desc: 'Cloud service provider name'
611
+ option :resourceId, type: :string, required: true, desc: 'Unique identifier/resource namespace for policy compliance result'
612
+ option :resourceName, type: :string, required: true, desc: 'Friendly name of Cloud resource'
613
+ option :resourceType, type: :string, required: true, desc: 'Type of Cloud resource'
614
+ # ComplianceResults Array Objects
615
+ option :cspPolicyDefinitionId, type: :string, required: true, desc: 'Unique identifier/compliance namespace for CSP/Resource\'s policy definition/compliance check'
616
+ option :isCompliant, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false'
617
+ option :policyDefinitionTitle, type: :string, required: true, desc: 'Friendly policy/compliance check title. Recommend short title'
618
+
619
+ # Optional parameter/fields
620
+ option :initiatedBy, type: :string, required: false, desc: 'Email of POC'
621
+ option :cspAccountId, type: :string, required: false, desc: 'System/owner\'s CSP account ID/number'
622
+ option :cspRegion, type: :string, required: false, desc: 'CSP region of system'
623
+ option :isBaseline, type: :boolean, required: false, default: true, desc: 'BOOLEAN - true or false'
624
+ # Tags Object
625
+ option :test, type: :string, required: false, desc: 'The test tag'
626
+ # ComplianceResults Array Objects
627
+ option :assessmentProcedure, type: :string, required: false, desc: 'Comma separated correlation to Assessment Procedure (i.e. CCI number for DoD Control Set)'
628
+ option :complianceCheckTimestamp, type: :numeric, required: false, desc: 'The compliance timestamp Unix date format.'
629
+ option :complianceReason, type: :string, required: false, desc: 'Reason/comments for compliance result'
630
+ option :control, type: :string, required: false, desc: 'Comma separated correlation to Security Control (e.g. exact NIST Control acronym)'
631
+ option :policyDeploymentName, type: :string, required: false, desc: 'Name of policy deployment'
632
+ option :policyDeploymentVersion, type: :string, required: false, desc: 'policyDeploymentVersion'
633
+ option :severity, type: :string, required: false, enum: %w[Low Medium Moderate High Critical]
634
+
635
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
636
+ def add
637
+ # Required and Optional main fields
638
+ body = {}
639
+ body[:provider] = options[:provider]
640
+ body[:resourceId] = options[:resourceId]
641
+ body[:resourceName] = options[:resourceName]
642
+ body[:resourceType] = options[:resourceType]
643
+
644
+ body[:initiatedBy] = options[:initiatedBy] if options[:initiatedBy]
645
+ body[:cspAccountId] = options[:cspAccountId] if options[:cspAccountId]
646
+ body[:cspRegion] = options[:cspRegion] if options[:cspRegion]
647
+ body[:isBaseline] = options[:isBaseline] if options[:isBaseline]
648
+
649
+ # Optional tags field
650
+ tags = {}
651
+ tags[:test] = options[:test] if options[:test]
652
+
653
+ # Required and Optional compliances results fields
654
+ compliance_results = {}
655
+ compliance_results[:cspPolicyDefinitionId] = options[:cspPolicyDefinitionId]
656
+ compliance_results[:isCompliant] = options[:isCompliant]
657
+ compliance_results[:policyDefinitionTitle] = options[:policyDefinitionTitle]
658
+ # Optional fields
659
+ compliance_results[:assessmentProcedure] = options[:assessmentProcedure] if options[:assessmentProcedure]
660
+ compliance_results[:complianceCheckTimestamp] = options[:complianceCheckTimestamp] if options[:complianceCheckTimestamp]
661
+ compliance_results[:complianceReason] = options[:complianceReason] if options[:complianceReason]
662
+ compliance_results[:control] = options[:control] if options[:control]
663
+ compliance_results[:policyDeploymentName] = options[:policyDeploymentName] if options[:policyDeploymentName]
664
+ compliance_results[:policyDeploymentVersion] = options[:policyDeploymentVersion] if options[:policyDeploymentVersion]
665
+ compliance_results[:severity] = options[:severity] if options[:severity]
666
+
667
+ compliance_results_array = Array.new(1, compliance_results)
668
+
669
+ body[:tags] = tags
670
+ body[:complianceResults] = compliance_results_array
671
+
672
+ body_array = Array.new(1, body)
673
+
674
+ begin
675
+ result = EmassClient::CloudResourcesApi
676
+ .new.add_cloud_resources_by_system_id(options[:systemId], body_array)
677
+ puts to_output_hash(result).green
678
+ rescue EmassClient::ApiError => e
679
+ puts 'Exception when calling StaticCodeScansApi->add_cloud_resources_by_system_id'.red
680
+ puts to_output_hash(e)
681
+ end
682
+ end
683
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
684
+ end
685
+
686
+ # The Containers endpoint provides the ability to upload (add)
687
+ # containers and their scan results in the assets module for a system.
688
+ #
689
+ #
690
+ # Endpoint:
691
+ # /api/systems/{systemId}/container-scan-results - Upload containers and their scan results
692
+ class Container < SubCommandBase
693
+ def self.exit_on_failure?
694
+ true
695
+ end
696
+
697
+ desc 'add', 'Upload containers and their scan results'
698
+ long_desc Help.text(:container_post_mapper)
699
+
700
+ # Required parameters/fields
701
+ option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
702
+ option :containerId, type: :string, required: true, desc: 'Unique identifier of the container'
703
+ option :containerName, type: :string, required: true, desc: 'Friendly name of the container'
704
+ option :time, type: :numeric, required: true, desc: 'Datetime of scan/result. Unix date format'
705
+ # Benchmarks Array Objects
706
+ option :benchmark, type: :string, required: true, desc: 'Identifier of the benchmark/grouping of compliance results'
707
+ # Benchmarks.Results Array Objects
708
+ option :lastSeen, type: :numeric, required: true, desc: 'Date last seen, Unix date format'
709
+ option :ruleId, type: :string, required: true, desc: 'Identifier for the compliance result, vulnerability, etc. the result is for'
710
+ option :status, type: :string, required: true, enum: ['Pass', 'Fail', 'Other', 'Not Reviewed', 'Not Checked', 'Not Applicable']
711
+
712
+ # Optional parameter/fields
713
+ option :namespace, type: :string, required: false, desc: 'Namespace of container in container orchestration'
714
+ option :podIp, type: :string, required: false, desc: 'IP address of the pod'
715
+ option :podName, type: :string, required: false, desc: 'Name of pod (e.g. Kubernetes pod)'
716
+ # Tags Object
717
+ option :test, type: :string, required: false, desc: 'The test tag'
718
+ # Benchmarks Array Objects
719
+ option :isBaseline, type: :boolean, required: false, default: true, desc: 'BOOLEAN - true or false'
720
+ # Benchmarks.Results Array Objects
721
+ option :message, type: :string, required: false, desc: 'Benchmark result comments'
722
+
723
+ # rubocop:disable Metrics/CyclomaticComplexity
724
+ def add
725
+ # Required and Optional main fields
726
+ body = {}
727
+ body[:containerId] = options[:containerId]
728
+ body[:containerName] = options[:containerName]
729
+ body[:time] = options[:time]
730
+ body[:namespace] = options[:namespace] if options[:namespace]
731
+ body[:podIp] = options[:podIp] if options[:podIp]
732
+ body[:podName] = options[:podName] if options[:podName]
733
+
734
+ # Optional tags field
735
+ tags = {}
736
+ tags[:test] = options[:test] if options[:test]
737
+
738
+ # Required and Optional Benchmarks fields
739
+ benchmarks = {}
740
+ benchmarks[:benchmark] = options[:benchmark]
741
+ # Optional fields
742
+ benchmarks[:isBaseline] = options[:isBaseline] if options[:isBaseline]
743
+
744
+ # Required and Optional Benchmarks.Results
745
+ benchmarks_results = {}
746
+ benchmarks_results[:lastSeen] = options[:lastSeen]
747
+ benchmarks_results[:ruleId] = options[:ruleId]
748
+ benchmarks_results[:status] = options[:status]
749
+ benchmarks_results[:message] = options[:message] if options[:message]
750
+
751
+ # Add Benchmark results to an array and add array to benchmarks object
752
+ benchmarks_results_array = Array.new(1, benchmarks_results)
753
+ benchmarks[:results] = benchmarks_results_array # = Array.new(1, benchmarks_results)
754
+ # Add benchmarks object to an array
755
+ benchmarks_array = Array.new(1, benchmarks)
756
+ # Add tags and benchmark ojects to body object
757
+ body[:tags] = tags
758
+ body[:benchmarks] = benchmarks_array
759
+
760
+ body_array = Array.new(1, body)
761
+
762
+ begin
763
+ result = EmassClient::ContainersApi
764
+ .new.add_container_sans_by_system_id(options[:systemId], body_array)
765
+ puts to_output_hash(result).green
766
+ rescue EmassClient::ApiError => e
767
+ puts 'Exception when calling StaticCodeScansApi->add_container_sans_by_system_id'.red
768
+ puts to_output_hash(e)
769
+ end
770
+ end
771
+ # rubocop:enable Metrics/CyclomaticComplexity
772
+ end
773
+
587
774
  class Post < SubCommandBase
588
775
  desc 'test_results', 'Add system Test Results'
589
776
  subcommand 'test_results', TestResults
@@ -605,5 +792,11 @@ module Emasser
605
792
 
606
793
  desc 'scan_findings', 'Upload static code scans'
607
794
  subcommand 'scan_findings', ScanFindings
795
+
796
+ desc 'cloud_resource', 'Upload cloud resource and their scan results'
797
+ subcommand 'cloud_resource', CloudResource
798
+
799
+ desc 'container', 'Upload container and their scan results'
800
+ subcommand 'container', Container
608
801
  end
609
802
  end