emasser 3.10.0 → 3.22.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.env-example +18 -12
  3. data/.github/workflows/anchore-syft.yml +38 -0
  4. data/.github/workflows/codeql-analysis.yml +4 -4
  5. data/.github/workflows/gh-pages.yml +1 -1
  6. data/.github/workflows/push-to-docker-mail.yml +6 -7
  7. data/.github/workflows/push-to-docker.yml +6 -6
  8. data/.github/workflows/release.yml +1 -1
  9. data/.github/workflows/rubocop.yml +2 -2
  10. data/.github/workflows/test-cli.yml +5 -5
  11. data/.mergify.yml +11 -11
  12. data/.rubocop.yml +1 -1
  13. data/CHANGELOG.md +58 -2
  14. data/Dockerfile +6 -4
  15. data/Gemfile.lock +108 -64
  16. data/README.md +23 -22
  17. data/docs/features.md +682 -539
  18. data/emasser.gemspec +19 -13
  19. data/images/emasser_architecture.png +0 -0
  20. data/lib/emasser/configuration.rb +136 -35
  21. data/lib/emasser/constants.rb +4 -4
  22. data/lib/emasser/delete.rb +145 -15
  23. data/lib/emasser/errors.rb +9 -0
  24. data/lib/emasser/get.rb +891 -251
  25. data/lib/emasser/help/approvalCac_post_mapper.md +6 -5
  26. data/lib/emasser/help/approvalPac_post_mapper.md +1 -5
  27. data/lib/emasser/help/artifacts_del_mapper.md +2 -2
  28. data/lib/emasser/help/artifacts_post_mapper.md +23 -34
  29. data/lib/emasser/help/artifacts_put_mapper.md +28 -9
  30. data/lib/emasser/help/cloudresource_post_mapper.md +4 -3
  31. data/lib/emasser/help/controls_put_mapper.md +24 -16
  32. data/lib/emasser/help/hardware_post_mapper.md +41 -0
  33. data/lib/emasser/help/hardware_put_mapper.md +42 -0
  34. data/lib/emasser/help/milestone_del_mapper.md +1 -1
  35. data/lib/emasser/help/milestone_post_mapper.md +3 -1
  36. data/lib/emasser/help/milestone_put_mapper.md +1 -8
  37. data/lib/emasser/help/poam_del_mapper.md +1 -1
  38. data/lib/emasser/help/poam_post_mapper.md +40 -14
  39. data/lib/emasser/help/poam_put_mapper.md +43 -18
  40. data/lib/emasser/help/software_post_mapper.md +59 -0
  41. data/lib/emasser/help/software_put_mapper.md +60 -0
  42. data/lib/emasser/help/staticcode_post_mapper.md +0 -4
  43. data/lib/emasser/help/testresults_post_mapper.md +8 -11
  44. data/lib/emasser/output_converters.rb +64 -46
  45. data/lib/emasser/post.rb +603 -231
  46. data/lib/emasser/put.rb +453 -193
  47. data/lib/emasser/version.rb +1 -1
  48. metadata +51 -33
  49. data/images/emasser_architecture.jpg +0 -0
  50. data/images/emasser_diagram-Page-3.jpg +0 -0
data/emasser.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = 'Provide an automated capability for invoving eMASS API endpoints'
13
13
  spec.description = 'The emasser can be used as a gem or used from the command line (CL) to access eMASS endpoints via their API.'
14
14
  spec.homepage = 'https://saf.mitre.org'
15
- spec.required_ruby_version = Gem::Requirement.new('~> 2.5')
15
+ spec.required_ruby_version = Gem::Requirement.new('~> 3.2')
16
16
 
17
17
  # Specify which files should be added to the gem when it is released.
18
18
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -24,21 +24,27 @@ Gem::Specification.new do |spec|
24
24
  # References: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-rubygems-registry
25
25
  spec.metadata = { "github_repo" => "ssh://github.com/mitre/emasser" }
26
26
 
27
+ # ---- Run Time Dependencies
27
28
  spec.add_runtime_dependency 'activesupport', '>= 6.1.4', '< 7.1.0'
28
- spec.add_runtime_dependency 'colorize', '~> 0.8.1'
29
- spec.add_runtime_dependency 'dotenv', '~> 2.7.6'
29
+ spec.add_runtime_dependency 'colorize', '~> 1.1.0'
30
+ spec.add_runtime_dependency 'dotenv', '~> 3.1.2'
30
31
  spec.add_runtime_dependency 'rubyzip', '~> 2.3.2'
31
- spec.add_runtime_dependency 'thor', '~> 1.1.0'
32
- spec.add_runtime_dependency 'emass_client', '~> 3.10'
32
+ spec.add_runtime_dependency 'thor', '~> 1.3.0'
33
+ spec.add_runtime_dependency 'emass_client', '~> 3.20'
33
34
 
34
- spec.add_development_dependency 'bundler', '~> 2.3'
35
- spec.add_development_dependency 'bundler-audit', '~> 0.7'
35
+ # ---- Development Dependencies
36
+ spec.add_development_dependency 'bundler', '~> 2.5'
37
+ spec.add_development_dependency 'bundler-audit', '~> 0.9'
38
+ # byebug - does not work with ruby 3.3.3
36
39
  spec.add_development_dependency 'byebug', '~> 11.1.3'
37
- spec.add_development_dependency 'rspec', '~> 3.10.0'
38
- spec.add_development_dependency 'yaml', '~> 0.2.0'
39
- spec.add_development_dependency 'rake', '~> 13.0'
40
+ spec.add_development_dependency 'rspec', '~> 3.13.0'
41
+ spec.add_development_dependency 'yaml', '~> 0.3.0'
42
+ spec.add_development_dependency 'rake', '~> 13.2.1'
40
43
  spec.add_development_dependency 'rubocop', '~> 1.7'
41
- spec.add_development_dependency 'rubocop-minitest', '~> 0.10'
42
- spec.add_development_dependency 'rubocop-performance', '~> 1.11'
43
- spec.add_development_dependency 'rubocop-rake', '~> 0.5'
44
+ # spec.add_development_dependency 'rubocop', '~> 1.64'
45
+ spec.add_development_dependency 'rubocop-minitest', '~> 0.35'
46
+ spec.add_development_dependency 'rubocop-performance', '~> 1.21'
47
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
48
+ # solargraph - does not work with ruby 3.3.3
49
+ spec.add_development_dependency 'solargraph', '~> 0.50'
44
50
  end
Binary file
@@ -2,48 +2,149 @@
2
2
 
3
3
  module Emasser
4
4
  require 'emasser/errors'
5
+ require 'fileutils'
5
6
 
6
7
  class Configuration
7
- # rubocop: disable Style/RaiseArgs
8
+ # rubocop: disable Style/GuardClause, Lint/NonAtomicFileOperation
9
+ # rubocop: disable Style/RescueStandardError, Lint/DuplicateBranch, Style/RedundantReturn
10
+ # rubocop: disable Style/RaiseArgs, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
8
11
  def self.raise_unless_present(env)
9
- ENV.fetch(env) { raise Emasser::ConfigurationMissingError.new(env) }
12
+ env_value = ENV.fetch(env) { raise Emasser::ConfigurationMissingError.new(env) }
13
+ if env_value.empty?
14
+ raise Emasser::ConfigurationEmptyValueError.new(env)
15
+ end
16
+
17
+ return env_value
18
+ rescue Emasser::ConfigurationEmptyValueError => e
19
+ unless (ARGV[0].to_s.include? 'post') && (ARGV[1].to_s.include? 'register') && (env.include? 'EMASSER_API_KEY')
20
+ puts "\n", e.message.red
21
+ show = Configuration.new
22
+ show.emasser_env_msg
23
+ exit
24
+ end
10
25
  rescue Emasser::ConfigurationMissingError => e
11
- if (ARGV[0].to_s.include? '-v') || (ARGV[0].to_s.include? '-V')
12
- puts "emasser version: #{Emasser::VERSION}".green
13
- else
26
+ unless (ARGV[0].to_s.include? 'post') && (ARGV[1].to_s.include? 'register') && (env.include? 'EMASSER_API_KEY')
14
27
  puts "\n", e.message.red
15
- puts 'Create a .env file containing required variables, place it in the root directory where the emasser command is executed'.yellow
16
- puts 'Required environment variables are:'.yellow
17
- puts ' export EMASSER_API_KEY=<API key>'.green
18
- puts ' export EMASSER_USER_UID=<unique identifier of the eMASS user EMASSER_API_KEY belongs to>'.green
19
- puts ' export EMASSER_HOST_URL=<FQDN of the eMASS server>'.green
20
- puts ' export EMASSER_KEY_FILE_PATH=<path to your emass key in PEM format>'.green
21
- puts ' export EMASSER_CERT_FILE_PATH=<path to your emass certficate in PEM format>'.green
22
- puts ' export EMASSER_KEY_FILE_PASSWORD=<password for the key given in EMASSER_KEY_FILE_PATH>'.green, "\n"
23
- puts 'See emasser environment variables requirements in emasser CLI Features for more information (https://mitre.github.io/emasser/docs/features.html).', "\n"
28
+ show = Configuration.new
29
+ show.emasser_env_msg
30
+ exit
24
31
  end
25
- exit
26
32
  end
27
- # rubocop: enable Style/RaiseArgs
28
-
29
- # rubocop: disable Style/TernaryParentheses, Style/IfWithBooleanLiteralBranches
30
- EmassClient.configure do |config|
31
- # Required env variables
32
- config.api_key['api-key'] = raise_unless_present('EMASSER_API_KEY')
33
- config.api_key['user-uid'] = raise_unless_present('EMASSER_USER_UID')
34
- config.scheme = 'https'
35
- config.base_path = '/'
36
- config.server_index = nil
37
- config.host = raise_unless_present('EMASSER_HOST_URL')
38
- config.key_file = raise_unless_present('EMASSER_KEY_FILE_PATH')
39
- config.cert_file = raise_unless_present('EMASSER_CERT_FILE_PATH')
40
- config.key_password = raise_unless_present('EMASSER_KEY_FILE_PASSWORD')
41
- # Optional env variables
42
- config.client_side_validation = (ENV.fetch('EMASSER_CLIENT_SIDE_VALIDATION', 'true').eql? 'true') ? true : false
43
- config.verify_ssl = (ENV.fetch('EMASSER_VERIFY_SSL', 'true').eql? 'true') ? true : false
44
- config.verify_ssl_host = (ENV.fetch('EMASSER_VERIFY_SSL_HOST', 'true').eql? 'true') ? true : false
45
- config.debugging = (ENV.fetch('EMASSER_DEBUGGING', 'false') == 'false') ? false : true
33
+
34
+ def self.check_folder_exists(env)
35
+ folder_path = ENV.fetch(env) # raises error if EMASSER_DOWNLOAD_DIR is missing
36
+ if folder_path.empty?
37
+ # Create a default downloads folder
38
+ raise CreateDefaultDownloadDirectory
39
+ else
40
+ # Create the folder if does not exist
41
+ unless Dir.exist?(folder_path)
42
+ FileUtils.mkdir_p('eMASSerDownloads')
43
+ end
44
+ return folder_path
45
+ end
46
+ rescue # CreateDefaultDownloadDirectory
47
+ # Create the default folder if does not exist
48
+ unless Dir.exist?('eMASSerDownloads')
49
+ FileUtils.mkdir_p('eMASSerDownloads')
50
+ end
51
+ return 'eMASSerDownloads'
52
+ end
53
+ # rubocop: enable Style/GuardClause, Lint/NonAtomicFileOperation
54
+ # rubocop: enable Style/RescueStandardError, Lint/DuplicateBranch, Style/RedundantReturn
55
+ # rubocop: enable Style/RaiseArgs, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
56
+
57
+ def emasser_env_msg
58
+ puts 'Create (or update) the configuration (.env) file and place it in the root directory where the emasser command is executed'.yellow
59
+ puts 'Required environment variables:'.yellow
60
+ puts ' export EMASSER_API_KEY=<API key>'.green
61
+ puts ' export EMASSER_HOST_URL=<FQDN of the eMASS server>'.green
62
+ puts ' export EMASSER_KEY_FILE_PATH=<path to your eMASS key in PEM format>'.green
63
+ puts ' export EMASSER_CERT_FILE_PATH=<path to your eMASS certficate in PEM format>'.green
64
+ puts ' export EMASSER_KEY_FILE_PASSWORD=<password for the key given in EMASSER_KEY_FILE_PATH>'.green
65
+ puts 'Note: '.yellow + 'EMASSER_API_KEY is acquired by invoking the "emasser post register cert" API command'.cyan, "\n"
66
+ puts 'Actionable (POST,PUT,DELETE) variable required by some eMASS instances:'.yellow
67
+ puts ' export EMASSER_USER_UID=<unique identifier of the eMASS user EMASSER_API_KEY belongs to>'.green
68
+ puts "\n"
69
+ puts 'See eMASSer environment variables requirements in eMASSer CLI Features for more information (https://mitre.github.io/emasser/docs/features.html).'.yellow
70
+ end
71
+
72
+ def emasser_pki_help
73
+ puts 'eMASSer PKI Certificate Requirements:'.yellow
74
+ puts 'eMASSer uses a client (signed certificate) and key (private key to the certificate) certificate for authenticating to eMASS.'.cyan
75
+ puts 'Both files are pem (Privacy-Enhanced Mail) text-based containers using base-64 encoding. The key file requires a passphrase.'.cyan
76
+ puts "\n"
77
+ puts 'The following variables must be provided on the .env (configuration) file:'.yellow
78
+ puts ' EMASSER_HOST_URL - The eMASS host URL'.cyan
79
+ puts ' EMASSER_CERT_FILE_PATH - The client certificate (.pem) file (full path is required)'.cyan
80
+ puts ' EMASSER_KEY_FILE_PATH - The private key for the certificate (.pem) file (full path is required)'.cyan
81
+ puts ' EMASSER_KEY_FILE_PASSWORD - The key file passphrase value if the key file password has been set'.cyan
82
+ puts 'IMPORTANT: If using a self signed certificate in the certificate chain the optional "EMASSER_VERIFY_SSL" variable must be set to false.'.red
83
+ end
84
+
85
+ if (ARGV[0].to_s.downcase.include? '-v') || (ARGV[0].to_s.downcase.include? '--V')
86
+ puts "emasser version: #{Emasser::VERSION}".green
87
+ exit
88
+ elsif ARGV[0].to_s.downcase.include? 'hello'
89
+ user_name = ENV.fetch('USERNAME', 'rookie')
90
+ puts "Hello #{user_name} - enjoy using eMASSer version #{Emasser::VERSION}!".cyan
91
+ exit
92
+ elsif ARGV[0].to_s.downcase.include? 'auth'
93
+ puts 'eMASS Authentication/Authorization Requirements:'.yellow
94
+ puts 'Every call to the eMASS API (via eMASSer) requires the use of a client certificate and an API key.'.cyan
95
+ puts 'A PKI-valid/trusted client certificate must be obtained from the owning eMASS instances that eMASSer is connecting to.'.cyan
96
+ puts 'An API key is also required. To obtain the API key, after configuring the PKI certificate, than'.cyan
97
+ puts 'invoke the following API command to retrieve the API key: "emasser post register cert")'.cyan
98
+ puts "\n"
99
+ puts 'eMASS Authentication Errors:'.red
100
+ puts 'If the API receives an untrusted certificate, a 403 forbidden response code will be returned.'.cyan
101
+ puts 'If an invalid api-key or combination of client certificate and api-key (from the registered account) is received, a 401 unauthorized response code will be returned.'.cyan
102
+ puts "\n"
103
+ show = Configuration.new
104
+ show.emasser_pki_help
105
+ exit
106
+ elsif (ARGV[0].to_s.downcase.include? 'help') || (ARGV[0].to_s.include? '-')
107
+ puts 'eMASSer Help'.yellow
108
+ puts 'eMASSer makes use of an environment configuration (.env) file for required and optional variables.'.cyan
109
+ puts 'The configuration file containing required variables must be located in the root directory where the eMASSer command is executed.'.cyan
110
+ puts "\n"
111
+ show = Configuration.new
112
+ show.emasser_pki_help
113
+ puts "\n"
114
+ puts 'See eMASSer environment variables requirements in eMASSer CLI Features for more information (https://mitre.github.io/emasser/docs/features.html).'.yellow
115
+ puts 'For eMASS Authentication Requirements invoke the eMASSer API command with the [auth]entication parameter (emasser auth)'.green
116
+ exit
117
+ elsif ARGV.empty?
118
+ puts 'eMASSer commands:'.yellow
119
+ puts ' emasser [get, put, post, delet] or [-h, --h, -v, -V, --version, --Version] or [help, Authentication, Authorization] '.yellow
120
+ exit
121
+ else
122
+ # rubocop: disable Style/TernaryParentheses, Style/IfWithBooleanLiteralBranches, Style/RedundantCondition
123
+ EmassClient.configure do |config|
124
+ # -----------------------------------------------------------------------
125
+ # Required env variables
126
+ config.scheme = 'https'
127
+ config.base_path = '/'
128
+ config.server_index = nil
129
+ config.api_key['api-key'] = raise_unless_present('EMASSER_API_KEY')
130
+ config.host = raise_unless_present('EMASSER_HOST_URL')
131
+ config.key_file = raise_unless_present('EMASSER_KEY_FILE_PATH')
132
+ config.cert_file = raise_unless_present('EMASSER_CERT_FILE_PATH')
133
+ config.key_password = raise_unless_present('EMASSER_KEY_FILE_PASSWORD')
134
+
135
+ # Some eMASS instances may not require this variable, others
136
+ # may required for actionable (POST,PUT,DELETE) requests
137
+ config.api_key['user-uid'] = ENV.fetch('EMASSER_USER_UID', '')
138
+
139
+ # -----------------------------------------------------------------------
140
+ # Optional env variables
141
+ config.client_side_validation = (ENV.fetch('EMASSER_CLIENT_SIDE_VALIDATION', 'true').eql? 'true') ? true : false
142
+ config.verify_ssl = (ENV.fetch('EMASSER_VERIFY_SSL', 'true').eql? 'true') ? true : false
143
+ config.verify_ssl_host = (ENV.fetch('EMASSER_VERIFY_SSL_HOST', 'true').eql? 'true') ? true : false
144
+ config.debugging = (ENV.fetch('EMASSER_DEBUGGING', 'false') == 'false') ? false : true
145
+ config.temp_folder_path = check_folder_exists('EMASSER_DOWNLOAD_DIR')
146
+ end
147
+ # rubocop: enable Style/TernaryParentheses, Style/IfWithBooleanLiteralBranches, Style/RedundantCondition
46
148
  end
47
- # rubocop: enable Style/TernaryParentheses, Style/IfWithBooleanLiteralBranches
48
149
  end
49
150
  end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Emasser
4
+ GET_REGISTER_RETURN_TYPE = {
5
+ debug_return_type: 'Object'
6
+ }.freeze
7
+
4
8
  GET_SYSTEM_ID_QUERY_PARAMS = {
5
9
  include_package: false,
6
10
  include_ditpr_metrics: false,
@@ -19,8 +23,4 @@ module Emasser
19
23
  GET_WORKFLOWINSTANCES_RETURN_TYPE = {
20
24
  debug_return_type: 'Object'
21
25
  }.freeze
22
-
23
- DEL_MILESTONES_RETURN_TYPE = {
24
- debug_return_type: 'Object'
25
- }.freeze
26
26
  end
@@ -51,11 +51,11 @@ module Emasser
51
51
  long_desc Help.text(:poam_del_mapper)
52
52
 
53
53
  # Required parameters/fields
54
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
55
- option :poamId, type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
54
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
55
+ option :poamId, aliases: '-p', type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
56
56
 
57
57
  def remove
58
- body = EmassClient::PoamGet.new
58
+ body = EmassClient::PoamRequestDeleteBodyInner.new
59
59
  body.poam_id = options[:poamId]
60
60
  body_array = Array.new(1, body)
61
61
 
@@ -80,26 +80,24 @@ module Emasser
80
80
  long_desc Help.text(:milestone_del_mapper)
81
81
 
82
82
  # Required parameters/fields
83
- option :systemId, type: :numeric, required: true,
83
+ option :systemId, aliases: '-s', type: :numeric, required: true,
84
84
  desc: 'A numeric value representing the system identification'
85
- option :poamId, type: :numeric, required: true,
85
+ option :poamId, aliases: '-p', type: :numeric, required: true,
86
86
  desc: 'A numeric value representing the poam identification'
87
- option :milestoneId, type: :numeric, required: true,
87
+ option :milestoneId, aliases: '-m', type: :numeric, required: true,
88
88
  desc: 'A numeric value representing the milestone identification'
89
89
 
90
90
  def remove
91
- body = EmassClient::MilestonesGet.new
91
+ body = EmassClient::MilestonesRequestDeleteBodyInner.new
92
92
  body.milestone_id = options[:milestoneId]
93
93
  body_array = Array.new(1, body)
94
94
 
95
- # Getting an empty return when utilizing the default return type - using 'Object' as return type
96
- opts = Emasser::DEL_MILESTONES_RETURN_TYPE
97
-
98
- result = EmassClient::MilestonesApi.new.delete_milestone(options[:systemId], options[:poamId], body_array, opts)
95
+ result = EmassClient::MilestonesApi.new.delete_milestone(options[:systemId], options[:poamId], body_array)
96
+ # The server returns an empty object upon successfully deleting a milestone.
99
97
  puts to_output_hash(result).green
100
98
  rescue EmassClient::ApiError => e
101
99
  puts 'Exception when calling MilestonesApi->delete_milestone'.red
102
- puts to_output_hash(e).yellow
100
+ puts to_output_hash(e)
103
101
  end
104
102
  end
105
103
 
@@ -113,11 +111,11 @@ module Emasser
113
111
  end
114
112
 
115
113
  desc 'remove', 'Delete one or many artifacts in a system'
116
- long_desc Help.text(:artifact_del_mapper)
114
+ long_desc Help.text(:artifacts_del_mapper)
117
115
 
118
116
  # Required parameters/fields
119
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
120
- option :files, type: :array, required: true, desc: 'Artifact file(s) to remove from the given system'
117
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
118
+ option :files, aliases: '-f', type: :array, required: true, desc: 'Artifact file(s) to remove from the given system'
121
119
 
122
120
  def remove
123
121
  body_array = []
@@ -135,6 +133,126 @@ module Emasser
135
133
  end
136
134
  end
137
135
 
136
+ # Remove one or many hardware assets in a system
137
+ #
138
+ # Endpoint:
139
+ # /api/systems/{systemId}/hw-baseline
140
+ class Hardware < SubCommandBase
141
+ def self.exit_on_failure?
142
+ true
143
+ end
144
+
145
+ desc 'remove', 'Delete one or many hardware assets in a system'
146
+
147
+ # Required parameters/fields
148
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
149
+ option :hardwareIds, aliases: '-w', type: :array, required: true, desc: 'GUID identifying the specific hardware asset'
150
+
151
+ def remove
152
+ body_array = []
153
+ options[:hardwareIds].each do |hardware|
154
+ obj = {}
155
+ obj[:hardwareId] = hardware
156
+ body_array << obj
157
+ end
158
+
159
+ result = EmassClient::HardwareBaselineApi.new.delete_hw_baseline_assets(options[:systemId], body_array)
160
+ puts to_output_hash(result).green
161
+ rescue EmassClient::ApiError => e
162
+ puts 'Exception when calling HardwareBaselineApi->delete_hw_baseline_assets'.red
163
+ puts to_output_hash(e)
164
+ end
165
+ end
166
+
167
+ # Remove one or many software assets in a system
168
+ #
169
+ # Endpoint:
170
+ # /api/systems/{systemId}/sw-baseline
171
+ class Software < SubCommandBase
172
+ def self.exit_on_failure?
173
+ true
174
+ end
175
+
176
+ desc 'remove', 'Delete one or many software assets in a system'
177
+
178
+ # Required parameters/fields
179
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
180
+ option :softwareIds, aliases: '-w', type: :array, required: true, desc: 'GUID identifying the specific software asset'
181
+
182
+ def remove
183
+ body_array = []
184
+ options[:softwareIds].each do |software|
185
+ obj = {}
186
+ obj[:softwareId] = software
187
+ body_array << obj
188
+ end
189
+
190
+ result = EmassClient::SoftwareBaselineApi.new.delete_sw_baseline_assets(options[:systemId], body_array)
191
+ puts to_output_hash(result).green
192
+ rescue EmassClient::ApiError => e
193
+ puts 'Exception when calling SoftwareBaselineApi->delete_sw_baseline_assets'.red
194
+ puts to_output_hash(e)
195
+ end
196
+ end
197
+
198
+ # The Cloud Resource Results endpoint provides the ability to remove
199
+ # cloud resources and their scan results in the assets module for a system.
200
+ #
201
+ # Endpoint:
202
+ # /api/systems/{systemId}/cloud-resource-results
203
+ class CloudResource < SubCommandBase
204
+ def self.exit_on_failure?
205
+ true
206
+ end
207
+
208
+ desc 'remove', 'Delete one or many Cloud Resources and their scan results in the assets module for a system'
209
+
210
+ # Required parameters/fields
211
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
212
+ option :resourceId, aliases: '-r', type: :string, required: true, desc: 'Unique identifier/resource namespace for policy compliance result'
213
+
214
+ def remove
215
+ body = EmassClient::CloudResourcesDeleteBodyInner.new
216
+ body.resource_id = options[:resourceId]
217
+ body_array = Array.new(1, body)
218
+
219
+ result = EmassClient::CloudResourceResultsApi.new.delete_cloud_resources(options[:systemId], body_array)
220
+ puts to_output_hash(result).green
221
+ rescue EmassClient::ApiError => e
222
+ puts 'Exception when calling CloudResourceResultsApi->delete_cloud_resources'.red
223
+ puts to_output_hash(e)
224
+ end
225
+ end
226
+
227
+ # The Container Scan Results endpoint provides the ability to remove
228
+ # containers and their scan results in the assets module for a system.
229
+ #
230
+ # Endpoint:
231
+ # /api/systems/{systemId}/container-scan-results
232
+ class Container < SubCommandBase
233
+ def self.exit_on_failure?
234
+ true
235
+ end
236
+
237
+ desc 'remove', 'Delete one or many containers scan results in the assets module for a system'
238
+
239
+ # Required parameters/fields
240
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
241
+ option :containerId, aliases: '-c', type: :string, required: true, desc: 'Unique identifier of the container'
242
+
243
+ def remove
244
+ body = EmassClient::ContainerResourcesDeleteBodyInner.new
245
+ body.container_id = options[:containerId]
246
+ body_array = Array.new(1, body)
247
+
248
+ result = EmassClient::ContainerScanResultsApi.new.delete_container_sans(options[:systemId], body_array)
249
+ puts to_output_hash(result).green
250
+ rescue EmassClient::ApiError => e
251
+ puts 'Exception when calling ContainerScanResultsApi->delete_container_sans'.red
252
+ puts to_output_hash(e)
253
+ end
254
+ end
255
+
138
256
  class Delete < SubCommandBase
139
257
  desc 'poams', 'Delete Plan of Action and Milestones (POA&M) items for a system'
140
258
  subcommand 'poams', Poams
@@ -144,5 +262,17 @@ module Emasser
144
262
 
145
263
  desc 'artifacts', 'Delete system Artifacts'
146
264
  subcommand 'artifacts', Artifacts
265
+
266
+ desc 'hardware', 'Delete one or many hardware assets to a system'
267
+ subcommand 'hardware', Hardware
268
+
269
+ desc 'software', 'Delete one or many software assets to a system'
270
+ subcommand 'software', Software
271
+
272
+ desc 'cloud_resource', 'Delete cloud resource and their scan results'
273
+ subcommand 'cloud_resource', CloudResource
274
+
275
+ desc 'container', 'Delete container and their scan results'
276
+ subcommand 'container', Container
147
277
  end
148
278
  end
@@ -11,4 +11,13 @@ module Emasser
11
11
  super("#{message} #{@config}")
12
12
  end
13
13
  end
14
+
15
+ class ConfigurationEmptyValueError < Error
16
+ attr_reader :config
17
+
18
+ def initialize(config = 'an option', message = 'Environment variable cannot be an empty:')
19
+ @config = config
20
+ super("#{message} #{@config}")
21
+ end
22
+ end
14
23
  end