oscal 0.1.0 → 0.2.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/Dockerfile +19 -0
  3. data/.docker/Makefile +43 -0
  4. data/.docker/docker-compose.yml +14 -0
  5. data/.docker/readme.md +61 -0
  6. data/.github/workflows/rake.yml +15 -0
  7. data/.github/workflows/release.yml +24 -0
  8. data/.gitignore +13 -0
  9. data/.gitmodules +3 -0
  10. data/.hound.yml +5 -0
  11. data/.rspec +0 -1
  12. data/.rubocop.yml +10 -0
  13. data/.ruby-version +1 -0
  14. data/Gemfile +4 -0
  15. data/LICENSE +25 -0
  16. data/Makefile +1 -0
  17. data/README.adoc +66 -0
  18. data/Rakefile +13 -6
  19. data/bin/console +30 -0
  20. data/bin/rspec +27 -0
  21. data/bin/setup +8 -0
  22. data/docker-compose.yml +1 -0
  23. data/exe/convert2oscalyaml.rb +560 -0
  24. data/lib/oscal/add.rb +26 -0
  25. data/lib/oscal/address.rb +22 -0
  26. data/lib/oscal/address_line.rb +11 -0
  27. data/lib/oscal/alter.rb +22 -0
  28. data/lib/oscal/assembly.rb +119 -0
  29. data/lib/oscal/assessment_plan.rb +28 -0
  30. data/lib/oscal/assessment_result.rb +230 -0
  31. data/lib/oscal/attribute_type_hash.rb +80 -0
  32. data/lib/oscal/back_matter.rb +20 -0
  33. data/lib/oscal/base64_object.rb +11 -0
  34. data/lib/oscal/base_class.rb +50 -0
  35. data/lib/oscal/catalog.rb +51 -10
  36. data/lib/oscal/choice.rb +11 -0
  37. data/lib/oscal/citation.rb +22 -0
  38. data/lib/oscal/combine.rb +11 -0
  39. data/lib/oscal/common_utils.rb +35 -0
  40. data/lib/oscal/constraint.rb +20 -0
  41. data/lib/oscal/control.rb +20 -30
  42. data/lib/oscal/custom.rb +22 -0
  43. data/lib/oscal/datatypes.rb +50 -0
  44. data/lib/oscal/document_id.rb +11 -0
  45. data/lib/oscal/email_address.rb +11 -0
  46. data/lib/oscal/exclude_control.rb +22 -0
  47. data/lib/oscal/external_id.rb +11 -0
  48. data/lib/oscal/group.rb +26 -34
  49. data/lib/oscal/guideline.rb +11 -0
  50. data/lib/oscal/hash_object.rb +11 -0
  51. data/lib/oscal/import_object.rb +22 -0
  52. data/lib/oscal/include_control.rb +22 -0
  53. data/lib/oscal/insert_control.rb +22 -0
  54. data/lib/oscal/link.rb +11 -0
  55. data/lib/oscal/list.rb +160 -0
  56. data/lib/oscal/location.rb +31 -0
  57. data/lib/oscal/location_uuid.rb +11 -0
  58. data/lib/oscal/logger.rb +8 -0
  59. data/lib/oscal/matching.rb +11 -0
  60. data/lib/oscal/member_of_organization.rb +11 -0
  61. data/lib/oscal/merge.rb +20 -0
  62. data/lib/oscal/metadata_block.rb +28 -13
  63. data/lib/oscal/modify.rb +22 -0
  64. data/lib/oscal/parameter.rb +22 -19
  65. data/lib/oscal/parsing_functions.rb +19 -0
  66. data/lib/oscal/part.rb +14 -22
  67. data/lib/oscal/party.rb +36 -0
  68. data/lib/oscal/party_uuid.rb +11 -0
  69. data/lib/oscal/profile.rb +33 -7
  70. data/lib/oscal/property.rb +4 -25
  71. data/lib/oscal/remove.rb +11 -0
  72. data/lib/oscal/resource.rb +29 -0
  73. data/lib/oscal/responsible_party.rb +24 -0
  74. data/lib/oscal/revision.rb +23 -0
  75. data/lib/oscal/rlink.rb +20 -0
  76. data/lib/oscal/role.rb +22 -0
  77. data/lib/oscal/select.rb +20 -0
  78. data/lib/oscal/serializer.rb +17 -4
  79. data/lib/oscal/set_parameter.rb +31 -0
  80. data/lib/oscal/telephone_number.rb +11 -0
  81. data/lib/oscal/test.rb +11 -0
  82. data/lib/oscal/url.rb +11 -0
  83. data/lib/oscal/value.rb +37 -0
  84. data/lib/oscal/version.rb +1 -1
  85. data/lib/oscal/with_id.rb +40 -0
  86. data/lib/oscal.rb +1 -13
  87. data/oscal.gemspec +9 -11
  88. data/spec/oscal/catalog_spec.rb +40 -0
  89. data/spec/oscal_spec.rb +7 -0
  90. data/spec/sample_inputs/import-ap.json +4 -0
  91. data/spec/spec_helper.rb +15 -0
  92. metadata +84 -10
  93. data/lib/oscal/component.rb +0 -14
  94. data/lib/oscal/prose.rb +0 -13
  95. data/lib/oscal/statement.rb +0 -12
@@ -0,0 +1,119 @@
1
+ require_relative "parsing_functions"
2
+ require_relative "logger"
3
+ require_relative "metadata_block"
4
+
5
+ module Oscal
6
+ class MetadataBlockWrapper < Oscal::MetadataBlock
7
+ include ParsingFunctions
8
+ def initialize(metadata_hash)
9
+ # MetadataBlock likes to get strings, but may sometimes get symbols
10
+ # this little function makes sure it gets strings everytime
11
+ super(metadata_hash.transform_keys { |key| sym2str(key) })
12
+ end
13
+ end
14
+
15
+ class Assembly
16
+ include Oscal::ParsingFunctions
17
+ include Oscal::ParsingLogger
18
+
19
+ def mandatory_attributes
20
+ if self.class.constants.include?(:MANDATORY)
21
+ self.class::MANDATORY
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ def allowed_attributes
28
+ if self.class.constants.include?(:OPTIONAL)
29
+ mandatory_attributes + self.class::OPTIONAL
30
+ else
31
+ mandatory_attributes
32
+ end
33
+ end
34
+
35
+ def to_json
36
+ to_h.to_json
37
+ end
38
+
39
+ def to_h
40
+ allowed_attributes.each_with_object({}) do |var, hash|
41
+ attr_value = method(var).call
42
+ hash[sym2str(var)] = if attr_value == nil
43
+ next
44
+ elsif attr_value.class <= OscalArray
45
+ attr_value.each(&:to_h)
46
+ elsif attr_value.class <= OscalDatatype
47
+ attr_value
48
+ else
49
+ attr_value.to_h
50
+ end
51
+ end
52
+ end
53
+
54
+ def check_and_normalize_input(input)
55
+ @logger.debug("Checking to see if input is a Hash")
56
+ unless input.is_a? Hash
57
+ raise Oscal::InvalidTypeError,
58
+ "Assemblies can only be created from Hash types"
59
+ end
60
+ @logger.debug("Assembly is hash with keys #{input.keys}")
61
+
62
+ @logger.debug("Attempting to transform strings to symbols.")
63
+ # Transform the keys from Strings to Symbols
64
+ input.transform_keys { |key| str2sym(key) }
65
+ end
66
+
67
+ def validate_input(input)
68
+ @logger.debug("Checking mandatory and optional values.")
69
+ missing_values?(mandatory_attributes, input)
70
+ extra_values?(allowed_attributes, input)
71
+ end
72
+
73
+ def missing_values?(mandatory, provided)
74
+ @logger.debug("Checking mandatory values: #{mandatory}")
75
+ missing_values = mandatory - provided.keys.intersection(mandatory)
76
+ if missing_values.length.positive?
77
+ raise Oscal::InvalidTypeError,
78
+ "Missing mandatory values: #{missing_values}"
79
+ end
80
+ end
81
+
82
+ def extra_values?(allowed, provided)
83
+ @logger.debug("Checking allowed values: #{allowed}")
84
+ extra_values = provided.keys - provided.keys.intersection(allowed)
85
+ if extra_values.length.positive?
86
+ raise Oscal::InvalidTypeError,
87
+ "Extra attributes provided #{extra_values}"
88
+ end
89
+ end
90
+
91
+ def validate_content(key, value)
92
+ @logger.info("Validating #{value}")
93
+ expected_class = Oscal::get_type_of_attribute(key)
94
+ @logger.debug("Attempting to instiate #{key} as #{expected_class}")
95
+ instantiated = expected_class.new(value)
96
+ rescue Oscal::InvalidTypeError
97
+ raise Oscal::InvalidTypeError,
98
+ "Value #{value.to_s[0, 25]} not a valid #{key}"
99
+ else
100
+ instantiated # Return the valid class
101
+ end
102
+
103
+ def initialize(input)
104
+ @logger = get_logger
105
+ @logger.debug("#{self.class}.new called with #{input.to_s[0, 25]}")
106
+
107
+ # covert String:String to Symbol:String
108
+ sym_hash = check_and_normalize_input(input)
109
+
110
+ # Make sure all required and no extra keys are provided
111
+ validate_input(sym_hash)
112
+
113
+ # Attempt to convert each value to it's registered type
114
+ sym_hash.each do |key, value|
115
+ method("#{key}=".to_sym).call(validate_content(key, value))
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,28 @@
1
+ require_relative "assembly"
2
+ require_relative "metadata_block"
3
+ require_relative "datatypes"
4
+
5
+ module Oscal
6
+ module AssessmentPlan
7
+ class ImportSSP < Assembly
8
+ attr_accessor(*(MANDATORY = %i(href).freeze),
9
+ *(OPTIONAL = %i(remarks).freeze))
10
+ end
11
+
12
+ class ReviewedControls < Assembly
13
+ attr_accessor(*(MANDATORY = %i(control_selections).freeze),
14
+ *(OPTIONAL = %i(description props links
15
+ control_objective_selections
16
+ remarks).freeze))
17
+ end
18
+
19
+ class AssessmentPlan < Assembly
20
+ attr_accessor(*(MANDATORY = %i(uuid metadata import_ssp
21
+ reviewed_controls).freeze),
22
+ *(OPTIONAL = %i(local_definitions terms_and_conditions
23
+ reviewed_controls assessment_subjects
24
+ assessment_assets tasks
25
+ back_matter).freeze))
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,230 @@
1
+ require_relative "assembly"
2
+ require_relative "metadata_block"
3
+ require_relative "datatypes"
4
+
5
+ module Oscal
6
+ module AssessmentResult
7
+ class Activity < Assembly
8
+ attr_accessor(*(MANDATORY = %i(uuid).freeze),
9
+ *(OPTIONAL = %i(title description props links steps
10
+ related_controls responsible_roles
11
+ remarks).freeze))
12
+ end
13
+
14
+ class Attestations < Assembly
15
+ # TODO: Define this. Punting for the time being
16
+ end
17
+
18
+ class AssessmentAssets < Assembly
19
+ attr_accessor(*(MANDATORY = %i(assessment_platforms).freeze),
20
+ *(OPTIONAL = %i(components).freeze))
21
+ end
22
+
23
+ class AssessmentLog
24
+ attr_accessor(*(MANDATORY = %i(entries).freeze))
25
+ end
26
+
27
+ class AssessmentPlatform < Assembly
28
+ # TODO: Define this. Punting for the time being
29
+ end
30
+
31
+ class AssessmentTask < Assembly
32
+ attr_accessor(*(MANDATORY = %i(uuid type title).freeze),
33
+ *(OPTIONAL = %i(description props links timing dependencies
34
+ tasks associated_activities subjects
35
+ responsible_roles remarks).freeze))
36
+ end
37
+
38
+ class AssociatedActivity < Assembly
39
+ attr_accessor(*(MANDATORY = %i(activity_uuid subjects).freeze),
40
+ *(OPTIONAL = %i(props links responsible_roles
41
+ remarks).freeze))
42
+ end
43
+
44
+ class AssociatedRisk < Assembly
45
+ attr_accessor(*(MANDATORY = %i(risk_uuid).freeze))
46
+ end
47
+
48
+ class Attestation < Assembly
49
+ # TODO: Define this. Punting for the time being
50
+ end
51
+
52
+ class Component < Assembly
53
+ # TODO: Define this. Punting for the time being
54
+ end
55
+
56
+ class ControlObjectiveSelection < Assembly
57
+ attr_accessor(*(OPTIONAL = %i(description props links include_all
58
+ include_objectives exclude_objectives
59
+ remarks).freeze))
60
+ end
61
+
62
+ class ControlSelection < Assembly
63
+ attr_accessor(*(OPTIONAL = %i(description props links include_all
64
+ include_controls exclude_controls
65
+ remarks).freeze))
66
+ end
67
+
68
+ class Entry < Assembly
69
+ # TODO: Define this. Punting for the time being
70
+ end
71
+
72
+ class ExcludeControl
73
+ # TODO: Define this. Punting for the time being
74
+ # NOTE: This has the same name as profile/exclude-control, but a different
75
+ # definition!
76
+ end
77
+
78
+ class ExcludeObjective < Assembly
79
+ attr_accessor(*(MANDATORY = %i(objective_id).freeze))
80
+ end
81
+
82
+ class Finding < Assembly
83
+ attr_accessor(*(MANDATORY = %i(uuid title description target).freeze),
84
+ *(OPTIONAL = %i(implementation_statement_uuid
85
+ related_observations related_risks
86
+ remarks).freeze))
87
+ end
88
+
89
+ class ImportAP < Assembly
90
+ attr_accessor(*(MANDATORY = %i(href).freeze),
91
+ *(OPTIONAL = %i(remarks).freeze))
92
+ end
93
+
94
+ class IncludeAll < Assembly
95
+ # This is an Assembly that acts like a flag - it has no no contents
96
+ end
97
+
98
+ class IncludeControl < Assembly
99
+ attr_accessor(*(MANDATORY = %i(control_id).freeze),
100
+ *(OPTIONAL = %i(statement_ids).freeze))
101
+ end
102
+
103
+ class IncludeObjective < Assembly
104
+ attr_accessor(*(MANDATORY = %i(objective_id).freeze))
105
+ end
106
+
107
+ class InventoryItem < Assembly
108
+ # TODO: Define this. Punting for the time being
109
+ end
110
+
111
+ class LocalDefinitions < Assembly
112
+ # NOTE we deviate fromt the spec here! local-definitions is defined twice
113
+ # with different attributes. All attributes are optional, so we merge it
114
+ # into one big back of optional attributes
115
+ attr_accessor(*(OPTIONAL = %i(objectives_and_methods activities
116
+ remarks components inventory_items users
117
+ assesssment_assets tasks).freeze))
118
+ end
119
+
120
+ class ObjectivesAndMethods < Assembly
121
+ attr_accessor(*(MANDATORY = %i(control_id parts).freeze),
122
+ *(OPTIONAL = %i(description props links remarks).freeze))
123
+ end
124
+
125
+ class Observation < Assembly
126
+ attr_accessor(*(MANDATORY = %i(uuid description methods collected).freeze),
127
+ *(OPTIONAL = %i(title props links methods types origins
128
+ subjects relevent_evidence expires
129
+ remarks).freeze))
130
+ end
131
+
132
+ class RelatedControls < Assembly
133
+ attr_accessor(*(MANDATORY = %i(control_selections).freeze),
134
+ *(OPTIONAL = %i(description props links
135
+ control_objective_selections
136
+ remarks).freeze))
137
+ end
138
+
139
+ class RelatedObservation < Assembly
140
+ attr_accessor(*(MANDATORY = %i(observation_uuid).freeze),
141
+ *(OPTIONAL = %i().freeze))
142
+ end
143
+
144
+ class ResponsibleRole < Assembly
145
+ attr_accessor(*(MANDATORY = %i(role_id).freeze),
146
+ *(OPTIONAL = %i(props links party_uuids remarks).freeze))
147
+ end
148
+
149
+ class Result < Assembly
150
+ attr_accessor(*(MANDATORY = %i(uuid title description start).freeze),
151
+ *(OPTIONAL = %i(end props links local_definitions
152
+ reviewed_controls attestations
153
+ assessment_log observations risks findings
154
+ remarks).freeze))
155
+ end
156
+
157
+ class ReviewedControls < Assembly
158
+ attr_accessor(*(MANDATORY = %i(control_selections).freeze),
159
+ *(OPTIONAL = %i(description props links
160
+ control_objective_selections
161
+ remarks).freeze))
162
+ end
163
+
164
+ class Risk < Assembly
165
+ attr_accessor(*(MANDATORY = %i(uuid title description statement
166
+ status).freeze),
167
+ *(OPTIONAL = %i(propse links origins threat_ids
168
+ characterizations mitigating_factors
169
+ deadline remediations risk_log
170
+ related_observations).freeze))
171
+ end
172
+
173
+ class Status
174
+ # Status is defined twice, once as a datatype, once as an assembly
175
+ # this class figures out which is which
176
+ def initialize(input)
177
+ if input.instance_of? String
178
+ StatusString.new(input)
179
+ elsif input.instance_of? Hash
180
+ StatusAssembly.new(input)
181
+ else
182
+ raise Oscal::InvalidTypeError, "status must be a string or assembly"
183
+ end
184
+ end
185
+ end
186
+
187
+ class StatusString < TokenDataType
188
+ end
189
+
190
+ class StatusAssembly < Assembly
191
+ attr_accessor(*(MANDATORY = %i(state).freeze),
192
+ *(OPTIONAL = %i(reason remarks).freeze))
193
+ end
194
+
195
+ class Step < Assembly
196
+ attr_accessor(*(MANDATORY = %i(uuid).freeze),
197
+ *(OPTIONAL = %i(title description props links
198
+ reviewed_controls responsible_roles
199
+ remarks).freeze))
200
+ end
201
+
202
+ class Subject < Assembly
203
+ attr_accessor(*(OPTIONAL = %i(subject_uuid type
204
+ description props links include_all
205
+ include_subjects exclude_subjects
206
+ remarks).freeze))
207
+ end
208
+
209
+ class Target < Assembly
210
+ attr_accessor(*(MANDATORY = %i(type target_id status).freeze),
211
+ *(OPTIONAL = %i(title description props links
212
+ implementation_status remarks).freeze))
213
+ end
214
+
215
+ class Task < Assembly
216
+ # TODO: Define this. Punting for the time being
217
+ end
218
+
219
+ class User < Assembly
220
+ # TODO: Define this. Punting for the time being
221
+ end
222
+
223
+ ##########################################
224
+
225
+ class AssessmentResult < Assembly
226
+ attr_accessor(*(MANDATORY = %i(uuid metadata import_ap results).freeze),
227
+ *(OPTIONAL = %i(local_definitions back_matter).freeze))
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,80 @@
1
+ require_relative("datatypes")
2
+ require_relative("list")
3
+
4
+ module Oscal
5
+ ATTRIBUTE_TYPE_HASH = {
6
+ activities: AssessmentResult::ActivityArray,
7
+ activity_uuid: Uuid,
8
+ assessment_plan: AssessmentPlan::AssessmentPlan,
9
+ assessment_platforms: AssessmentResult::AssessmentPlatformArray,
10
+ assessment_results: AssessmentResult::AssessmentResult,
11
+ assessment_log: AssessmentResult::AssessmentLog,
12
+ associated_activities: AssessmentResult::AssociatedActivityArray,
13
+ attestations: AssessmentResult::AttestationArray,
14
+ collected: DateTimeWithTimezoneDataType,
15
+ components: AssessmentResult::ComponentArray,
16
+ control_id: TokenDataType,
17
+ control_objective_selections: AssessmentResult::ControlObjectiveSelectionArray,
18
+ control_selections: AssessmentResult::ControlSelectionArray,
19
+ description: MarkupMultilineDataType,
20
+ end: DateTimeWithTimezoneDataType,
21
+ entries: AssessmentResult::EntryArray,
22
+ exclude_controls: AssessmentResult::ExcludeControlArray,
23
+ exclude_objectives: AssessmentResult::ExcludeObjectiveArray,
24
+ expires: DateTimeWithTimezoneDataType,
25
+ findings: AssessmentResult::FindingArray,
26
+ href: UriReference,
27
+ implementation_statement_uuid: Uuid,
28
+ import_ap: AssessmentResult::ImportAP,
29
+ import_ssp: AssessmentPlan::ImportSSP,
30
+ include_all: AssessmentResult::IncludeAll,
31
+ include_controls: AssessmentResult::IncludeControlArray,
32
+ inventory_items: AssessmentResult::InventoryItemArray,
33
+ links: AssessmentResult::LinkArray,
34
+ local_definitions: AssessmentResult::LocalDefinitions,
35
+ metadata: MetadataBlockWrapper,
36
+ methods: AssessmentResult::MethodArray,
37
+ objective_id: TokenDataType,
38
+ objectives_and_methods: AssessmentResult::ObjectivesAndMethodsArray,
39
+ observations: AssessmentResult::ObservationArray,
40
+ observation_uuid: Uuid,
41
+ parts: AssessmentResult::PartArray,
42
+ party_uuids: AssessmentResult::PartyUuidArray,
43
+ props: AssessmentResult::PropArray,
44
+ related_controls: AssessmentResult::RelatedControls,
45
+ related_observations: AssessmentResult::RelatedObservationArray,
46
+ related_risks: AssessmentResult::RelatedRiskArray,
47
+ remarks: MarkupMultilineDataType,
48
+ responsible_roles: AssessmentResult::ResponsibleRoleArray,
49
+ results: AssessmentResult::ResultArray,
50
+ reviewed_controls: AssessmentResult::ReviewedControls,
51
+ risks: AssessmentResult::RiskArray,
52
+ risk_uuid: Uuid,
53
+ role_id: TokenDataType,
54
+ start: DateTimeWithTimezoneDataType,
55
+ state: TokenDataType,
56
+ status: AssessmentResult::Status,
57
+ statement: MarkupMultilineDataType,
58
+ statement_ids: AssessmentResult::StatementIdArray,
59
+ steps: AssessmentResult::StepArray,
60
+ subjects: AssessmentResult::SubjectArray,
61
+ subject_uuid: Uuid,
62
+ target: AssessmentResult::Target,
63
+ target_id: TokenDataType,
64
+ tasks: AssessmentResult::AssessmentTaskArray,
65
+ title: MarkupMultilineDataType,
66
+ type: TokenDataType,
67
+ types: AssessmentResult::TypeArray,
68
+ uuid: Uuid,
69
+ users: AssessmentResult::UserArray,
70
+ }.freeze
71
+
72
+ def self.get_type_of_attribute(attribute_name)
73
+ klass = Oscal::ATTRIBUTE_TYPE_HASH[attribute_name.to_sym]
74
+ if klass == nil
75
+ raise InvalidTypeError, "No type found for #{attribute_name}"
76
+ else
77
+ klass
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,20 @@
1
+ require_relative "base_class"
2
+
3
+ module Oscal
4
+ class BackMatter < Oscal::BaseClass
5
+ KEY = %i(resources)
6
+
7
+ attr_accessor *KEY
8
+
9
+ attr_serializable *KEY
10
+
11
+ def set_value(key_name, val)
12
+ case key_name
13
+ when "resources"
14
+ Resource.wrap(val)
15
+ else
16
+ val
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "base_class"
2
+
3
+ module Oscal
4
+ class Base64Object < Oscal::BaseClass
5
+ KEY = %i(filename media_type value)
6
+
7
+ attr_accessor *KEY
8
+
9
+ attr_serializable *KEY
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ require_relative "serializer"
2
+
3
+ module Oscal
4
+ class BaseClass
5
+ include Serializer
6
+
7
+ KEY = %i(val)
8
+
9
+ attr_accessor *KEY
10
+
11
+ attr_serializable *KEY
12
+
13
+ def self.wrap(obj)
14
+ klass = self
15
+ return obj if obj.is_a? klass
16
+ return klass.new(obj) unless obj.is_a? Array
17
+
18
+ obj.map do |x|
19
+ klass.wrap(x)
20
+ end
21
+ end
22
+
23
+ def initialize(options = {})
24
+ klass = self.class
25
+
26
+ unless options.is_a? Hash
27
+ options = { klass::KEY.first.to_s => options }
28
+ end
29
+
30
+ options.each_pair.each do |key, val|
31
+ key_name = key.gsub("-", "_")
32
+ key_name = "klass" if key == "class"
33
+
34
+ unless klass::KEY.include?(key_name.to_sym)
35
+ raise UnknownAttributeError.new(
36
+ "Unknown key `#{key}` in #{klass.name}",
37
+ )
38
+ end
39
+
40
+ val = set_value(key_name, val)
41
+
42
+ send("#{key_name}=", val)
43
+ end
44
+ end
45
+
46
+ def set_value(_key_name, val)
47
+ val
48
+ end
49
+ end
50
+ end
data/lib/oscal/catalog.rb CHANGED
@@ -1,22 +1,63 @@
1
+ require "date"
2
+ require_relative "serializer"
3
+ require_relative "common_utils"
4
+
1
5
  module Oscal
2
6
  class Catalog
3
- attr_accessor :uuid, :metadata, :groups
7
+ include Serializer
8
+ include CommonUtils
9
+
10
+ KEY = %i(uuid metadata params controls groups back_matter)
11
+ attr_accessor *KEY
12
+
13
+ attr_serializable *KEY
4
14
 
5
- def initialize(metadata, groups)
6
- @metadata = MetadataBlock.new(metadata)
7
- @groups = Group.wrap(groups)
15
+ def initialize(uuid, metadata, params, controls, groups, back_matter)
16
+ @uuid = uuid
17
+ @metadata = MetadataBlock.new(metadata)
18
+ @params = Parameter.wrap(params) if params
19
+ @controls = Control.wrap(controls) if controls
20
+ @groups = Group.wrap(groups) if groups
21
+ @back_matter = BackMatter.wrap(back_matter) if back_matter
22
+
23
+ @all_controls = []
8
24
  end
9
25
 
10
26
  def self.load_from_yaml(path)
11
- yaml_data = YAML.load_file(path, permitted_classes: [Time, Date, DateTime])
27
+ yaml_data = safe_load_yaml(path)
28
+ yaml_catalog = yaml_data["catalog"]
29
+
30
+ uuid = yaml_catalog["uuid"]
31
+ metadata = yaml_catalog["metadata"]
32
+ params = yaml_catalog["params"]
33
+ controls = yaml_catalog["controls"]
34
+ groups = yaml_catalog["groups"]
35
+ back_matter = yaml_catalog["back-matter"]
12
36
 
13
- yaml_catalog = yaml_data['catalog']
37
+ Catalog.new(uuid, metadata, params, controls, groups, back_matter)
38
+ end
39
+
40
+ def get_all_controls
41
+ append_all_control_group(self)
42
+ @all_controls.uniq
43
+ end
14
44
 
15
- metadata = yaml_catalog['metadata']
16
- group_data = yaml_catalog['groups']
45
+ def append_all_control_group(obj)
46
+ if /Oscal::Control/.match?(obj.to_s)
47
+ @all_controls << obj
48
+ end
17
49
 
18
- Catalog.new(metadata, group_data)
50
+ if obj.respond_to?(:controls) && !obj.controls.nil?
51
+ obj.controls.each do |c|
52
+ append_all_control_group(c)
53
+ end
54
+ end
55
+
56
+ if obj.respond_to?(:groups) && !obj.groups.nil?
57
+ obj.groups.each do |g|
58
+ append_all_control_group(g)
59
+ end
60
+ end
19
61
  end
20
62
  end
21
-
22
63
  end
@@ -0,0 +1,11 @@
1
+ require_relative "base_class"
2
+
3
+ module Oscal
4
+ class Choice < Oscal::BaseClass
5
+ KEY = %i(val)
6
+
7
+ attr_accessor *KEY
8
+
9
+ attr_serializable *KEY
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "base_class"
2
+
3
+ module Oscal
4
+ class Citation < Oscal::BaseClass
5
+ KEY = %i(text props links)
6
+
7
+ attr_accessor *KEY
8
+
9
+ attr_serializable *KEY
10
+
11
+ def set_value(key_name, val)
12
+ case key_name
13
+ when "props"
14
+ Property.wrap(val)
15
+ when "links"
16
+ Link.wrap(val)
17
+ else
18
+ val
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "base_class"
2
+
3
+ module Oscal
4
+ class Combine < Oscal::BaseClass
5
+ KEY = %i(method)
6
+
7
+ attr_accessor *KEY
8
+
9
+ attr_serializable *KEY
10
+ end
11
+ end