oscal 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) 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 +80 -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 +8 -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/sample_inputs/import-ap.json +4 -0
  82. metadata +21 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3d2839d5707adc52e720080828918e04373d2399cbd138edd403055b1712cc0
4
- data.tar.gz: 0fc05ead23c4745d188f9bf359c2594463867fd1cce5256a271cf93befef287e
3
+ metadata.gz: 562c827a391c4d86af9edb5cdf98ee1a61fb3a7d57374aa29a3a70609df09bb3
4
+ data.tar.gz: 2031cf36b122b094cb3532bff51534de756907327d7f3bb09456a9120a510607
5
5
  SHA512:
6
- metadata.gz: 91233436e998fe98ae7d84873ac7d8b68f9fc84ec6f7a7775533ee45922beaeb507c031ed88da4fe0ed71f34e76064861e6e3b0cb340acac23d25b3134cc323f
7
- data.tar.gz: 957a56d62c3c43b5d22f4659b993acbd6e85ad344fe042fda702a3e07dda5fed576caf6ad0b742eb783597ac0e1165020af957a8d6c9699b0aad977910a910aa
6
+ metadata.gz: cbd11774c99f75b1e8b365488fb3fdd4969a7917de692cbf7546118b2a3102767af8b9ed9a140c2998a0d4cff9f282061eeea03da47371b864d67067307433c3
7
+ data.tar.gz: d7510b9543be93b734be45f0d4140d32413af2d8a5462339bac65db9de3ff9a98c3b696537af73a9f23c1f491e85b269e898008df07da5180b2036e52fdff48a
@@ -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