oscal 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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