oscal 0.1.1 → 0.2.2

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 (83) 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/.gitignore +2 -0
  7. data/.rspec +0 -1
  8. data/.rubocop.yml +1 -1
  9. data/.ruby-version +1 -0
  10. data/Gemfile +2 -0
  11. data/LICENSE +25 -0
  12. data/Makefile +1 -0
  13. data/README.adoc +3 -0
  14. data/Rakefile +13 -6
  15. data/bin/console +2 -2
  16. data/bin/rspec +27 -0
  17. data/docker-compose.yml +1 -0
  18. data/lib/oscal/add.rb +5 -4
  19. data/lib/oscal/address.rb +3 -2
  20. data/lib/oscal/address_line.rb +1 -0
  21. data/lib/oscal/alter.rb +3 -2
  22. data/lib/oscal/assembly.rb +119 -0
  23. data/lib/oscal/assessment_plan.rb +28 -0
  24. data/lib/oscal/assessment_result.rb +230 -0
  25. data/lib/oscal/attribute_type_hash.rb +81 -0
  26. data/lib/oscal/back_matter.rb +2 -1
  27. data/lib/oscal/base64_object.rb +1 -0
  28. data/lib/oscal/base_class.rb +5 -4
  29. data/lib/oscal/catalog.rb +8 -7
  30. data/lib/oscal/choice.rb +1 -0
  31. data/lib/oscal/citation.rb +3 -2
  32. data/lib/oscal/combine.rb +1 -0
  33. data/lib/oscal/common_utils.rb +1 -1
  34. data/lib/oscal/constraint.rb +2 -1
  35. data/lib/oscal/control.rb +6 -5
  36. data/lib/oscal/custom.rb +3 -2
  37. data/lib/oscal/datatypes.rb +50 -0
  38. data/lib/oscal/document_id.rb +1 -0
  39. data/lib/oscal/email_address.rb +1 -0
  40. data/lib/oscal/exclude_control.rb +3 -2
  41. data/lib/oscal/external_id.rb +1 -0
  42. data/lib/oscal/group.rb +9 -8
  43. data/lib/oscal/guideline.rb +1 -0
  44. data/lib/oscal/hash_object.rb +1 -0
  45. data/lib/oscal/import_object.rb +3 -2
  46. data/lib/oscal/include_control.rb +3 -2
  47. data/lib/oscal/insert_control.rb +3 -2
  48. data/lib/oscal/link.rb +1 -0
  49. data/lib/oscal/list.rb +160 -0
  50. data/lib/oscal/location.rb +8 -7
  51. data/lib/oscal/location_uuid.rb +1 -0
  52. data/lib/oscal/logger.rb +12 -0
  53. data/lib/oscal/matching.rb +1 -0
  54. data/lib/oscal/member_of_organization.rb +1 -0
  55. data/lib/oscal/merge.rb +2 -1
  56. data/lib/oscal/metadata_block.rb +11 -10
  57. data/lib/oscal/modify.rb +3 -2
  58. data/lib/oscal/parameter.rb +8 -7
  59. data/lib/oscal/parsing_functions.rb +19 -0
  60. data/lib/oscal/part.rb +4 -3
  61. data/lib/oscal/party.rb +11 -10
  62. data/lib/oscal/party_uuid.rb +1 -0
  63. data/lib/oscal/profile.rb +7 -6
  64. data/lib/oscal/property.rb +1 -0
  65. data/lib/oscal/remove.rb +1 -0
  66. data/lib/oscal/resource.rb +7 -6
  67. data/lib/oscal/responsible_party.rb +11 -10
  68. data/lib/oscal/revision.rb +4 -3
  69. data/lib/oscal/rlink.rb +2 -1
  70. data/lib/oscal/role.rb +3 -2
  71. data/lib/oscal/select.rb +2 -1
  72. data/lib/oscal/set_parameter.rb +8 -7
  73. data/lib/oscal/telephone_number.rb +1 -0
  74. data/lib/oscal/test.rb +1 -0
  75. data/lib/oscal/url.rb +1 -0
  76. data/lib/oscal/value.rb +5 -4
  77. data/lib/oscal/version.rb +1 -1
  78. data/lib/oscal/with_id.rb +2 -1
  79. data/lib/oscal.rb +1 -1
  80. data/spec/oscal/catalog_spec.rb +5 -4
  81. data/spec/oscal_spec.rb +11 -0
  82. data/spec/sample_inputs/import-ap.json +4 -0
  83. metadata +24 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3d2839d5707adc52e720080828918e04373d2399cbd138edd403055b1712cc0
4
- data.tar.gz: 0fc05ead23c4745d188f9bf359c2594463867fd1cce5256a271cf93befef287e
3
+ metadata.gz: b2897a40fdb6b5d42344c89a7834fa5d1ebfb899bd0a73e855023274fcff0cbe
4
+ data.tar.gz: 99d05b3aa2f7e5bee0b4340f64a660670d403d48289af8818e09e556131186bf
5
5
  SHA512:
6
- metadata.gz: 91233436e998fe98ae7d84873ac7d8b68f9fc84ec6f7a7775533ee45922beaeb507c031ed88da4fe0ed71f34e76064861e6e3b0cb340acac23d25b3134cc323f
7
- data.tar.gz: 957a56d62c3c43b5d22f4659b993acbd6e85ad344fe042fda702a3e07dda5fed576caf6ad0b742eb783597ac0e1165020af957a8d6c9699b0aad977910a910aa
6
+ metadata.gz: 141e14988d56c8942450ba94739396c8f5f41804d5df0065e0e698f5f0d9201bd8552fc6d4ac3ff7efac785dab13e80d4b7a0fec6c36708d87f8a02f74d222ca
7
+ data.tar.gz: 3aacc11681265d9db40625aa3b4a552503bc20d3eeeb423a8dd5330819f60b83b80232737d0efcc02e15a6411cc96a5c694ce2a05a0f7c74efa59cc6198f2567
@@ -0,0 +1,19 @@
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"]
data/.docker/Makefile ADDED
@@ -0,0 +1,43 @@
1
+ export SPEC ?= spec
2
+ SPEC_FILE = $(subst ../,, $(SPEC))
3
+ export RUBY_IMAGE ?= ruby:3.1.2-slim
4
+
5
+ .PHONY: up
6
+ up:
7
+ docker-compose up
8
+
9
+ .PHONY: down
10
+ down:
11
+ docker-compose down
12
+
13
+ .PHONY: test
14
+ test: rspec
15
+
16
+ .PHONY: ssh
17
+ ssh:
18
+ docker-compose run lib bash
19
+
20
+ .PHONY: install
21
+ install:
22
+ docker-compose run lib bin/setup
23
+
24
+ .PHONY: console
25
+ console:
26
+ docker-compose run lib bin/console
27
+
28
+ .PHONY: rspec
29
+ rspec:
30
+ docker-compose run lib bin/rspec ${SPEC_FILE}
31
+
32
+ .PHONY: rake
33
+ rake:
34
+ docker-compose run lib bundle exec rake
35
+
36
+ .PHONY: lint
37
+ lint:
38
+ docker-compose run lib bundle exec rubocop
39
+
40
+ .PHONY: setup
41
+ setup:
42
+ docker-compose build --build-arg RUBY_IMAGE=${RUBY_IMAGE}
43
+ docker-compose run lib bin/setup
@@ -0,0 +1,14 @@
1
+ version: "3"
2
+
3
+ services:
4
+ lib:
5
+ build:
6
+ context: .
7
+ dockerfile: ./.docker/Dockerfile
8
+
9
+ volumes:
10
+ - .:/workspace
11
+ - bundle:/bundle
12
+
13
+ volumes:
14
+ bundle:
data/.docker/readme.md ADDED
@@ -0,0 +1,61 @@
1
+ ## Docker
2
+
3
+ This directory is only meant to be used for development, and contains some
4
+ necessary setup to spin up docker containers with multiple ruby environment.
5
+
6
+ ### Setup
7
+
8
+ Before doing anything, you might want to create a symlink to the docker file and
9
+ Makefile. This would allow you to avoid some of the unnecessary work related to
10
+ the file paths To do that run the following from the root of the project.
11
+
12
+ ```
13
+ ln -sf .docker/Makefile .
14
+ ln -sf .docker/docker-compose.yml .
15
+ ```
16
+
17
+ By default it usages the most recent ruby version for docker environment, but if
18
+ you want to run it in any specific version then you can set it up by exporting
19
+ `RUBY_IMAGE` environment variable in your shell:
20
+
21
+ ```sh
22
+ export RUBY_IMAGE=ruby:3.0-buster
23
+ ```
24
+
25
+ Once everything is set then you would need to build the development images for
26
+ the first time and you can do that using:
27
+
28
+ ```sh
29
+ make setup
30
+ ```
31
+
32
+ The setup process will install all dependencies and it will also setup a volume
33
+ to speed up the repeated gem installation.
34
+
35
+ ### Playground
36
+
37
+ The `Makefile` contains two target for tests, and you can run the tests using
38
+ any of the following commands:
39
+
40
+ ```sh
41
+ make test
42
+
43
+ # or
44
+ make rspec
45
+ ```
46
+
47
+ If you need more control, and you want to do some development on the go then you
48
+ can get into the container using:
49
+
50
+ ```sh
51
+ make ssh
52
+ ```
53
+
54
+ ### Cleanup
55
+
56
+ Once you are done with your experiment then you can cleanup the docker
57
+ environment using the following command.
58
+
59
+ ```sh
60
+ make down
61
+ ```
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ .rubocop-https---**
13
+ Gemfile.lock
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
- --format documentation
2
1
  --color
3
2
  --require spec_helper
data/.rubocop.yml CHANGED
@@ -7,4 +7,4 @@ inherit_from:
7
7
  # ...
8
8
 
9
9
  AllCops:
10
- TargetRubyVersion: 2.5
10
+ TargetRubyVersion: 2.7
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.1
data/Gemfile CHANGED
@@ -12,3 +12,5 @@ gem "rspec", "~> 3.0"
12
12
  gem "rubocop", "~> 1.21"
13
13
 
14
14
  gem "rubocop-performance", "~> 1.16"
15
+
16
+ gem "debug"
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2023, Ribose
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Makefile ADDED
@@ -0,0 +1 @@
1
+ .docker/Makefile
data/README.adoc CHANGED
@@ -110,3 +110,6 @@ Everyone interacting in the Oscal project's codebases, issue trackers, chat room
110
110
  == LICENSE
111
111
 
112
112
  Copyright Ribose. The OSCAL schema is published by NIST.
113
+
114
+ Published under the 2-clause BSD license.
115
+
data/Rakefile CHANGED
@@ -3,10 +3,17 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
5
 
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rubocop/rake_task"
6
+ # Note:
7
+ #
8
+ # There seems to be lots of issue with the current rubocop rules
9
+ # We are commenting this out for the moment, so instead of fixing
10
+ # those right away, we can focus on the new codes and then later
11
+ # come back to this and fix the issues.
12
+ #
13
+ # require "rubocop/rake_task"
14
+ # RuboCop::RakeTask.new
15
+ #
16
+ # task default: %i[spec rubocop]
9
17
 
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]
18
+ RSpec::Core::RakeTask.new(:spec)
19
+ task default: :spec
data/bin/console CHANGED
@@ -14,9 +14,9 @@ require "oscal"
14
14
  require "irb"
15
15
 
16
16
  def reload!(print = true)
17
- puts 'Reloading ...' if print
17
+ puts "Reloading ..." if print
18
18
  # Main project directory.
19
- root_dir = File.expand_path('..', __dir__)
19
+ root_dir = File.expand_path("..", __dir__)
20
20
  # Directories within the project that should be reloaded.
21
21
  reload_dirs = %w{lib}
22
22
  # Loop through and reload every file in all relevant project directories.
data/bin/rspec ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rspec-core", "rspec")
@@ -0,0 +1 @@
1
+ .docker/docker-compose.yml
data/lib/oscal/add.rb CHANGED
@@ -5,17 +5,18 @@ module Oscal
5
5
  KEY = %i(position by_id title params props links parts)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'params'
13
+ when "params"
13
14
  Parameter.wrap(val)
14
- when 'props'
15
+ when "props"
15
16
  Property.wrap(val)
16
- when 'links'
17
+ when "links"
17
18
  Link.wrap(val)
18
- when 'part'
19
+ when "part"
19
20
  Part.wrap(val)
20
21
  else
21
22
  val
data/lib/oscal/address.rb CHANGED
@@ -5,13 +5,14 @@ module Oscal
5
5
  KEY = %i(type addr_lines city state postal_code country)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'addr_lines'
13
+ when "addr_lines"
13
14
  AddressLine.wrap(val)
14
- when 'links'
15
+ when "links"
15
16
  Link.wrap(val)
16
17
  else
17
18
  val
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(val)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
data/lib/oscal/alter.rb CHANGED
@@ -5,13 +5,14 @@ module Oscal
5
5
  KEY = %i(control_id klass removes adds)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'removes'
13
+ when "removes"
13
14
  Remove.wrap(val)
14
- when 'adds'
15
+ when "adds"
15
16
  Add.wrap(val)
16
17
  else
17
18
  val
@@ -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