emasser 3.12.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 (48) 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 +2 -2
  5. data/.github/workflows/push-to-docker-mail.yml +1 -2
  6. data/.github/workflows/push-to-docker.yml +2 -2
  7. data/.github/workflows/rubocop.yml +1 -1
  8. data/.github/workflows/test-cli.yml +4 -4
  9. data/.mergify.yml +11 -11
  10. data/.rubocop.yml +1 -1
  11. data/CHANGELOG.md +6 -0
  12. data/Dockerfile +6 -4
  13. data/Gemfile.lock +108 -64
  14. data/README.md +7 -7
  15. data/docs/features.md +492 -524
  16. data/emasser.gemspec +19 -13
  17. data/images/emasser_architecture.png +0 -0
  18. data/lib/emasser/configuration.rb +136 -35
  19. data/lib/emasser/constants.rb +4 -0
  20. data/lib/emasser/delete.rb +75 -7
  21. data/lib/emasser/errors.rb +9 -0
  22. data/lib/emasser/get.rb +610 -177
  23. data/lib/emasser/help/approvalCac_post_mapper.md +6 -5
  24. data/lib/emasser/help/approvalPac_post_mapper.md +1 -5
  25. data/lib/emasser/help/artifacts_del_mapper.md +2 -2
  26. data/lib/emasser/help/artifacts_post_mapper.md +23 -34
  27. data/lib/emasser/help/artifacts_put_mapper.md +28 -9
  28. data/lib/emasser/help/cloudresource_post_mapper.md +4 -3
  29. data/lib/emasser/help/controls_put_mapper.md +24 -16
  30. data/lib/emasser/help/hardware_post_mapper.md +41 -0
  31. data/lib/emasser/help/hardware_put_mapper.md +42 -0
  32. data/lib/emasser/help/milestone_del_mapper.md +1 -1
  33. data/lib/emasser/help/milestone_post_mapper.md +3 -1
  34. data/lib/emasser/help/milestone_put_mapper.md +1 -8
  35. data/lib/emasser/help/poam_del_mapper.md +1 -1
  36. data/lib/emasser/help/poam_post_mapper.md +40 -14
  37. data/lib/emasser/help/poam_put_mapper.md +43 -18
  38. data/lib/emasser/help/software_post_mapper.md +59 -0
  39. data/lib/emasser/help/software_put_mapper.md +60 -0
  40. data/lib/emasser/help/staticcode_post_mapper.md +0 -4
  41. data/lib/emasser/help/testresults_post_mapper.md +8 -11
  42. data/lib/emasser/output_converters.rb +50 -42
  43. data/lib/emasser/post.rb +603 -231
  44. data/lib/emasser/put.rb +453 -193
  45. data/lib/emasser/version.rb +1 -1
  46. metadata +51 -33
  47. data/images/emasser_architecture.jpg +0 -0
  48. 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.12'
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,
@@ -125,7 +125,7 @@ module Emasser
125
125
  body_array << obj
126
126
  end
127
127
 
128
- result = EmassClient::ArtifactsApi.new.delete_artifact(body_array, options[:systemId])
128
+ result = EmassClient::ArtifactsApi.new.delete_artifact(options[:systemId], body_array)
129
129
  puts to_output_hash(result).green
130
130
  rescue EmassClient::ApiError => e
131
131
  puts 'Exception when calling ArtifactsApi->delete_artifact'.red
@@ -133,11 +133,73 @@ module Emasser
133
133
  end
134
134
  end
135
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
+
136
198
  # The Cloud Resource Results endpoint provides the ability to remove
137
199
  # cloud resources and their scan results in the assets module for a system.
138
200
  #
139
201
  # Endpoint:
140
- # /api/systems/{systemId}/cloud-resource-results - Remove one or many cloud resources in a system
202
+ # /api/systems/{systemId}/cloud-resource-results
141
203
  class CloudResource < SubCommandBase
142
204
  def self.exit_on_failure?
143
205
  true
@@ -147,7 +209,7 @@ module Emasser
147
209
 
148
210
  # Required parameters/fields
149
211
  option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
150
- option :resourceId, aliases: '-c', type: :string, required: true, desc: 'Unique identifier/resource namespace for policy compliance result'
212
+ option :resourceId, aliases: '-r', type: :string, required: true, desc: 'Unique identifier/resource namespace for policy compliance result'
151
213
 
152
214
  def remove
153
215
  body = EmassClient::CloudResourcesDeleteBodyInner.new
@@ -157,7 +219,7 @@ module Emasser
157
219
  result = EmassClient::CloudResourceResultsApi.new.delete_cloud_resources(options[:systemId], body_array)
158
220
  puts to_output_hash(result).green
159
221
  rescue EmassClient::ApiError => e
160
- puts 'Exception when calling MilestonesApi->delete_cloud_resources'.red
222
+ puts 'Exception when calling CloudResourceResultsApi->delete_cloud_resources'.red
161
223
  puts to_output_hash(e)
162
224
  end
163
225
  end
@@ -166,7 +228,7 @@ module Emasser
166
228
  # containers and their scan results in the assets module for a system.
167
229
  #
168
230
  # Endpoint:
169
- # /api/systems/{systemId}/container-scan-results - Remove one or many containers in a system
231
+ # /api/systems/{systemId}/container-scan-results
170
232
  class Container < SubCommandBase
171
233
  def self.exit_on_failure?
172
234
  true
@@ -180,13 +242,13 @@ module Emasser
180
242
 
181
243
  def remove
182
244
  body = EmassClient::ContainerResourcesDeleteBodyInner.new
183
- body.containerId = options[:containerId]
245
+ body.container_id = options[:containerId]
184
246
  body_array = Array.new(1, body)
185
247
 
186
248
  result = EmassClient::ContainerScanResultsApi.new.delete_container_sans(options[:systemId], body_array)
187
249
  puts to_output_hash(result).green
188
250
  rescue EmassClient::ApiError => e
189
- puts 'Exception when calling MilestonesApi->delete_cloud_resources'.red
251
+ puts 'Exception when calling ContainerScanResultsApi->delete_container_sans'.red
190
252
  puts to_output_hash(e)
191
253
  end
192
254
  end
@@ -201,6 +263,12 @@ module Emasser
201
263
  desc 'artifacts', 'Delete system Artifacts'
202
264
  subcommand 'artifacts', Artifacts
203
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
+
204
272
  desc 'cloud_resource', 'Delete cloud resource and their scan results'
205
273
  subcommand 'cloud_resource', CloudResource
206
274
 
@@ -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