oscal 0.2.2 → 0.4.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.
- checksums.yaml +4 -4
- data/lib/oscal/v1_2_1/all_models.rb +16379 -0
- data/lib/oscal/v1_2_1.rb +109 -0
- data/lib/oscal/version.rb +1 -1
- data/lib/oscal/version_registry.rb +183 -0
- data/lib/oscal/versioned.rb +88 -0
- data/lib/oscal.rb +93 -3
- metadata +38 -110
- data/.docker/Dockerfile +0 -19
- data/.docker/Makefile +0 -43
- data/.docker/docker-compose.yml +0 -14
- data/.docker/readme.md +0 -61
- data/.github/workflows/rake.yml +0 -15
- data/.github/workflows/release.yml +0 -24
- data/.gitignore +0 -13
- data/.gitmodules +0 -3
- data/.hound.yml +0 -5
- data/.rspec +0 -2
- data/.rubocop.yml +0 -10
- data/.ruby-version +0 -1
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -16
- data/LICENSE +0 -25
- data/Makefile +0 -1
- data/README.adoc +0 -115
- data/Rakefile +0 -19
- data/bin/console +0 -30
- data/bin/rspec +0 -27
- data/bin/setup +0 -8
- data/docker-compose.yml +0 -1
- data/exe/convert2oscalyaml.rb +0 -560
- data/lib/oscal/add.rb +0 -26
- data/lib/oscal/address.rb +0 -22
- data/lib/oscal/address_line.rb +0 -11
- data/lib/oscal/alter.rb +0 -22
- data/lib/oscal/assembly.rb +0 -119
- data/lib/oscal/assessment_plan.rb +0 -28
- data/lib/oscal/assessment_result.rb +0 -230
- data/lib/oscal/attribute_type_hash.rb +0 -81
- data/lib/oscal/back_matter.rb +0 -20
- data/lib/oscal/base64_object.rb +0 -11
- data/lib/oscal/base_class.rb +0 -50
- data/lib/oscal/catalog.rb +0 -63
- data/lib/oscal/choice.rb +0 -11
- data/lib/oscal/citation.rb +0 -22
- data/lib/oscal/combine.rb +0 -11
- data/lib/oscal/common_utils.rb +0 -35
- data/lib/oscal/constraint.rb +0 -20
- data/lib/oscal/control.rb +0 -28
- data/lib/oscal/custom.rb +0 -22
- data/lib/oscal/datatypes.rb +0 -50
- data/lib/oscal/document_id.rb +0 -11
- data/lib/oscal/email_address.rb +0 -11
- data/lib/oscal/exclude_control.rb +0 -22
- data/lib/oscal/external_id.rb +0 -11
- data/lib/oscal/group.rb +0 -33
- data/lib/oscal/guideline.rb +0 -11
- data/lib/oscal/hash_object.rb +0 -11
- data/lib/oscal/import_object.rb +0 -22
- data/lib/oscal/include_control.rb +0 -22
- data/lib/oscal/insert_control.rb +0 -22
- data/lib/oscal/link.rb +0 -11
- data/lib/oscal/list.rb +0 -160
- data/lib/oscal/location.rb +0 -31
- data/lib/oscal/location_uuid.rb +0 -11
- data/lib/oscal/logger.rb +0 -12
- data/lib/oscal/matching.rb +0 -11
- data/lib/oscal/member_of_organization.rb +0 -11
- data/lib/oscal/merge.rb +0 -20
- data/lib/oscal/metadata_block.rb +0 -36
- data/lib/oscal/modify.rb +0 -22
- data/lib/oscal/parameter.rb +0 -31
- data/lib/oscal/parsing_functions.rb +0 -19
- data/lib/oscal/part.rb +0 -24
- data/lib/oscal/party.rb +0 -36
- data/lib/oscal/party_uuid.rb +0 -11
- data/lib/oscal/profile.rb +0 -37
- data/lib/oscal/property.rb +0 -11
- data/lib/oscal/remove.rb +0 -11
- data/lib/oscal/resource.rb +0 -29
- data/lib/oscal/responsible_party.rb +0 -24
- data/lib/oscal/revision.rb +0 -23
- data/lib/oscal/rlink.rb +0 -20
- data/lib/oscal/role.rb +0 -22
- data/lib/oscal/select.rb +0 -20
- data/lib/oscal/serializer.rb +0 -75
- data/lib/oscal/set_parameter.rb +0 -31
- data/lib/oscal/telephone_number.rb +0 -11
- data/lib/oscal/test.rb +0 -11
- data/lib/oscal/url.rb +0 -11
- data/lib/oscal/value.rb +0 -37
- data/lib/oscal/with_id.rb +0 -40
- data/oscal.gemspec +0 -30
- data/sig/oscal.rbs +0 -4
- data/spec/oscal/catalog_spec.rb +0 -40
- data/spec/oscal_spec.rb +0 -18
- data/spec/sample_inputs/import-ap.json +0 -4
- data/spec/spec_helper.rb +0 -15
data/lib/oscal/v1_2_1.rb
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "metaschema"
|
|
4
|
+
|
|
5
|
+
module Oscal
|
|
6
|
+
# OSCAL 1.2.1 version module.
|
|
7
|
+
#
|
|
8
|
+
# Contains all model classes for OSCAL version 1.2.1.
|
|
9
|
+
# Classes are pre-generated from the OSCAL metaschema and loaded
|
|
10
|
+
# from lib/oscal/v1_2_1/*.rb.
|
|
11
|
+
module V1_2_1
|
|
12
|
+
extend Oscal::Versioned
|
|
13
|
+
|
|
14
|
+
def self.register_id
|
|
15
|
+
:oscal_1_2_1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.oscal_version
|
|
19
|
+
"1.2.1"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.fallback_registers
|
|
23
|
+
%i[oscal_common default]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Maps clean class names to ModelGenerator keys.
|
|
27
|
+
ROOT_CLASS_KEYS = {
|
|
28
|
+
"Catalog" => "Assembly_catalog",
|
|
29
|
+
"Profile" => "Assembly_profile",
|
|
30
|
+
"ComponentDefinition" => "Assembly_component_definition",
|
|
31
|
+
"SystemSecurityPlan" => "Assembly_system_security_plan",
|
|
32
|
+
"AssessmentPlan" => "Assembly_assessment_plan",
|
|
33
|
+
"AssessmentResults" => "Assembly_assessment_results",
|
|
34
|
+
"PlanOfActionAndMilestones" => "Assembly_plan_of_action_and_milestones",
|
|
35
|
+
"MappingCollection" => "Assembly_mapping_collection",
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
def self.register_models!
|
|
39
|
+
load_pre_generated
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Load pre-generated model files from lib/oscal/v1_2_1/
|
|
43
|
+
def self.load_pre_generated
|
|
44
|
+
dir = File.join(__dir__, "v1_2_1")
|
|
45
|
+
if Dir.exist?(dir) && !Dir.empty?(dir)
|
|
46
|
+
Dir[File.join(dir, "*.rb")].sort.each { |f| require f }
|
|
47
|
+
else
|
|
48
|
+
# Fall back to runtime generation if pre-generated files don't exist
|
|
49
|
+
generate_runtime
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Register all classes with the version register
|
|
53
|
+
register_all_classes
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Generate classes at runtime from the OSCAL metaschema.
|
|
57
|
+
def self.generate_runtime
|
|
58
|
+
metaschema_path = find_metaschema("oscal_complete_metaschema.xml")
|
|
59
|
+
return unless metaschema_path
|
|
60
|
+
|
|
61
|
+
@runtime_classes = Metaschema::ModelGenerator.generate_from_file(metaschema_path)
|
|
62
|
+
|
|
63
|
+
# Expose as constants on this module
|
|
64
|
+
@runtime_classes.each do |key, klass|
|
|
65
|
+
const_name = class_key_to_const_name(key)
|
|
66
|
+
const_set(const_name, klass) unless const_defined?(const_name)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Register all loaded classes with this version's register.
|
|
71
|
+
def self.register_all_classes
|
|
72
|
+
constants.each do |const_name|
|
|
73
|
+
klass = const_get(const_name)
|
|
74
|
+
next unless klass.is_a?(Class) && klass < Lutaml::Model::Serializable
|
|
75
|
+
|
|
76
|
+
register.register_model(klass)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Resolve a class by clean name or ModelGenerator key.
|
|
81
|
+
def self.resolve_class(name)
|
|
82
|
+
const_get(name) if const_defined?(name)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def self.find_metaschema(filename)
|
|
88
|
+
paths = [
|
|
89
|
+
# Local submodule first
|
|
90
|
+
File.expand_path("../../fixtures/oscal/src/metaschema/#{filename}", __dir__),
|
|
91
|
+
# Sibling metaschema repo
|
|
92
|
+
File.expand_path("../../../metaschema/spec/fixtures/oscal/src/metaschema/#{filename}", __dir__),
|
|
93
|
+
File.expand_path("../../../../metaschema/spec/fixtures/oscal/src/metaschema/#{filename}", __dir__),
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
paths.find { |p| File.exist?(p) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.class_key_to_const_name(key)
|
|
100
|
+
parts = key.sub(/\A(Assembly|Field|Flag)_/, "").split("_")
|
|
101
|
+
name = parts.map(&:capitalize).join
|
|
102
|
+
# Avoid conflicts with Ruby built-ins
|
|
103
|
+
name = "#{name}Field" if %w[Hash Method Object Class Module].include?(name)
|
|
104
|
+
name
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
VersionRegistry.register_version("1.2.1", V1_2_1)
|
|
109
|
+
end
|
data/lib/oscal/version.rb
CHANGED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Oscal
|
|
6
|
+
# Registry for OSCAL version-specific model trees.
|
|
7
|
+
#
|
|
8
|
+
# Manages the mapping between OSCAL version strings and their modules.
|
|
9
|
+
# Version detection is based on the <oscal-version> element in the
|
|
10
|
+
# document's metadata, not on XML namespace (all OSCAL versions share
|
|
11
|
+
# the same namespace: http://csrc.nist.gov/ns/oscal/1.0).
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# # Get register for OSCAL 1.2.1
|
|
15
|
+
# register = Oscal::VersionRegistry.register_for_version("1.2.1")
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# # Detect version from an OSCAL document
|
|
19
|
+
# version = Oscal::VersionRegistry.detect_version(xml_string)
|
|
20
|
+
module VersionRegistry
|
|
21
|
+
@versions = {}
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
# Register a version module.
|
|
25
|
+
#
|
|
26
|
+
# @param version [String] OSCAL version string (e.g., "1.2.1")
|
|
27
|
+
# @param mod [Module] The version module (e.g., Oscal::V1_2_1)
|
|
28
|
+
def register_version(version, mod)
|
|
29
|
+
@versions = @versions.merge(version => mod)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Get the version module for a version string.
|
|
33
|
+
#
|
|
34
|
+
# @param version [String] OSCAL version string
|
|
35
|
+
# @return [Module, nil]
|
|
36
|
+
def version_module(version)
|
|
37
|
+
@versions[version]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Get the register for a specific OSCAL version.
|
|
41
|
+
#
|
|
42
|
+
# @param version [String] OSCAL version string
|
|
43
|
+
# @return [Lutaml::Model::Register, nil]
|
|
44
|
+
def register_for_version(version)
|
|
45
|
+
mod = @versions[version]
|
|
46
|
+
return nil unless mod
|
|
47
|
+
|
|
48
|
+
mod.init_models! unless mod.initialized?
|
|
49
|
+
mod.register
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get the latest registered version module.
|
|
53
|
+
#
|
|
54
|
+
# @return [Module, nil]
|
|
55
|
+
def latest_module
|
|
56
|
+
return nil if @versions.empty?
|
|
57
|
+
|
|
58
|
+
@versions.values.last
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get the latest register (initializes if needed).
|
|
62
|
+
#
|
|
63
|
+
# @return [Lutaml::Model::Register, nil]
|
|
64
|
+
def latest_register
|
|
65
|
+
mod = latest_module
|
|
66
|
+
return nil unless mod
|
|
67
|
+
|
|
68
|
+
mod.init_models! unless mod.initialized?
|
|
69
|
+
mod.register
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# All registered version strings, sorted.
|
|
73
|
+
#
|
|
74
|
+
# @return [Array<String>]
|
|
75
|
+
def available_versions
|
|
76
|
+
@versions.keys.sort
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Detect OSCAL version from a document string (XML, JSON, or YAML).
|
|
80
|
+
#
|
|
81
|
+
# Looks for the "oscal-version" field in the document's metadata.
|
|
82
|
+
#
|
|
83
|
+
# @param content [String] Document content
|
|
84
|
+
# @return [String, nil] Detected version string
|
|
85
|
+
def detect_version(content)
|
|
86
|
+
return nil if content.nil? || content.strip.empty?
|
|
87
|
+
|
|
88
|
+
if content.lstrip.start_with?("<")
|
|
89
|
+
detect_version_xml(content)
|
|
90
|
+
elsif content.lstrip.start_with?("{")
|
|
91
|
+
detect_version_json(content)
|
|
92
|
+
else
|
|
93
|
+
detect_version_yaml(content)
|
|
94
|
+
end
|
|
95
|
+
rescue StandardError
|
|
96
|
+
nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Detect the root model type from a document.
|
|
100
|
+
#
|
|
101
|
+
# @param content [String] Document content
|
|
102
|
+
# @return [Symbol, nil] Model type (:catalog, :profile, etc.)
|
|
103
|
+
def detect_model_type(content)
|
|
104
|
+
return nil if content.nil? || content.strip.empty?
|
|
105
|
+
|
|
106
|
+
if content.lstrip.start_with?("<")
|
|
107
|
+
detect_model_type_xml(content)
|
|
108
|
+
elsif content.lstrip.start_with?("{")
|
|
109
|
+
detect_model_type_json(content)
|
|
110
|
+
else
|
|
111
|
+
detect_model_type_yaml(content)
|
|
112
|
+
end
|
|
113
|
+
rescue StandardError
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def detect_version_xml(xml)
|
|
120
|
+
doc = Nokogiri::XML(xml, &:noblanks)
|
|
121
|
+
node = doc.at_xpath("//*[local-name()='oscal-version']")
|
|
122
|
+
node&.text&.strip
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def detect_version_json(json_str)
|
|
126
|
+
data = JSON.parse(json_str)
|
|
127
|
+
# OSCAL JSON wraps in a root key
|
|
128
|
+
root_key = data.keys.first
|
|
129
|
+
metadata = data[root_key]&.dig("metadata")
|
|
130
|
+
metadata&.dig("oscal-version")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def detect_version_yaml(yaml_str)
|
|
134
|
+
require "yaml"
|
|
135
|
+
data = YAML.safe_load(yaml_str, permitted_classes: [Date, Time, Symbol])
|
|
136
|
+
return nil unless data.is_a?(Hash)
|
|
137
|
+
|
|
138
|
+
root_key = data.keys.first
|
|
139
|
+
metadata = data[root_key]&.dig("metadata")
|
|
140
|
+
metadata&.dig("oscal-version")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
ROOT_ELEMENTS = %w[
|
|
144
|
+
catalog profile component-definition
|
|
145
|
+
system-security-plan assessment-plan
|
|
146
|
+
assessment-results plan-of-action-and-milestones
|
|
147
|
+
mapping-collection
|
|
148
|
+
].freeze
|
|
149
|
+
|
|
150
|
+
ROOT_KEY_MAP = {
|
|
151
|
+
"catalog" => :catalog,
|
|
152
|
+
"profile" => :profile,
|
|
153
|
+
"component-definition" => :component_definition,
|
|
154
|
+
"system-security-plan" => :system_security_plan,
|
|
155
|
+
"assessment-plan" => :assessment_plan,
|
|
156
|
+
"assessment-results" => :assessment_results,
|
|
157
|
+
"plan-of-action-and-milestones" => :plan_of_action_and_milestones,
|
|
158
|
+
"mapping-collection" => :mapping_collection,
|
|
159
|
+
}.freeze
|
|
160
|
+
|
|
161
|
+
def detect_model_type_xml(xml)
|
|
162
|
+
doc = Nokogiri::XML(xml, &:noblanks)
|
|
163
|
+
root_name = doc.root&.name
|
|
164
|
+
ROOT_KEY_MAP[root_name]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def detect_model_type_json(json_str)
|
|
168
|
+
data = JSON.parse(json_str)
|
|
169
|
+
root_key = data.keys.first
|
|
170
|
+
ROOT_KEY_MAP[root_key]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def detect_model_type_yaml(yaml_str)
|
|
174
|
+
require "yaml"
|
|
175
|
+
data = YAML.safe_load(yaml_str, permitted_classes: [Date, Time, Symbol])
|
|
176
|
+
return nil unless data.is_a?(Hash)
|
|
177
|
+
|
|
178
|
+
root_key = data.keys.first
|
|
179
|
+
ROOT_KEY_MAP[root_key]
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Oscal
|
|
4
|
+
# Base module for version-specific OSCAL model trees.
|
|
5
|
+
#
|
|
6
|
+
# Each version module (V1_2_1, V1_3_0, etc.) extends this
|
|
7
|
+
# and provides its own register.
|
|
8
|
+
#
|
|
9
|
+
# Unlike XMI which uses namespace-based version detection, OSCAL
|
|
10
|
+
# uses schema-version-based detection (the <oscal-version> element
|
|
11
|
+
# in metadata). All OSCAL versions share the same XML namespace.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# module Oscal::V1_2_1
|
|
15
|
+
# extend Oscal::Versioned
|
|
16
|
+
#
|
|
17
|
+
# def self.register_id
|
|
18
|
+
# :oscal_1_2_1
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# def self.oscal_version
|
|
22
|
+
# "1.2.1"
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# def self.fallback_registers
|
|
26
|
+
# %i[oscal_common default]
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# def self.register_models!
|
|
30
|
+
# # Load pre-generated or runtime-generated classes
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
module Versioned
|
|
34
|
+
def self.extended(base)
|
|
35
|
+
base.instance_eval do
|
|
36
|
+
@register = nil
|
|
37
|
+
@initialized = false
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The version-specific register for type resolution.
|
|
42
|
+
def register
|
|
43
|
+
@register ||= create_register
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Create and configure the register with fallback chain.
|
|
47
|
+
def create_register
|
|
48
|
+
reg = Lutaml::Model::Register.new(register_id,
|
|
49
|
+
fallback: fallback_registers)
|
|
50
|
+
Lutaml::Model::GlobalRegister.register(reg)
|
|
51
|
+
reg
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Initialize this version's model tree. Safe to call multiple times.
|
|
55
|
+
def init_models!
|
|
56
|
+
return if @initialized
|
|
57
|
+
|
|
58
|
+
register
|
|
59
|
+
register_models!
|
|
60
|
+
@initialized = true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Override in each version module to load/register model classes.
|
|
64
|
+
def register_models!
|
|
65
|
+
raise NotImplementedError, "Each version must implement #register_models!"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Override: the register ID symbol for this version.
|
|
69
|
+
def register_id
|
|
70
|
+
raise NotImplementedError, "Each version must implement #register_id"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Override: the OSCAL version string (e.g., "1.2.1").
|
|
74
|
+
def oscal_version
|
|
75
|
+
raise NotImplementedError, "Each version must implement #oscal_version"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Override: fallback register IDs for type resolution.
|
|
79
|
+
# Default chain: common types, then global default.
|
|
80
|
+
def fallback_registers
|
|
81
|
+
%i[oscal_common default]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def initialized?
|
|
85
|
+
@initialized
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
data/lib/oscal.rb
CHANGED
|
@@ -1,10 +1,100 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "oscal/version"
|
|
4
|
+
require_relative "oscal/versioned"
|
|
5
|
+
require_relative "oscal/version_registry"
|
|
6
|
+
require "lutaml/model"
|
|
4
7
|
|
|
8
|
+
# Oscal provides a clean facade for OSCAL model access.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# require "oscal"
|
|
12
|
+
#
|
|
13
|
+
# # Default version (latest) — auto-resolves constants
|
|
14
|
+
# catalog = Oscal::Catalog.from_xml(File.read("catalog.xml"))
|
|
15
|
+
#
|
|
16
|
+
# # Explicit version
|
|
17
|
+
# v121 = Oscal.version("1.2.1")
|
|
18
|
+
# catalog = v121::Catalog.from_xml(xml)
|
|
19
|
+
#
|
|
20
|
+
# # Auto-detect version and parse
|
|
21
|
+
# doc = Oscal.parse(File.read("catalog.xml"))
|
|
22
|
+
#
|
|
5
23
|
module Oscal
|
|
6
24
|
class Error < StandardError; end
|
|
7
25
|
|
|
8
|
-
|
|
9
|
-
|
|
26
|
+
@default_version = nil
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# Load the default version module. Called lazily on first access.
|
|
30
|
+
def default_version_module
|
|
31
|
+
@default_version_module ||= begin
|
|
32
|
+
require_relative "oscal/v1_2_1"
|
|
33
|
+
VersionRegistry.latest_module
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Use a specific OSCAL version.
|
|
38
|
+
#
|
|
39
|
+
# @param version_string [String] OSCAL version (e.g., "1.2.1")
|
|
40
|
+
# @return [Module] The version module
|
|
41
|
+
def version(version_string)
|
|
42
|
+
mod = VersionRegistry.version_module(version_string)
|
|
43
|
+
raise Error, "Unknown OSCAL version: #{version_string}" unless mod
|
|
44
|
+
|
|
45
|
+
mod.init_models! unless mod.initialized?
|
|
46
|
+
mod
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Parse an OSCAL document with automatic version detection.
|
|
50
|
+
#
|
|
51
|
+
# @param content [String] XML, JSON, or YAML string
|
|
52
|
+
# @param model [Symbol] Model type (:catalog, :profile, etc.)
|
|
53
|
+
# @return [Lutaml::Model::Serializable] Parsed instance
|
|
54
|
+
def parse(content, model: nil)
|
|
55
|
+
version = VersionRegistry.detect_version(content)
|
|
56
|
+
if version
|
|
57
|
+
ver_mod = VersionRegistry.version_module(version)
|
|
58
|
+
if ver_mod
|
|
59
|
+
ver_mod.init_models! unless ver_mod.initialized?
|
|
60
|
+
register = ver_mod.register
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
ver_mod ||= default_version_module
|
|
65
|
+
register ||= ver_mod.register
|
|
66
|
+
|
|
67
|
+
model_type = model || VersionRegistry.detect_model_type(content)
|
|
68
|
+
raise Error, "Cannot detect model type. Specify with model: :catalog" unless model_type
|
|
69
|
+
|
|
70
|
+
class_name = model_type.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }
|
|
71
|
+
klass = ver_mod.resolve_class(class_name)
|
|
72
|
+
raise Error, "Unknown model type: #{class_name}" unless klass
|
|
73
|
+
|
|
74
|
+
if content.lstrip.start_with?("<")
|
|
75
|
+
register ? klass.from_xml(content, register: register) : klass.from_xml(content)
|
|
76
|
+
elsif content.lstrip.start_with?("{")
|
|
77
|
+
klass.from_json(content)
|
|
78
|
+
else
|
|
79
|
+
klass.from_yaml(content)
|
|
80
|
+
end
|
|
81
|
+
rescue StandardError => e
|
|
82
|
+
raise Error, "Failed to parse OSCAL document: #{e.message}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Resolve missing constants from the default version module.
|
|
86
|
+
def const_missing(name)
|
|
87
|
+
name_s = name.to_s
|
|
88
|
+
mod = default_version_module
|
|
89
|
+
mod.init_models! unless mod.initialized?
|
|
90
|
+
|
|
91
|
+
if mod.const_defined?(name_s)
|
|
92
|
+
klass = mod.const_get(name_s)
|
|
93
|
+
const_set(name, klass)
|
|
94
|
+
klass
|
|
95
|
+
else
|
|
96
|
+
super
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
10
100
|
end
|
metadata
CHANGED
|
@@ -1,138 +1,66 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oscal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-04-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: lutaml-model
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 0.8.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
27
|
-
|
|
26
|
+
version: 0.8.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: metaschema
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.2.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.2.0
|
|
41
|
+
description: Ruby library for OSCAL (Open Security Controls Assessment Language) models.
|
|
42
|
+
Supports XML, JSON, and YAML serialization via metaschema-driven generation.
|
|
28
43
|
email:
|
|
29
44
|
- open.source@ribose.com
|
|
30
|
-
executables:
|
|
31
|
-
- convert2oscalyaml.rb
|
|
45
|
+
executables: []
|
|
32
46
|
extensions: []
|
|
33
47
|
extra_rdoc_files: []
|
|
34
48
|
files:
|
|
35
|
-
- ".docker/Dockerfile"
|
|
36
|
-
- ".docker/Makefile"
|
|
37
|
-
- ".docker/docker-compose.yml"
|
|
38
|
-
- ".docker/readme.md"
|
|
39
|
-
- ".github/workflows/rake.yml"
|
|
40
|
-
- ".github/workflows/release.yml"
|
|
41
|
-
- ".gitignore"
|
|
42
|
-
- ".gitmodules"
|
|
43
|
-
- ".hound.yml"
|
|
44
|
-
- ".rspec"
|
|
45
|
-
- ".rubocop.yml"
|
|
46
|
-
- ".ruby-version"
|
|
47
|
-
- CODE_OF_CONDUCT.md
|
|
48
|
-
- Gemfile
|
|
49
|
-
- LICENSE
|
|
50
|
-
- Makefile
|
|
51
|
-
- README.adoc
|
|
52
|
-
- Rakefile
|
|
53
|
-
- bin/console
|
|
54
|
-
- bin/rspec
|
|
55
|
-
- bin/setup
|
|
56
|
-
- docker-compose.yml
|
|
57
|
-
- exe/convert2oscalyaml.rb
|
|
58
49
|
- lib/oscal.rb
|
|
59
|
-
- lib/oscal/
|
|
60
|
-
- lib/oscal/
|
|
61
|
-
- lib/oscal/address_line.rb
|
|
62
|
-
- lib/oscal/alter.rb
|
|
63
|
-
- lib/oscal/assembly.rb
|
|
64
|
-
- lib/oscal/assessment_plan.rb
|
|
65
|
-
- lib/oscal/assessment_result.rb
|
|
66
|
-
- lib/oscal/attribute_type_hash.rb
|
|
67
|
-
- lib/oscal/back_matter.rb
|
|
68
|
-
- lib/oscal/base64_object.rb
|
|
69
|
-
- lib/oscal/base_class.rb
|
|
70
|
-
- lib/oscal/catalog.rb
|
|
71
|
-
- lib/oscal/choice.rb
|
|
72
|
-
- lib/oscal/citation.rb
|
|
73
|
-
- lib/oscal/combine.rb
|
|
74
|
-
- lib/oscal/common_utils.rb
|
|
75
|
-
- lib/oscal/constraint.rb
|
|
76
|
-
- lib/oscal/control.rb
|
|
77
|
-
- lib/oscal/custom.rb
|
|
78
|
-
- lib/oscal/datatypes.rb
|
|
79
|
-
- lib/oscal/document_id.rb
|
|
80
|
-
- lib/oscal/email_address.rb
|
|
81
|
-
- lib/oscal/exclude_control.rb
|
|
82
|
-
- lib/oscal/external_id.rb
|
|
83
|
-
- lib/oscal/group.rb
|
|
84
|
-
- lib/oscal/guideline.rb
|
|
85
|
-
- lib/oscal/hash_object.rb
|
|
86
|
-
- lib/oscal/import_object.rb
|
|
87
|
-
- lib/oscal/include_control.rb
|
|
88
|
-
- lib/oscal/insert_control.rb
|
|
89
|
-
- lib/oscal/link.rb
|
|
90
|
-
- lib/oscal/list.rb
|
|
91
|
-
- lib/oscal/location.rb
|
|
92
|
-
- lib/oscal/location_uuid.rb
|
|
93
|
-
- lib/oscal/logger.rb
|
|
94
|
-
- lib/oscal/matching.rb
|
|
95
|
-
- lib/oscal/member_of_organization.rb
|
|
96
|
-
- lib/oscal/merge.rb
|
|
97
|
-
- lib/oscal/metadata_block.rb
|
|
98
|
-
- lib/oscal/modify.rb
|
|
99
|
-
- lib/oscal/parameter.rb
|
|
100
|
-
- lib/oscal/parsing_functions.rb
|
|
101
|
-
- lib/oscal/part.rb
|
|
102
|
-
- lib/oscal/party.rb
|
|
103
|
-
- lib/oscal/party_uuid.rb
|
|
104
|
-
- lib/oscal/profile.rb
|
|
105
|
-
- lib/oscal/property.rb
|
|
106
|
-
- lib/oscal/remove.rb
|
|
107
|
-
- lib/oscal/resource.rb
|
|
108
|
-
- lib/oscal/responsible_party.rb
|
|
109
|
-
- lib/oscal/revision.rb
|
|
110
|
-
- lib/oscal/rlink.rb
|
|
111
|
-
- lib/oscal/role.rb
|
|
112
|
-
- lib/oscal/select.rb
|
|
113
|
-
- lib/oscal/serializer.rb
|
|
114
|
-
- lib/oscal/set_parameter.rb
|
|
115
|
-
- lib/oscal/telephone_number.rb
|
|
116
|
-
- lib/oscal/test.rb
|
|
117
|
-
- lib/oscal/url.rb
|
|
118
|
-
- lib/oscal/value.rb
|
|
50
|
+
- lib/oscal/v1_2_1.rb
|
|
51
|
+
- lib/oscal/v1_2_1/all_models.rb
|
|
119
52
|
- lib/oscal/version.rb
|
|
120
|
-
- lib/oscal/
|
|
121
|
-
- oscal.
|
|
122
|
-
|
|
123
|
-
- spec/oscal/catalog_spec.rb
|
|
124
|
-
- spec/oscal_spec.rb
|
|
125
|
-
- spec/sample_inputs/import-ap.json
|
|
126
|
-
- spec/spec_helper.rb
|
|
127
|
-
homepage: https://github.com/metanorma/oscal-ruby/
|
|
53
|
+
- lib/oscal/version_registry.rb
|
|
54
|
+
- lib/oscal/versioned.rb
|
|
55
|
+
homepage: https://github.com/lutaml/oscal-ruby/
|
|
128
56
|
licenses:
|
|
129
57
|
- BSD-2-Clause
|
|
130
58
|
metadata:
|
|
131
|
-
homepage_uri: https://github.com/
|
|
132
|
-
source_code_uri: https://github.com/
|
|
133
|
-
changelog_uri: https://github.com/
|
|
59
|
+
homepage_uri: https://github.com/lutaml/oscal-ruby/
|
|
60
|
+
source_code_uri: https://github.com/lutaml/oscal-ruby/
|
|
61
|
+
changelog_uri: https://github.com/lutaml/oscal-ruby/CHANGELOG
|
|
134
62
|
rubygems_mfa_required: 'true'
|
|
135
|
-
post_install_message:
|
|
63
|
+
post_install_message:
|
|
136
64
|
rdoc_options: []
|
|
137
65
|
require_paths:
|
|
138
66
|
- lib
|
|
@@ -140,15 +68,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
140
68
|
requirements:
|
|
141
69
|
- - ">="
|
|
142
70
|
- !ruby/object:Gem::Version
|
|
143
|
-
version:
|
|
71
|
+
version: 3.0.0
|
|
144
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
73
|
requirements:
|
|
146
74
|
- - ">="
|
|
147
75
|
- !ruby/object:Gem::Version
|
|
148
76
|
version: '0'
|
|
149
77
|
requirements: []
|
|
150
|
-
rubygems_version: 3.
|
|
151
|
-
signing_key:
|
|
78
|
+
rubygems_version: 3.5.22
|
|
79
|
+
signing_key:
|
|
152
80
|
specification_version: 4
|
|
153
|
-
summary:
|
|
81
|
+
summary: OSCAL Ruby models with multi-version support
|
|
154
82
|
test_files: []
|
data/.docker/Dockerfile
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
ARG RUBY_IMAGE=ruby:3.1.2-slim
|
|
2
|
-
|
|
3
|
-
FROM ${RUBY_IMAGE}
|
|
4
|
-
|
|
5
|
-
RUN apt-get update \
|
|
6
|
-
&& apt-get install -y build-essential git \
|
|
7
|
-
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
8
|
-
|
|
9
|
-
# install latest bundler
|
|
10
|
-
RUN gem install bundler
|
|
11
|
-
|
|
12
|
-
# Create app directory
|
|
13
|
-
WORKDIR /workspace
|
|
14
|
-
|
|
15
|
-
# Set bundle path
|
|
16
|
-
ENV BUNDLE_PATH /bundle
|
|
17
|
-
|
|
18
|
-
# Default to console
|
|
19
|
-
CMD ["bin/console"]
|