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/lib/emasser/put.rb CHANGED
@@ -22,7 +22,7 @@ class SubCommandBase < Thor
22
22
  # rubocop:enable Style/OptionalBooleanParameter
23
23
  end
24
24
 
25
- # Override thor's long_desc identation behavior
25
+ # Override thor's long_desc indentation behavior
26
26
  class Thor
27
27
  module Shell
28
28
  class Basic
@@ -47,108 +47,118 @@ module Emasser
47
47
  true
48
48
  end
49
49
 
50
- desc 'update', 'Get control information in a system for one or many controls (acronym)'
50
+ desc 'update', 'Update control information in a system for one or many controls (acronym)'
51
51
  long_desc Help.text(:controls_put_mapper)
52
52
 
53
53
  # Required parameters/fields
54
54
  option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
55
55
  option :acronym, type: :string, required: true, desc: 'The system acronym(s) e.g "AC-1, AC-2"'
56
56
  option :responsibleEntities, type: :string, required: true,
57
- desc: 'Description of the responsible entities for the Security Control'
57
+ desc: 'Description of the responsible entities for the Security Control'
58
58
  option :controlDesignation, type: :string, required: true,
59
- enum: ['Common', 'System-Specific', 'Hybrid'],
60
- desc: 'The Security Control Designation'
59
+ enum: ['Common', 'System-Specific', 'Hybrid'], desc: 'The Security Control Designation'
61
60
  option :estimatedCompletionDate, type: :numeric, required: true, desc: 'Estimated completion date, Unix time format'
62
61
  option :implementationNarrative, type: :string, required: true, desc: 'Security control comments'
63
62
 
64
63
  # Conditional parameters/fields
65
64
  option :commonControlProvider,
66
- type: :string,
67
- required: false,
68
- enum: ['DoD', 'Component', 'Enclave'],
65
+ type: :string, required: false, enum: ['DoD', 'Component', 'Enclave'],
69
66
  desc: 'Indicate the type of Common Control Provider for an "Inherited" Security Control'
70
67
  option :naJustification,
71
68
  type: :string, required: false,
72
69
  desc: 'Provide justification for Security Controls deemed Not Applicable to the system'
73
- option :slcmCriticality,
74
- type: :string, required: false,
75
- desc: 'Criticality of Security Control regarding SLCM'
76
- option :slcmFrequency,
77
- type: :string, required: false,
70
+ option :slcmCriticality, type: :string, required: false, desc: 'Criticality of Security Control regarding SLCM'
71
+ option :slcmFrequency, type: :string, required: false,
78
72
  enum: ['Constantly', 'Daily', 'Weekly', 'Monthly', 'Quarterly', 'Semi-Annually',
79
73
  'Annually', 'Every Two Years', 'Every Three Years', 'Undetermined'],
80
74
  desc: 'The System-Level Continuous Monitoring frequency'
81
- option :slcmMethod,
82
- type: :string, required: false,
75
+ option :slcmMethod, type: :string, required: false,
83
76
  enum: ['Automated', 'Semi-Automated', 'Manual', 'Undetermined'],
84
77
  desc: 'The System-Level Continuous Monitoring method'
85
- option :slcmReporting,
86
- type: :string, required: false,
78
+ option :slcmReporting, type: :string, required: false,
87
79
  desc: 'The System-Level Continuous Monitoring reporting'
88
- option :slcmTracking,
89
- type: :string, required: false,
80
+ option :slcmTracking, type: :string, required: false,
90
81
  desc: 'The System-Level Continuous Monitoring tracking'
91
- option :slcmComments,
92
- type: :string, required: false,
82
+ option :slcmComments, type: :string, required: false,
93
83
  desc: 'Additional comments for Security Control regarding SLCM'
94
84
 
95
85
  # Optional parameters/fields
96
- option :implementationStatus,
97
- type: :string, required: false,
86
+ option :implementationStatus, type: :string, required: false,
98
87
  enum: ['Planned', 'Implemented', 'Inherited', 'Not Applicable', 'Manually Inherited'],
99
88
  desc: 'Implementation status of the security control for the information system'
100
- option :severity,
101
- type: :string, required: false,
89
+ option :severity, type: :string, required: false,
102
90
  enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High'],
103
91
  desc: 'The security control severity, required for approved items'
104
92
  option :vulnerabiltySummary, type: :string, required: false, desc: 'The security control vulnerability summary'
105
93
  option :recommendations, type: :string, required: false, desc: 'The security control vulnerability recommendation'
106
- option :relevanceOfThreat,
107
- type: :string, required: false,
94
+ option :relevanceOfThreat, type: :string, required: false,
108
95
  enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High'],
109
96
  desc: 'The security control vulnerability of threat'
110
- option :likelihood,
111
- type: :string, required: false,
97
+ option :likelihood, type: :string, required: false,
112
98
  enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High'],
113
99
  desc: 'The security control likelihood of vulnerability to threats'
114
- option :impact,
115
- type: :string, required: false,
100
+ option :impact, type: :string, required: false,
116
101
  enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High'],
117
102
  desc: 'The security control vulnerability impact'
118
103
  option :impactDescription, type: :string, required: false, desc: 'Description of the security control impact'
119
- option :residualRiskLevel,
120
- type: :string, required: false,
104
+ option :residualRiskLevel, type: :string, required: false,
121
105
  enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High'],
122
106
  desc: 'The security control risk level'
123
- option :testMethod,
124
- type: :string, required: false,
107
+ option :testMethod, type: :string, required: false,
125
108
  enum: ['Test', 'Interview', 'Examine', 'Test, Interview', 'Test, Examine',
126
109
  'Interview, Examine', 'Test, Interview, Examine'],
127
110
  desc: 'Assessment method/combination that determines if the security requirements are implemented correctly'
111
+ option :mitigations, type: :string, required: false,
112
+ desc: 'Identify any mitigations in place for the Non-Compliant Security Control\'s vulnerabilities.'
128
113
 
129
114
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
130
115
  def update
131
- # Required fields
132
- body = EmassClient::ControlsGet.new
133
- body.acronym = options[:acronym]
134
- body.responsible_entities = options[:responsibleEntities]
135
- body.control_designation = options[:controlDesignation]
136
- body.estimated_completion_date = options[:estimatedCompletionDate]
137
- body.implementation_narrative = options[:implementationNarrative]
116
+ # Check if business logic is satisfied
117
+ process_business_logic
138
118
 
139
- process_business_logic(body)
119
+ # Required fields
120
+ required = EmassClient::ControlsRequiredFields.new
121
+ required.acronym = options[:acronym]
122
+ required.responsible_entities = options[:responsibleEntities]
123
+ required.control_designation = options[:controlDesignation]
124
+ required.estimated_completion_date = options[:estimatedCompletionDate]
125
+ required.implementation_narrative = options[:implementationNarrative]
140
126
 
141
127
  # Add optional fields
142
- body.severity = options[:severity] if options[:severity]
143
- body.vulnerabilty_summary = options[:vulnerabiltySummary] if options[:vulnerabiltySummary]
144
- body.recommendations = options[:recommendations] if options[:recommendations]
145
- body.relevance_of_threat = options[:relevanceOfThreat] if options[:relevanceOfThreat]
146
- body.likelihood = options[:likelihood] if options[:likelihood]
147
- body.impact = options[:impact] if options[:impact]
148
- body.impact_description = options[:impactDescription] if options[:impactDescription]
149
- body.residual_risk_level = options[:residualRiskLevel] if options[:residualRiskLevel]
150
- body.test_method = options[:testMethod] if options[:testMethod]
128
+ optional = EmassClient::ControlsOptionalFields.new
129
+ optional.implementation_status = options[:implementationStatus] if options[:implementationStatus]
130
+ optional.severity = options[:severity] if options[:severity]
131
+ optional.vulnerabilty_summary = options[:vulnerabiltySummary] if options[:vulnerabiltySummary]
132
+ optional.recommendations = options[:recommendations] if options[:recommendations]
133
+ optional.relevance_of_threat = options[:relevanceOfThreat] if options[:relevanceOfThreat]
134
+ optional.likelihood = options[:likelihood] if options[:likelihood]
135
+ optional.impact = options[:impact] if options[:impact]
136
+ optional.impact_description = options[:impactDescription] if options[:impactDescription]
137
+ optional.residual_risk_level = options[:residualRiskLevel] if options[:residualRiskLevel]
138
+ optional.test_method = options[:testMethod] if options[:testMethod]
139
+ optional.mitigations = options[:mitigations] if options[:mitigations]
140
+ optional.application_layer = options[:applicationLayer] if options[:applicationLayer]
141
+ optional.database_layer = options[:databaseLayer] if options[:databaseLayer]
142
+ optional.operating_system_layer = options[:operatingSystemLayer] if options[:operatingSystemLayer]
151
143
 
144
+ # Add conditional fields
145
+ conditional = EmassClient::ControlsConditionalFields.new
146
+ conditional.common_control_provider = options[:commonControlProvider] if options[:commonControlProvider]
147
+ conditional.na_justification = options[:naJustification] if options[:naJustification]
148
+ conditional.slcm_criticality = options[:slcmCriticality] if options[:slcmCriticality]
149
+ conditional.slcm_frequency = options[:slcmFrequency] if options[:slcmFrequency]
150
+ conditional.slcm_method = options[:slcmMethod] if options[:slcmMethod]
151
+ conditional.slcm_reporting = options[:slcmReporting] if options[:slcmReporting]
152
+ conditional.slcm_tracking = options[:slcmTracking] if options[:slcmTracking]
153
+ conditional.slcm_comments = options[:slcmComments] if options[:slcmComments]
154
+
155
+ # Build the request body
156
+ body = {}
157
+ body = body.merge(required)
158
+ body = body.merge(optional)
159
+ body = body.merge(conditional)
160
+
161
+ # All good, wrap object into an array
152
162
  body_array = Array.new(1, body)
153
163
 
154
164
  begin
@@ -163,13 +173,11 @@ module Emasser
163
173
 
164
174
  # rubocop:disable Style/CaseLikeIf, Style/StringLiterals, Metrics/BlockLength, Metrics/CyclomaticComplexity
165
175
  no_commands do
166
- # rubocop:disable Metrics/PerceivedComplexity, Style/GuardClause
167
- def process_business_logic(body)
176
+ # rubocop:disable Metrics/PerceivedComplexity, Style/MultipleComparison
177
+ def process_business_logic
168
178
  # Conditional fields based on implementationStatus content
169
179
  # unless executes code if conditional is false
170
180
  unless options[:implementationStatus].nil?
171
- body.implementation_status = options[:implementationStatus]
172
-
173
181
  if options[:implementationStatus] == "Planned" || options[:implementationStatus] == "Implemented"
174
182
  if options[:responsibleEntities].nil? || options[:slcmCriticality].nil? ||
175
183
  options[:slcmFrequency].nil? || options[:slcmMethod].nil? ||
@@ -179,14 +187,6 @@ module Emasser
179
187
  puts ' slcmMethod,slcmReporting, slcmTracking, slcmComments'.red
180
188
  puts CONTROLS_PUT_HELP_MESSAGE.yellow
181
189
  exit
182
- else
183
- body.responsible_entities = options[:responsibleEntities]
184
- body.slcm_criticality = options[:slcmCriticality]
185
- body.slcm_frequency = options[:slcmFrequency]
186
- body.slcm_method = options[:slcmMethod]
187
- body.slcm_reporting = options[:slcmReporting]
188
- body.slcm_tracking = options[:slcmTracking]
189
- body.slcm_comments = options[:slcmComments]
190
190
  end
191
191
  elsif options[:implementationStatus] == 'Not Applicable'
192
192
  if options[:naJustification].nil? || options[:responsibleEntities].nil?
@@ -194,9 +194,6 @@ module Emasser
194
194
  puts ' naJustification, responsibleEntities'.red
195
195
  puts CONTROLS_PUT_HELP_MESSAGE.yellow
196
196
  exit
197
- else
198
- body.slcm_reporting = options[:naJustification]
199
- body.responsible_entities = options[:responsibleEntities]
200
197
  end
201
198
  elsif options[:implementationStatus] == 'Manually Inherited'
202
199
  if options[:commonControlProvider].nil? || options[:responsibleEntities].nil? ||
@@ -207,15 +204,6 @@ module Emasser
207
204
  puts ' slcmFrequency, slcmMethod, slcmReporting, slcmTracking, slcmComments'.red
208
205
  puts CONTROLS_PUT_HELP_MESSAGE.yellow
209
206
  exit
210
- else
211
- body.common_control_provider = options[:commonControlProvider]
212
- body.responsible_entities = options[:responsibleEntities]
213
- body.slcm_criticality = options[:slcmCriticality]
214
- body.slcm_frequency = options[:slcmFrequency]
215
- body.slcm_method = options[:slcmMethod]
216
- body.slcm_reporting = options[:slcmReporting]
217
- body.slcm_tracking = options[:slcmTracking]
218
- body.slcm_comments = options[:slcmComments]
219
207
  end
220
208
  elsif options[:implementationStatus] == 'Inherited'
221
209
  if options[:commonControlProvider].nil?
@@ -224,13 +212,15 @@ module Emasser
224
212
  puts 'Missing the commonControlProvider field'.red
225
213
  puts CONTROLS_PUT_HELP_MESSAGE.yellow
226
214
  exit
227
- else
228
- body.common_control_provider = options[:commonControlProvider]
229
215
  end
230
216
  end
217
+ else
218
+ puts 'The "--implementationStatus" parameter is required when updating a Security Control.'.red
219
+ puts 'Values include the following: (Planned, Implemented, Inherited, Not Applicable, Manually Inherited)'.red
220
+ exit
231
221
  end
232
222
  end
233
- # rubocop:enable Metrics/PerceivedComplexity, Style/GuardClause
223
+ # rubocop:enable Metrics/PerceivedComplexity, Style/MultipleComparison
234
224
  end
235
225
  # rubocop:enable Style/CaseLikeIf, Style/StringLiterals, Metrics/BlockLength, Metrics/CyclomaticComplexity
236
226
  end
@@ -239,7 +229,7 @@ module Emasser
239
229
  # Update Plan of Action (POA&M) items to a system.
240
230
  #
241
231
  # Endpoint:
242
- # /api/systems/{systemId}/poams - Update one or many poa&m items in a system
232
+ # /api/systems/{systemId}/poams
243
233
  class Poams < SubCommandBase
244
234
  def self.exit_on_failure?
245
235
  true
@@ -256,7 +246,6 @@ module Emasser
256
246
  # completionDate, milestones (at least 1)
257
247
  # Not Applicable POAM can not be created
258
248
  #--------------------------------------------------------------------------
259
- #
260
249
  # If a POC email is supplied, the application will attempt to locate a user
261
250
  # already registered within the application and pre-populate any information
262
251
  # not explicitly supplied in the request. If no such user is found, these
@@ -267,83 +256,147 @@ module Emasser
267
256
  long_desc Help.text(:poam_put_mapper)
268
257
 
269
258
  # Required parameters/fields
270
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
271
- option :poamId, type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
272
- # option :displayPoamId,
273
- # type: :numeric, required: true,
259
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
260
+ option :poamId, aliases: '-p', type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
261
+ # option :displayPoamId, type: :numeric, required: true,
274
262
  # desc: 'Globally unique identifier for individual POA&M Items, seen on the front-end as "ID"'
275
263
  option :status, type: :string, required: true, enum: ['Ongoing', 'Risk Accepted', 'Completed', 'Not Applicable']
276
264
  option :vulnerabilityDescription, type: :string, required: true, desc: 'POA&M vulnerability description'
277
- option :sourceIdentVuln,
278
- type: :string, required: true, desc: 'Source that identifies the vulnerability'
265
+ option :sourceIdentifyingVulnerability, type: :string, required: true, desc: 'Source that identifies the vulnerability'
279
266
  option :pocOrganization, type: :string, required: true, desc: 'Organization/Office represented'
280
267
  option :resources, type: :string, required: true, desc: 'List of resources used'
281
268
 
269
+ # Some eMASS instances also require the Risk Analysis fields
270
+ # Note: These are grouped here for identification only, they are not marked as required.
271
+ option :severity, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
272
+ option :relevanceOfThreat, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
273
+ option :likelihood, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
274
+ option :impact, type: :string, required: false, desc: 'Description of Security Control’s impact'
275
+ option :residualRiskLevel, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
276
+ option :mitigations, type: :string, required: false, desc: 'Mitigations explanation'
277
+
282
278
  # Conditional parameters/fields
283
- option :milestone,
284
- type: :hash, required: false, desc: 'key:values are: milestoneId, description and scheduledCompletionDate'
279
+ option :milestone, type: :hash, required: false, desc: 'key:values are: milestoneId, description and scheduledCompletionDate'
285
280
  option :pocFirstName, type: :string, required: false, desc: 'First name of POC'
286
281
  option :pocLastName, type: :string, required: false, desc: 'Last name of POC.'
287
282
  option :pocEmail, type: :string, required: false, desc: 'Email address of POC'
288
283
  option :pocPhoneNumber, type: :string, required: false, desc: 'Phone number of POC (area code) ***-**** format'
289
- option :severity, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
284
+ # option :severity, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
290
285
  option :scheduledCompletionDate,
291
286
  type: :numeric, required: false, desc: 'The scheduled completion date - Unix time format'
292
287
  option :completionDate,
293
288
  type: :numeric, required: false, desc: 'The schedule completion date - Unix time format'
294
289
  option :comments, type: :string, required: false, desc: 'Comments for completed and risk accepted POA&M items'
295
- option :isActive, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false.'
290
+ # The next fields are Required for VA. Optional for Army and USCG.
291
+ option :personnelResourcesFundedBaseHours, type: :numeric, required: false, desc: 'Funded based hours (125.34)'
292
+ option :personnelResourcesCostCode, type: :string, required: false, desc: 'Values are specific per eMASS instance'
293
+ option :personnelResourcesUnfundedBaseHours, type: :numeric, required: false, desc: 'Funded based hours (100.00)'
294
+ option :personnelResourcesNonfundingObstacle, type: :string, required: false, desc: 'Values are specific per eMASS instance'
295
+ option :personnelResourcesNonfundingObstacleOtherReason, type: :string, required: false, desc: 'Reason (text 2,000 char)'
296
+ option :nonPersonnelResourcesFundedAmount, type: :numeric, required: false, desc: 'Funded based hours (100.00)'
297
+ option :nonPersonnelResourcesCostCode, type: :string, required: false, desc: 'Values are specific per eMASS instance'
298
+ option :nonPersonnelResourcesUnfundedAmount, type: :numeric, required: false, desc: 'Funded based hours (100.00)'
299
+ option :nonPersonnelResourcesNonfundingObstacle, type: :string, required: false, desc: 'Values are specific per eMASS instance'
300
+ option :nonPersonnelResourcesNonfundingObstacleOtherReason, type: :string, required: false, desc: 'Reason (text 2,000 char)'
296
301
 
297
302
  # Optional parameters/fields
303
+ # API spec states that the displayPoamId is a required field, by backend does not require it
304
+ option :displayPoamId, type: :numeric, required: false,
305
+ desc: 'Globally unique identifier for individual POA&M Items, seen on the front-end as "ID"'
298
306
  option :externalUid, type: :string, required: false, desc: 'External ID associated with the POA&M'
299
307
  option :controlAcronym, type: :string, required: false, desc: 'The system acronym(s) e.g "AC-1, AC-2"'
300
- option :cci, type: :string, required: false, desc: 'The system CCIS string numerical value'
308
+ option :assessmentProcedure, type: :string, required: false, desc: 'The system CCIS string numerical value'
301
309
  option :securityChecks, type: :string, required: false, desc: 'Security Checks that are associated with the POA&M'
302
310
  option :rawSeverity, type: :string, required: false, enum: %w[I II III]
303
- option :relevanceOfThreat,
304
- type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
305
- option :likelihood, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
306
- option :impact, type: :string, required: false, desc: 'Description of Security Control’s impact'
311
+ # option :relevanceOfThreat, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
312
+ # option :likelihood, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
313
+ # option :impact, type: :string, required: false, desc: 'Description of Security Control’s impact'
307
314
  option :impactDescription, type: :string, required: false, desc: 'Description of the security control impact'
308
- option :residualRiskLevel,
309
- type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
310
- option :recommendations, type: :string, required: false, desc: 'Recomendations'
311
- option :mitigation, type: :string, required: false, desc: 'Mitigation explanation'
312
-
313
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
315
+ # option :residualRiskLevel, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
316
+ option :recommendations, type: :string, required: false, desc: 'Recommendations'
317
+ # option :mitigations, type: :string, required: false, desc: 'Mitigations explanation'
318
+ # The next field is Required for VA. Optional for Army and USCG.
319
+ option :identifiedInCFOAuditOrOtherReview, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false.'
320
+ # The next fields are for Navy Only
321
+ option :resultingResidualRiskLevelAfterProposedMitigations, type: :string, required: false, enum: ['Very Low', 'Low', 'Moderate', 'High', 'Very High']
322
+ option :predisposingConditions, type: :string, required: false, desc: 'Conditions (text 2,000 char)'
323
+ option :threatDescription, type: :string, required: false, desc: 'Threat description (text 2,000 char)'
324
+ option :devicesAffected, type: :string, required: false, desc: 'Devices Affected (text 2,000 char)'
325
+
326
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
314
327
  def update
315
- # Required fields
316
- body = EmassClient::PoamGet.new
317
- body.poam_id = options[:poamId]
318
- body.status = options[:status]
319
- body.vulnerability_description = options[:vulnerabilityDescription]
320
- body.source_ident_vuln = options[:sourceIdentVuln]
321
- body.poc_organization = options[:pocOrganization]
322
- body.resources = options[:resources]
328
+ # Check if business logic is satisfied
329
+ process_business_logic
323
330
 
324
- process_business_logic(body)
331
+ # Required fields
332
+ require_id_fields = EmassClient::PoamIds.new
333
+ require_id_fields.poam_id = options[:poamId]
334
+ # API spec states that the displayPoamId is a required field, by backend does not require it
335
+ require_id_fields.display_poam_id = options[:displayPoamId] if options[:displayPoamId]
336
+
337
+ require_fields = EmassClient::PoamRequiredFields.new
338
+ require_fields.status = options[:status]
339
+ require_fields.vulnerability_description = options[:vulnerabilityDescription]
340
+ require_fields.source_identifying_vulnerability = options[:sourceIdentifyingVulnerability]
341
+ require_fields.poc_organization = options[:pocOrganization]
342
+ require_fields.resources = options[:resources]
343
+ # Required for VA, optional for Army and USCG. - defaults to false
344
+ require_fields.identified_in_cfo_audit_or_other_review = options[:identifiedInCFOAuditOrOtherReview] if options[:identifiedInCFOAuditOrOtherReview]
325
345
 
326
346
  # Add conditional fields
327
- body.poc_first_name = options[:pocFirstName] if options[:pocFirstName]
328
- body.poc_last_name = options[:pocLastName] if options[:pocLastName]
329
- body.poc_email = options[:pocEmail] if options[:pocEmail]
330
- body.poc_phone_number = options[:pocPhoneNumber] if options[:pocPhoneNumber]
331
- body.severity = options[:severity] if options[:severity]
347
+ conditional_fields = EmassClient::PoamConditionalFields.new
348
+ conditional_fields.poc_first_name = options[:pocFirstName] if options[:pocFirstName]
349
+ conditional_fields.poc_last_name = options[:pocLastName] if options[:pocLastName]
350
+ conditional_fields.poc_email = options[:pocEmail] if options[:pocEmail]
351
+ conditional_fields.poc_phone_number = options[:pocPhoneNumber] if options[:pocPhoneNumber]
352
+ conditional_fields.severity = options[:severity] if options[:severity]
353
+ conditional_fields.scheduled_completion_date = options[:scheduledCompletionDate] if options[:scheduledCompletionDate]
354
+ conditional_fields.completion_date = options[:completionDate] if options[:completionDate]
355
+ conditional_fields.comments = options[:comments] if options[:comments]
356
+ conditional_fields.personnel_resources_funded_base_hours = options[:personnelResourcesFundedBaseHours] if options[:personnelResourcesFundedBaseHours]
357
+ conditional_fields.personnel_resources_cost_code = options[:personnelResourcesCostCode] if options[:personnelResourcesCostCode]
358
+ conditional_fields.personnel_resources_unfunded_base_hours = options[:personnelResourcesUnfundedBaseHours] if options[:personnelResourcesUnfundedBaseHours]
359
+ conditional_fields.personnel_resources_nonfunding_obstacle = options[:personnelResourcesNonfundingObstacle] if options[:personnelResourcesNonfundingObstacle]
360
+ conditional_fields.personnel_resources_nonfunding_obstacle_other_reason = options[:personnelResourcesNonfundingObstacleOtherReason] if options[:personnelResourcesNonfundingObstacleOtherReason]
361
+ conditional_fields.non_personnel_resources_funded_amount = options[:nonPersonnelResourcesFundedAmount] if options[:nonPersonnelResourcesFundedAmount]
362
+ conditional_fields.non_personnel_resources_cost_code = options[:nonPersonnelResourcesCostCode] if options[:nonPersonnelResourcesCostCode]
363
+ conditional_fields.non_personnel_resources_unfunded_amount = options[:nonPersonnelResourcesUnfundedAmount] if options[:nonPersonnelResourcesUnfundedAmount]
364
+ conditional_fields.non_personnel_resources_nonfunding_obstacle = options[:nonPersonnelResourcesNonfundingObstacle] if options[:nonPersonnelResourcesNonfundingObstacle]
365
+ conditional_fields.non_personnel_resources_nonfunding_obstacle_other_reason = options[:nonPersonnelResourcesNonfundingObstacleOtherReason] if options[:nonPersonnelResourcesNonfundingObstacleOtherReason]
332
366
 
333
367
  # Add optional fields
334
- body.external_uid = options[:externalUid] if options[:externalUid]
335
- body.control_acronym = options[:controlAcronym] if options[:controlAcronym]
336
- body.cci = options[:cci] if options[:cci]
337
- body.security_checks = options[:securityChecks] if options[:securityChecks]
338
- body.raw_severity = options[:rawSeverity] if options[:rawSeverity]
339
- body.relevance_of_threat = options[:relevanceOfThreat] if options[:relevanceOfThreat]
340
- body.likelihood = options[:likelihood] if options[:likelihood]
341
- body.impact = options[:impact] if options[:impact]
342
- body.impact_description = options[:impactDescription] if options[:impactDescription]
343
- body.residual_risk_level = options[:residualRiskLevel] if options[:residualRiskLevel]
344
- body.recommendations = options[:recommendations] if options[:recommendations]
345
- body.mitigation = options[:mitigation] if options[:mitigation]
346
-
368
+ optional_fields = EmassClient::PoamOptionalFields.new
369
+ optional_fields.external_uid = options[:externalUid] if options[:externalUid]
370
+ optional_fields.control_acronym = options[:controlAcronym] if options[:controlAcronym]
371
+ optional_fields.assessment_procedure = options[:assessmentProcedure] if options[:assessmentProcedure]
372
+ optional_fields.security_checks = options[:securityChecks] if options[:securityChecks]
373
+ optional_fields.raw_severity = options[:rawSeverity] if options[:rawSeverity]
374
+ optional_fields.relevance_of_threat = options[:relevanceOfThreat] if options[:relevanceOfThreat]
375
+ optional_fields.likelihood = options[:likelihood] if options[:likelihood]
376
+ optional_fields.impact = options[:impact] if options[:impact]
377
+ optional_fields.impact_description = options[:impactDescription] if options[:impactDescription]
378
+ optional_fields.residual_risk_level = options[:residualRiskLevel] if options[:residualRiskLevel]
379
+ optional_fields.recommendations = options[:recommendations] if options[:recommendations]
380
+ optional_fields.mitigations = options[:mitigations] if options[:mitigations]
381
+ optional_fields.resulting_residual_risk_level_after_proposed_mitigations = options[:resultingResidualRiskLevelAfterProposedMitigations] if options[:resultingResidualRiskLevelAfterProposedMitigations]
382
+ optional_fields.predisposing_conditions = options[:predisposingConditions] if options[:predisposingConditions]
383
+ optional_fields.threat_description = options[:threatDescription] if options[:threatDescription]
384
+ optional_fields.devices_affected = options[:devicesAffected] if options[:devicesAffected]
385
+
386
+ # Build the milestones object array
387
+ milestone = {}
388
+ milestone['milestoneId'] = options[:milestone]['milestoneId'].to_i if options[:milestone]['milestoneId']
389
+ milestone['description'] = options[:milestone]['description'] if options[:milestone]['description']
390
+ milestone['scheduledCompletionDate'] = options[:milestone]['scheduledCompletionDate'].to_f if options[:milestone]['scheduledCompletionDate']
391
+ milestone_array = Array.new(1, milestone)
392
+
393
+ # Build the request body
394
+ body = {}
395
+ body = body.merge(require_id_fields)
396
+ body = body.merge(require_fields)
397
+ body = body.merge(optional_fields)
398
+ body = body.merge(conditional_fields)
399
+ body = body.merge({ milestones: milestone_array })
347
400
  body_array = Array.new(1, body)
348
401
 
349
402
  begin
@@ -354,11 +407,11 @@ module Emasser
354
407
  puts to_output_hash(e)
355
408
  end
356
409
  end
357
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
410
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
358
411
 
359
- # rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
412
+ # rubocop:disable Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
360
413
  no_commands do
361
- def process_business_logic(body)
414
+ def process_business_logic
362
415
  #-----------------------------------------------------------------------------
363
416
  # Conditional fields based on the status field values
364
417
  # "Risk Accepted" comments, resources
@@ -393,15 +446,6 @@ module Emasser
393
446
  puts 'Missing milstone parameters/fields'.red
394
447
  print_milestone_help
395
448
  exit
396
- else
397
- body.scheduled_completion_date = options[:scheduledCompletionDate]
398
-
399
- milestone = EmassClient::MilestonesRequiredPut.new
400
- milestone.milestone_id = options[:milestone]["milestoneId"] if options[:milestone]["milestoneId"]
401
- milestone.description = options[:milestone]["description"]
402
- milestone.scheduled_completion_date = options[:milestone]["scheduledCompletionDate"]
403
- milestone_array = Array.new(1, milestone)
404
- body.milestones = milestone_array
405
449
  end
406
450
  elsif options[:status] == "Completed"
407
451
  if options[:scheduledCompletionDate].nil? || options[:comments].nil? ||
@@ -411,17 +455,6 @@ module Emasser
411
455
  print_milestone_help
412
456
  puts POAMS_PUT_HELP_MESSAGE.yellow
413
457
  exit
414
- else
415
- body.scheduled_completion_date = options[:scheduledCompletionDate]
416
- body.comments = options[:comments]
417
- body.completion_date = options[:completionDate]
418
-
419
- milestone = EmassClient::MilestonesRequiredPut.new
420
- milestone.milestone_id = options[:milestone]["milestoneId"] if options[:milestone]["milestoneId"]
421
- milestone.description = options[:milestone]["description"]
422
- milestone.scheduled_completion_date = options[:milestone]["scheduledCompletionDate"]
423
- milestone_array = Array.new(1, milestone)
424
- body.milestones = milestone_array
425
458
  end
426
459
  end
427
460
 
@@ -464,13 +497,13 @@ module Emasser
464
497
  puts 'The milestoneId:[value] is optional, if not provided a new milestone is created'.yellow
465
498
  end
466
499
  end
467
- # rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
500
+ # rubocop:enable Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
468
501
  end
469
502
 
470
503
  # Update Milestones items to a system.
471
504
  #
472
505
  # Endpoint:
473
- # /api/systems/{systemId}/poams/{poamId}/milestones - Update milestones in one or many poa&m items in a system
506
+ # /api/systems/{systemId}/poams/{poamId}/milestones
474
507
  class Milestones < SubCommandBase
475
508
  def self.exit_on_failure?
476
509
  true
@@ -480,12 +513,12 @@ module Emasser
480
513
  long_desc Help.text(:milestone_put_mapper)
481
514
 
482
515
  # Required parameters/fields
483
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
484
- option :poamId, type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
485
- option :milestoneId,
516
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
517
+ option :poamId, aliases: '-p', type: :numeric, required: true, desc: 'A numeric value representing the poam identification'
518
+ option :milestoneId, aliases: '-m',
486
519
  type: :numeric, required: true, desc: 'A numeric value representing the milestone identification'
487
- option :description, type: :string, required: true, desc: 'The milestone description'
488
- option :scheduledCompletionDate,
520
+ option :description, aliases: '-d', type: :string, required: true, desc: 'The milestone description'
521
+ option :scheduledCompletionDate, aliases: '-c',
489
522
  type: :numeric, required: false, desc: 'The scheduled completion date - Unix time format'
490
523
 
491
524
  def update
@@ -508,10 +541,11 @@ module Emasser
508
541
  end
509
542
  end
510
543
 
511
- # Update one or many artifacts for a system (this implementation only updates one artifact per each execution)
544
+ # Update one or many artifacts for a system
545
+ # (only one artifact per each execution)
512
546
  #
513
547
  # Endpoint:
514
- # /api/systems/{systemId}/artifacts - Put (update) one or many artifacts for a system
548
+ # /api/systems/{systemId}/artifacts
515
549
  class Artifacts < SubCommandBase
516
550
  def self.exit_on_failure?
517
551
  true
@@ -521,44 +555,50 @@ module Emasser
521
555
  long_desc Help.text(:artifacts_put_mapper)
522
556
 
523
557
  # Required parameters/fields
524
- option :systemId, type: :numeric, required: true, desc: 'A numeric value representing the system identification'
525
- option :filename, type: :string, required: true, desc: 'Artifact file name to be updated'
526
- option :type,
527
- type: :string, required: true,
528
- enum: ['Procedure', 'Diagram', 'Policy', 'Labor', 'Document',
529
- 'Image', 'Other', 'Scan Result', 'Auditor Report']
530
- option :category, type: :string, required: true, enum: ['Implementation Guidance', 'Evidence']
531
- option :isTemplate, type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false.'
532
- # NOTE: compress is a required parameter, however Thor does not allow a boolean type to be required because it
558
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
559
+ option :filename, aliases: '-f', type: :string, required: true, desc: 'Artifact file name to be updated'
560
+ option :type, aliases: '-t', type: :string, required: true, default: 'Other',
561
+ desc: 'The type of artifact. Possible values are: Procedure, Diagram, Policy, Labor, Document, Image, Other, Scan Result, Auditor Report. May also accept other values set by system administrators.'
562
+ option :category, aliases: '-c', type: :string, required: true, default: 'Evidence',
563
+ desc: 'The category of artifact. Possible values are: Implementation Guidance, Evidence. May also accept other values set by system administrators.'
564
+ # NOTE: isTemplate is a required parameter, however Thor does not allow a boolean type to be required because it
533
565
  # automatically creates a --no-isTemplate option for isTemplate=false
566
+ option :isTemplate, aliases: '-T', type: :boolean, required: false, default: false, desc: 'BOOLEAN - true or false.'
534
567
 
535
568
  # Optional fields
569
+ option :name, type: :string, required: false, desc: 'Artifact name'
536
570
  option :description, type: :string, required: false, desc: 'Artifact description'
537
- option :refPageNumber, type: :string, required: false, desc: 'Artifact reference page number'
538
- option :ccis, type: :string, required: false, desc: 'The system CCIs string numerical value'
539
- option :controls,
540
- type: :string, required: false,
571
+ option :referencePageNumber, type: :string, required: false, desc: 'Artifact reference page number'
572
+ option :controls, type: :string, required: false,
541
573
  desc: 'Control acronym associated with the artifact. NIST SP 800-53 Revision 4 defined'
542
- option :artifactExpirationDate,
543
- type: :numeric, required: false, desc: 'Date Artifact expires and requires review - Unix time format'
544
- option :lastReviewedDate,
545
- type: :numeric, required: false, desc: 'Date Artifact was last reviewed - Unix time format'
574
+ option :assessmentProcedures, type: :string, required: false, desc: 'The Security Control Assessment Procedure being associated with the artifact'
575
+ option :expirationDate, type: :numeric, required: false, desc: 'Date Artifact expires and requires review - Unix time format'
576
+ option :lastReviewedDate, type: :numeric, required: false, desc: 'Date Artifact was last reviewed - Unix time format'
577
+ option :signedDate, type: :numeric, required: false, desc: 'Date Artifact was signed - Unix time format'
546
578
 
547
- # rubocop:disable Metrics/CyclomaticComplexity
579
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
548
580
  def update
549
- body = EmassClient::ArtifactsGet.new
550
- body.filename = options[:filename]
551
- body.type = options[:type]
552
- body.category = options[:category]
553
- body.is_template = options[:isTemplate]
554
- # Optional fields
555
- body.description = options[:description] if options[:description]
556
- body.ref_page_number = options[:refPageNumber] if options[:refPageNumber]
557
- body.ccis = options[:ccis] if options[:ccis]
558
- body.controls = options[:controls] if options[:controls]
559
- body.artifact_expiration_date = options[:artifactExpirationDate] if options[:artifactExpirationDate]
560
- body.last_reviewed_date = options[:lastReviewedDate] if options[:lastReviewedDate]
581
+ require_fields = EmassClient::ArtifactsRequiredFields.new
582
+ require_fields.filename = options[:filename]
583
+ require_fields.type = options[:type]
584
+ require_fields.category = options[:category]
585
+ require_fields.is_template = options[:isTemplate]
561
586
 
587
+ # Optional fields
588
+ optional_fields = EmassClient::ArtifactsOptionalFields.new
589
+ optional_fields.name = options[:name] if options[:name]
590
+ optional_fields.description = options[:description] if options[:description]
591
+ optional_fields.reference_page_number = options[:referencePageNumber] if options[:referencePageNumber]
592
+ optional_fields.controls = options[:controls] if options[:controls]
593
+ optional_fields.assessment_procedures = options[:assessmentProcedures] if options[:assessmentProcedures]
594
+ optional_fields.expiration_date = options[:expirationDate] if options[:expirationDate]
595
+ optional_fields.last_reviewed_date = options[:lastReviewedDate] if options[:lastReviewedDate]
596
+ optional_fields.signed_date = options[:signedDate] if options[:signedDate]
597
+
598
+ # Build the request body
599
+ body = {}
600
+ body = body.merge(require_fields)
601
+ body = body.merge(optional_fields)
562
602
  body_array = Array.new(1, body)
563
603
 
564
604
  begin
@@ -569,7 +609,221 @@ module Emasser
569
609
  puts to_output_hash(e)
570
610
  end
571
611
  end
572
- # rubocop:enable Metrics/CyclomaticComplexity
612
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
613
+ end
614
+
615
+ # Update Hardware Baseline assets for a system
616
+ # (only one hardware per each execution)
617
+ #
618
+ # Endpoints:
619
+ # /api/systems/{systemId}/hw-baseline
620
+ class Hardware < SubCommandBase
621
+ def self.exit_on_failure?
622
+ true
623
+ end
624
+
625
+ desc 'update', 'Update one hardware assets in a system per execution'
626
+ long_desc Help.text(:hardware_put_mapper)
627
+
628
+ # Required parameters/fields
629
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
630
+ option :hardwareId, aliases: '-h', type: :string, required: true, desc: 'GUID identifying the specific hardware asset'
631
+ option :assetName, aliases: '-a', type: :string, required: true, desc: 'Name of the hardware asset'
632
+
633
+ # Conditional fields
634
+ option :publicFacingFqdn, type: :string, required: false, desc: 'Public facing FQDN. Only applicable if Public Facing is set to true'
635
+ option :publicFacingIpAddress, type: :string, required: false, desc: 'Public facing IP address. Only applicable if Public Facing is set to true'
636
+ option :publicFacingUrls, type: :string, required: false, desc: 'Public facing URL(s). Only applicable if Public Facing is set to true'
637
+
638
+ # Optional fields
639
+ option :componentType, type: :string, required: false, desc: 'Component type of the hardware asset'
640
+ option :nickname, type: :string, required: false, desc: 'Nickname of the hardware asset'
641
+ option :assetIpAddress, type: :string, required: false, desc: 'IP address of the hardware asset'
642
+ option :publicFacing, type: :boolean, required: false, desc: 'Public facing is defined as any asset that is accessible from a commercial connection'
643
+ option :virtualAsset, type: :boolean, required: false, default: false, desc: 'Determine if this is a virtual hardware asset'
644
+ option :manufacturer, type: :string, required: false, desc: 'Manufacturer of the hardware asset. Populated with “Virtual” by default if Virtual Asset is true'
645
+ option :modelNumber, type: :string, required: false, desc: 'Model number of the hardware asset. Populated with “Virtual” by default if Virtual Asset is true'
646
+ option :serialNumber, type: :string, required: false, desc: 'Serial number of the hardware asset. Populated with “Virtual” by default if Virtual Asset is true'
647
+ option :OsIosFwVersion, type: :string, required: false, desc: 'OS/iOS/FW version of the hardware asset'
648
+ option :memorySizeType, type: :string, required: false, desc: 'Memory size / type of the hardware asset'
649
+ option :location, type: :string, required: false, desc: 'Location of the hardware asset'
650
+ option :approvalStatus, type: :string, required: false, desc: 'Approval status of the hardware asset'
651
+ option :criticalAsset, type: :boolean, required: false, default: false, desc: 'Indicates whether the asset is a critical information system asset'
652
+
653
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
654
+ def update
655
+ # Required fields
656
+ require_field = EmassClient::HwBaselineRequiredFields.new
657
+ require_field.asset_name = options[:assetName]
658
+ read_only_field = EmassClient::HwBaselineReadOnlyFields.new
659
+ read_only_field.hardware_id = options[:hardwareId] if options[:hardwareId]
660
+
661
+ # Conditional fields
662
+ conditional_fields = EmassClient::HwBaselineConditionalFields.new
663
+ conditional_fields.public_facing_fqdn = options[:publicFacingFqdn] if options[:publicFacingFqdn]
664
+ conditional_fields.public_facing_ip_address = options[:publicFacingIpAddress] if options[:publicFacingIpAddress]
665
+ conditional_fields.public_facing_urls = options[:publicFacingUrls] if options[:publicFacingUrls]
666
+
667
+ # Optional fields
668
+ optional_fields = EmassClient::HwBaselineOptionalFields.new
669
+ optional_fields.component_type = options[:componentType] if options[:componentType]
670
+ optional_fields.nickname = options[:nickname] if options[:nickname]
671
+ optional_fields.asset_ip_address = options[:assetIpAddress] if options[:assetIpAddress]
672
+ optional_fields.public_facing = options[:publicFacing] if options[:publicFacing]
673
+ optional_fields.virtual_asset = options[:virtualAsset] if options[:virtualAsset]
674
+ optional_fields.manufacturer = options[:manufacturer] if options[:manufacturer]
675
+ optional_fields.model_number = options[:modelNumber] if options[:modelNumber]
676
+ optional_fields.serial_number = options[:serialNumber] if options[:serialNumber]
677
+ optional_fields.os_ios_fw_version = options[:OsIosFwVersion] if options[:OsIosFwVersion]
678
+ optional_fields.memory_size_type = options[:memorySizeType] if options[:memorySizeType]
679
+ optional_fields.location = options[:location] if options[:location]
680
+ optional_fields.approval_status = options[:approvalStatus] if options[:approvalStatus]
681
+ optional_fields.critical_asset = options[:criticalAsset] if options[:criticalAsset]
682
+
683
+ # Build the body array
684
+ body = {}
685
+ body = body.merge(require_field)
686
+ body = body.merge(read_only_field)
687
+ body = body.merge(conditional_fields)
688
+ body = body.merge(optional_fields)
689
+ body_array = Array.new(1, body)
690
+
691
+ # Call the API
692
+ result = EmassClient::HardwareBaselineApi.new.update_hw_baseline_assets(options[:systemId], body_array)
693
+ puts to_output_hash(result).green
694
+ rescue EmassClient::ApiError => e
695
+ puts 'Exception when calling HardwareBaselineApi->update_hw_baseline_assets'.red
696
+ puts to_output_hash(e)
697
+ end
698
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
699
+ end
700
+
701
+ # Add Software Baseline assets for a system
702
+ # (only one software per each execution)
703
+ #
704
+ # Endpoints:
705
+ # /api/systems/{systemId}/sw-baseline
706
+ class Software < SubCommandBase
707
+ def self.exit_on_failure?
708
+ true
709
+ end
710
+
711
+ desc 'update', 'update one software assets into a system per execution'
712
+ long_desc Help.text(:software_put_mapper)
713
+
714
+ # Required parameters/fields
715
+ option :systemId, aliases: '-s', type: :numeric, required: true, desc: 'A numeric value representing the system identification'
716
+ option :softwareId, aliases: '-S', type: :string, required: true, desc: 'GUID identifying the specific software asset'
717
+ option :softwareVendor, aliases: '-V', type: :string, required: true, desc: 'Vendor of the software asset'
718
+ option :softwareName, aliases: '-N', type: :string, required: true, desc: 'Name of the software asset'
719
+ option :version, aliases: '-v', type: :string, required: true, desc: 'Version of the software asset'
720
+
721
+ # Conditional field
722
+ # If Approval Status is set to “Unapproved” or “In Progress”, Approval Date will be set to null.
723
+ option :approvalDate, type: :numeric, required: false, desc: 'Approval date of the software asset.'
724
+
725
+ # Optional fields
726
+ option :softwareType, type: :string, required: false, desc: 'Type of the software asset'
727
+ option :parentSystem, type: :string, required: false, desc: 'Parent system of the software asset'
728
+ option :subsystem, type: :string, required: false, desc: 'Subsystem of the software asset'
729
+ option :network, type: :string, required: false, desc: 'Network of the software asset'
730
+ option :hostingEnvironment, type: :string, required: false, desc: 'Hosting environment of the software asset'
731
+ option :softwareDependencies, type: :string, required: false, desc: 'Dependencies for the software asset'
732
+ option :cryptographicHash, type: :string, required: false, desc: 'Cryptographic hash for the software asset'
733
+ option :inServiceData, type: :string, required: false, desc: 'In service data for the software asset'
734
+ option :itBudgetUii, type: :string, required: false, desc: 'IT budget UII for the software asset'
735
+ option :fiscalYear, type: :string, required: false, desc: 'Fiscal year (FY) for the software asset'
736
+ option :popEndDate, type: :numeric, required: false, desc: 'Period of performance (POP) end date for the software asset'
737
+ option :licenseOrContract, type: :string, required: false, desc: 'License or contract for the software asset'
738
+ option :licenseTerm, type: :string, required: false, desc: 'License term for the software asset'
739
+ option :costPerLicense, type: :numeric, required: false, desc: 'Cost per license for the software asset'
740
+ option :totalLicenses, type: :numeric, required: false, desc: 'Number of total licenses for the software asset'
741
+ option :totalLicenseCost, type: :numeric, required: false, desc: 'Total cost of the licenses for the software asset'
742
+ option :licensesUsed, type: :numeric, required: false, desc: 'Number of licenses used for the software asset'
743
+ option :licensePoc, type: :string, required: false, desc: 'Point of contact (POC) for the software asset'
744
+ option :licenseRenewalDate, type: :numeric, required: false, desc: 'License renewal date for the software asset'
745
+ option :licenseExpirationDate, type: :numeric, required: false, desc: 'TLicense expiration date for the software asset'
746
+ option :approvalStatus, type: :string, required: false, desc: 'Approval status of the software asset'
747
+ option :releaseDate, type: :numeric, required: false, desc: 'Release date of the software asset'
748
+ option :maintenanceDate, type: :numeric, required: false, desc: 'Maintenance date of the software asset'
749
+ option :retirementDate, type: :numeric, required: false, desc: 'Retirement date of the software asset'
750
+ option :endOfLifeSupportDate, type: :numeric, required: false, desc: 'End of life/support date of the software asset'
751
+ option :extendedEndOfLifeSupportDate, type: :numeric, required: false, desc: 'Extended End of Life/Support Date cannot occur prior to the End of Life/Support Date'
752
+ option :criticalAsset, type: :boolean, required: false, default: false, desc: 'Indicates whether the asset is a critical information system asset'
753
+ option :location, type: :string, required: false, desc: 'Location of the software asset'
754
+ option :purpose, type: :string, required: false, desc: 'Purpose of the software asset'
755
+ # VA only
756
+ option :unsupportedOperatingSystem, type: :boolean, required: false, default: false, desc: 'Unsupported operating system'
757
+ option :unapprovedSoftwareFromTrm, type: :boolean, required: false, default: false, desc: 'Unapproved software from TRM'
758
+ option :approvedWaiver, type: :boolean, required: false, default: false, desc: 'Approved waiver'
759
+
760
+ # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
761
+ def update
762
+ # Required fields
763
+ require_field = EmassClient::SwBaselineRequiredFields.new
764
+ require_field.software_vendor = options[:softwareVendor]
765
+ require_field.software_name = options[:softwareName]
766
+ require_field.version = options[:version]
767
+ read_only_field = EmassClient::SwBaselineReadOnlyFields.new
768
+ read_only_field.software_id = options[:softwareId] if options[:softwareId]
769
+
770
+ # Conditional fields
771
+ conditional_fields = EmassClient::SwBaselineConditionalFields.new
772
+ conditional_fields.approval_date = options[:approvalDate] if options[:approvalDate]
773
+
774
+ # Optional fields
775
+ optional_fields = EmassClient::SwBaselineOptionalFields.new
776
+ optional_fields.software_type = options[:softwareType] if options[:softwareType]
777
+ optional_fields.parent_system = options[:parentSystem] if options[:parentSystem]
778
+ optional_fields.subsystem = options[:subsystem] if options[:subsystem]
779
+ optional_fields.network = options[:network] if options[:network]
780
+ optional_fields.hosting_environment = options[:hostingEnvironment] if options[:hostingEnvironment]
781
+ optional_fields.software_dependencies = options[:softwareDependencies] if options[:softwareDependencies]
782
+ optional_fields.cryptographic_hash = options[:cryptographicHash] if options[:cryptographicHash]
783
+ optional_fields.in_service_data = options[:inServiceData] if options[:inServiceData]
784
+ optional_fields.it_budget_uii = options[:itBudgetUii] if options[:itBudgetUii]
785
+ optional_fields.fiscal_year = options[:fiscalYear] if options[:fiscalYear]
786
+ optional_fields.pop_end_date = options[:popEndDate] if options[:popEndDate]
787
+ optional_fields.license_or_contract = options[:licenseOrContract] if options[:licenseOrContract]
788
+ optional_fields.license_term = options[:licenseTerm] if options[:licenseTerm]
789
+ optional_fields.cost_per_license = options[:costPerLicense] if options[:costPerLicense]
790
+ optional_fields.total_licenses = options[:totalLicenses] if options[:totalLicenses]
791
+ optional_fields.total_license_cost = options[:totalLicenseCost] if options[:totalLicenseCost]
792
+ optional_fields.licenses_used = options[:licensesUsed] if options[:licensesUsed]
793
+ optional_fields.license_poc = options[:licensePoc] if options[:licensePoc]
794
+ optional_fields.license_renewal_date = options[:licenseRenewalDate] if options[:licenseRenewalDate]
795
+ optional_fields.license_expiration_date = options[:licenseExpirationDate] if options[:licenseExpirationDate]
796
+ optional_fields.approval_status = options[:approvalStatus] if options[:approvalStatus]
797
+ optional_fields.approval_date = options[:approvalDate] if options[:approvalDate]
798
+ optional_fields.release_date = options[:releaseDate] if options[:releaseDate]
799
+ optional_fields.maintenance_date = options[:maintenanceDate] if options[:maintenanceDate]
800
+ optional_fields.retirement_date = options[:retirementDate] if options[:retirementDate]
801
+ optional_fields.end_of_life_support_date = options[:endOfLifeSupportDate] if options[:endOfLifeSupportDate]
802
+ optional_fields.extended_end_of_life_support_date = options[:extendedEndOfLifeSupportDate] if options[:extendedEndOfLifeSupportDate]
803
+ optional_fields.critical_asset = options[:criticalAsset] if options[:criticalAsset]
804
+ optional_fields.location = options[:location] if options[:location]
805
+ optional_fields.purpose = options[:purpose] if options[:purpose]
806
+ # VA only.
807
+ optional_fields.unsupported_operating_system = options[:unsupportedOperatingSystem] if options[:unsupportedOperatingSystem]
808
+ optional_fields.unapproved_software_from_trm = options[:unapprovedSoftwareFromTrm] if options[:unapprovedSoftwareFromTrm]
809
+ optional_fields.approved_waiver = options[:approvedWaiver] if options[:approvedWaiver]
810
+
811
+ # Build the body array
812
+ body = {}
813
+ body = body.merge(require_field)
814
+ body = body.merge(read_only_field)
815
+ body = body.merge(conditional_fields)
816
+ body = body.merge(optional_fields)
817
+ body_array = Array.new(1, body)
818
+
819
+ # Call the API
820
+ result = EmassClient::SoftwareBaselineApi.new.update_sw_baseline_assets(options[:systemId], body_array)
821
+ puts to_output_hash(result).green
822
+ rescue EmassClient::ApiError => e
823
+ puts 'Exception when calling SoftwareBaselineApi->update_sw_baseline_assets'.red
824
+ puts to_output_hash(e)
825
+ end
826
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
573
827
  end
574
828
 
575
829
  class Put < SubCommandBase
@@ -582,7 +836,13 @@ module Emasser
582
836
  desc 'milestones', 'Update Milestones items for a system'
583
837
  subcommand 'milestones', Milestones
584
838
 
585
- desc 'artifacts', 'Put system Artifacts'
839
+ desc 'artifacts', 'Update system Artifacts'
586
840
  subcommand 'artifacts', Artifacts
841
+
842
+ desc 'hardware', 'Update one or many hardware assets to a system'
843
+ subcommand 'hardware', Hardware
844
+
845
+ desc 'software', 'Update one or many software assets to a system'
846
+ subcommand 'software', Software
587
847
  end
588
848
  end