octool 0.0.1
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 +7 -0
- data/README.rdoc +3 -0
- data/bin/octool +93 -0
- data/lib/octool.rb +27 -0
- data/lib/octool/constants.rb +6 -0
- data/lib/octool/generated/certification.rb +35 -0
- data/lib/octool/generated/component.rb +57 -0
- data/lib/octool/generated/config.rb +55 -0
- data/lib/octool/generated/standard.rb +55 -0
- data/lib/octool/parser.rb +103 -0
- data/lib/octool/ssp.rb +47 -0
- data/lib/octool/system.rb +77 -0
- data/lib/octool/version.rb +3 -0
- data/octool.rdoc +52 -0
- data/schemas/v1.0.0/certification.yaml +27 -0
- data/schemas/v1.0.0/component.yaml +60 -0
- data/schemas/v1.0.0/config.yaml +64 -0
- data/schemas/v1.0.0/standard.yaml +50 -0
- data/templates/ssp.erb +120 -0
- metadata +179 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 776fce8b2089c05dcaf562c769ab2c65a81a3887977390a75e3fadda41c2ffe8
|
4
|
+
data.tar.gz: 107d9de9a54bac2b28bac82f26f10be04a28786bce24673b8ddd3cc6fe5afadd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1c864f87036e072690a9fe2645aa95eb0a50b3e51011bb23e3fc3c5e3cf4efd27b8b999098e2a1b2792af90aeaf35f88d79aaea3e2506cb970f89e167b196e23
|
7
|
+
data.tar.gz: 5e99234200a87b85a7b4de20da9196f3c72814f520bdc0329e8fc47b670b2064e2e5a2b950a11f76954c9becf29ef4d737eb01dde1b29eee49dada9fb329d674
|
data/README.rdoc
ADDED
data/bin/octool
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'gli'
|
3
|
+
require 'octool'
|
4
|
+
|
5
|
+
# Entrypoint and argument parser for the application.
|
6
|
+
class App
|
7
|
+
extend GLI::App
|
8
|
+
|
9
|
+
program_desc 'Open Compliance Tool'
|
10
|
+
version OCTool::VERSION
|
11
|
+
|
12
|
+
subcommand_option_handling :normal
|
13
|
+
arguments :strict
|
14
|
+
|
15
|
+
desc 'Check sanity of configuration'
|
16
|
+
command :validate do |v|
|
17
|
+
v.desc 'validate data'
|
18
|
+
v.arg_name 'path/to/system/config.yaml'
|
19
|
+
v.command :data do |vd|
|
20
|
+
vd.action do |global_options, options, args|
|
21
|
+
config_file = find_config(args)
|
22
|
+
OCTool::Parser.new(config_file).validate_data
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
v.desc 'validate schemas'
|
27
|
+
v.command :schemas do |vs|
|
28
|
+
vs.action do |global_options, options, args|
|
29
|
+
OCTool::Parser.validate_schemas
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
v.default_command :data
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'generate System Security Plan'
|
37
|
+
arg_name 'path/to/system/config.yaml'
|
38
|
+
command :ssp do |s|
|
39
|
+
s.desc 'where to store outputs'
|
40
|
+
s.default_value Dir.tmpdir
|
41
|
+
s.long_desc 'Default output directory respects env vars TMPDIR, TMP, TEMP'
|
42
|
+
s.arg_name 'path/to/output/dir'
|
43
|
+
s.flag [:d, :dir]
|
44
|
+
|
45
|
+
s.action do |global_options, options, args|
|
46
|
+
export_dir = options[:dir]
|
47
|
+
FileUtils.mkdir_p export_dir unless File.directory?(export_dir)
|
48
|
+
config_file = find_config(args)
|
49
|
+
system = OCTool::Parser.new(config_file).load_system
|
50
|
+
Dir.chdir File.dirname(config_file) do
|
51
|
+
OCTool::SSP.new(system, export_dir)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
pre do |global, command, options, args|
|
57
|
+
# Pre logic here
|
58
|
+
#
|
59
|
+
# Return true to proceed;
|
60
|
+
# false to abort and not call the chosen command
|
61
|
+
#
|
62
|
+
# Use skips_pre before a command
|
63
|
+
# to skip this block on that command only.
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
post do |global, command, options, args|
|
68
|
+
# Post logic here
|
69
|
+
# Use skips_post before a command
|
70
|
+
# to skip this block on that command only.
|
71
|
+
puts 'OK'
|
72
|
+
end
|
73
|
+
|
74
|
+
on_error do |exception|
|
75
|
+
# Error logic here
|
76
|
+
# Return false to skip default error handling.
|
77
|
+
if ENV['DEBUG']
|
78
|
+
puts exception.backtrace
|
79
|
+
pp exception
|
80
|
+
false
|
81
|
+
else
|
82
|
+
true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_config(args)
|
88
|
+
path = args.first || OCTool::DEFAULT_CONFIG_FILENAME
|
89
|
+
path = File.join(path, OCTool::DEFAULT_CONFIG_FILENAME) if File.directory?(path)
|
90
|
+
path
|
91
|
+
end
|
92
|
+
|
93
|
+
exit App.run(ARGV)
|
data/lib/octool.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'octool/version.rb'
|
2
|
+
|
3
|
+
# Built-ins.
|
4
|
+
require 'pp'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
# 3rd-party libs.
|
8
|
+
require 'kwalify'
|
9
|
+
require 'kwalify/util/hashlike'
|
10
|
+
require 'paru/pandoc'
|
11
|
+
|
12
|
+
# OCTool libs.
|
13
|
+
require 'octool/constants'
|
14
|
+
require 'octool/parser'
|
15
|
+
require 'octool/ssp'
|
16
|
+
require 'octool/system'
|
17
|
+
|
18
|
+
# Generated libs.
|
19
|
+
require 'octool/generated/certification'
|
20
|
+
require 'octool/generated/component'
|
21
|
+
require 'octool/generated/config'
|
22
|
+
require 'octool/generated/standard'
|
23
|
+
|
24
|
+
# Mixins.
|
25
|
+
module OCTool
|
26
|
+
include Kwalify::Util::HashLike # defines [], []=, and keys?
|
27
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
module OCTool
|
2
|
+
LATEST_SCHEMA_VERSION = 'v1.0.0'.freeze
|
3
|
+
BASE_SCHEMA_DIR = File.join(File.dirname(__FILE__), '..', '..', 'schemas').freeze
|
4
|
+
ERB_DIR = File.join(File.dirname(__FILE__), '..', '..', 'templates').freeze
|
5
|
+
DEFAULT_CONFIG_FILENAME = 'config.yaml'.freeze
|
6
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'kwalify/util/hashlike'
|
2
|
+
|
3
|
+
module OCTool
|
4
|
+
|
5
|
+
|
6
|
+
class Certification
|
7
|
+
include Kwalify::Util::HashLike
|
8
|
+
def initialize(hash=nil)
|
9
|
+
if hash.nil?
|
10
|
+
return
|
11
|
+
end
|
12
|
+
@certification_key = hash['certification_key']
|
13
|
+
@name = hash['name']
|
14
|
+
@requires = (v=hash['requires']) ? v.map!{|e| e.is_a?(ControlID) ? e : ControlID.new(e)} : v
|
15
|
+
end
|
16
|
+
attr_accessor :certification_key # str
|
17
|
+
attr_accessor :name # str
|
18
|
+
attr_accessor :requires # seq
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class ControlID
|
23
|
+
include Kwalify::Util::HashLike
|
24
|
+
def initialize(hash=nil)
|
25
|
+
if hash.nil?
|
26
|
+
return
|
27
|
+
end
|
28
|
+
@standard_key = hash['standard_key']
|
29
|
+
@control_key = hash['control_key']
|
30
|
+
end
|
31
|
+
attr_accessor :standard_key # str
|
32
|
+
attr_accessor :control_key # str
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'kwalify/util/hashlike'
|
2
|
+
|
3
|
+
module OCTool
|
4
|
+
|
5
|
+
|
6
|
+
class Component
|
7
|
+
include Kwalify::Util::HashLike
|
8
|
+
def initialize(hash=nil)
|
9
|
+
if hash.nil?
|
10
|
+
return
|
11
|
+
end
|
12
|
+
@name = hash['name']
|
13
|
+
@component_key = hash['component_key']
|
14
|
+
@description = hash['description']
|
15
|
+
@attestations = (v=hash['attestations']) ? v.map!{|e| e.is_a?(Attestation) ? e : Attestation.new(e)} : v
|
16
|
+
end
|
17
|
+
attr_accessor :name # str
|
18
|
+
attr_accessor :component_key # str
|
19
|
+
attr_accessor :description # str
|
20
|
+
attr_accessor :attestations # seq
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class Attestation
|
25
|
+
include Kwalify::Util::HashLike
|
26
|
+
def initialize(hash=nil)
|
27
|
+
if hash.nil?
|
28
|
+
return
|
29
|
+
end
|
30
|
+
@summary = hash['summary']
|
31
|
+
@status = hash['status']
|
32
|
+
@date_verified = hash['date_verified']
|
33
|
+
@satisfies = (v=hash['satisfies']) ? v.map!{|e| e.is_a?(ControlID) ? e : ControlID.new(e)} : v
|
34
|
+
@narrative = hash['narrative']
|
35
|
+
end
|
36
|
+
attr_accessor :summary # str
|
37
|
+
attr_accessor :status # str
|
38
|
+
attr_accessor :date_verified # date
|
39
|
+
attr_accessor :satisfies # seq
|
40
|
+
attr_accessor :narrative # str
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
class ControlID
|
45
|
+
include Kwalify::Util::HashLike
|
46
|
+
def initialize(hash=nil)
|
47
|
+
if hash.nil?
|
48
|
+
return
|
49
|
+
end
|
50
|
+
@standard_key = hash['standard_key']
|
51
|
+
@control_key = hash['control_key']
|
52
|
+
end
|
53
|
+
attr_accessor :standard_key # text
|
54
|
+
attr_accessor :control_key # text
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'kwalify/util/hashlike'
|
2
|
+
|
3
|
+
module OCTool
|
4
|
+
|
5
|
+
|
6
|
+
class Config
|
7
|
+
include Kwalify::Util::HashLike
|
8
|
+
def initialize(hash=nil)
|
9
|
+
if hash.nil?
|
10
|
+
return
|
11
|
+
end
|
12
|
+
@schema_version = hash['schema_version']
|
13
|
+
@name = hash['name']
|
14
|
+
@overview = hash['overview']
|
15
|
+
@maintainers = hash['maintainers']
|
16
|
+
@metadata = (v=hash['metadata']) && v.is_a?(Hash) ? Metadata.new(v) : v
|
17
|
+
@includes = (v=hash['includes']) ? v.map!{|e| e.is_a?(Include) ? e : Include.new(e)} : v
|
18
|
+
end
|
19
|
+
attr_accessor :schema_version # str
|
20
|
+
attr_accessor :name # str
|
21
|
+
attr_accessor :overview # str
|
22
|
+
attr_accessor :maintainers # seq
|
23
|
+
attr_accessor :metadata # map
|
24
|
+
attr_accessor :includes # seq
|
25
|
+
end
|
26
|
+
|
27
|
+
## Optional metadata.
|
28
|
+
class Metadata
|
29
|
+
include Kwalify::Util::HashLike
|
30
|
+
def initialize(hash=nil)
|
31
|
+
if hash.nil?
|
32
|
+
return
|
33
|
+
end
|
34
|
+
@abstract = hash['abstract']
|
35
|
+
@description = hash['description']
|
36
|
+
end
|
37
|
+
attr_accessor :abstract # str
|
38
|
+
attr_accessor :description # str
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
class Include
|
43
|
+
include Kwalify::Util::HashLike
|
44
|
+
def initialize(hash=nil)
|
45
|
+
if hash.nil?
|
46
|
+
return
|
47
|
+
end
|
48
|
+
@type = hash['type']
|
49
|
+
@path = hash['path']
|
50
|
+
end
|
51
|
+
attr_accessor :type # str
|
52
|
+
attr_accessor :path # str
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'kwalify/util/hashlike'
|
2
|
+
|
3
|
+
module OCTool
|
4
|
+
|
5
|
+
|
6
|
+
class Standard
|
7
|
+
include Kwalify::Util::HashLike
|
8
|
+
def initialize(hash=nil)
|
9
|
+
if hash.nil?
|
10
|
+
return
|
11
|
+
end
|
12
|
+
@name = hash['name']
|
13
|
+
@standard_key = hash['standard_key']
|
14
|
+
@families = (v=hash['families']) ? v.map!{|e| e.is_a?(ControlFamily) ? e : ControlFamily.new(e)} : v
|
15
|
+
@controls = (v=hash['controls']) ? v.map!{|e| e.is_a?(Control) ? e : Control.new(e)} : v
|
16
|
+
end
|
17
|
+
attr_accessor :name # str
|
18
|
+
attr_accessor :standard_key # str
|
19
|
+
attr_accessor :families # seq
|
20
|
+
attr_accessor :controls # seq
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class ControlFamily
|
25
|
+
include Kwalify::Util::HashLike
|
26
|
+
def initialize(hash=nil)
|
27
|
+
if hash.nil?
|
28
|
+
return
|
29
|
+
end
|
30
|
+
@family_key = hash['family_key']
|
31
|
+
@name = hash['name']
|
32
|
+
end
|
33
|
+
attr_accessor :family_key # str
|
34
|
+
attr_accessor :name # str
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class Control
|
39
|
+
include Kwalify::Util::HashLike
|
40
|
+
def initialize(hash=nil)
|
41
|
+
if hash.nil?
|
42
|
+
return
|
43
|
+
end
|
44
|
+
@control_key = hash['control_key']
|
45
|
+
@family_key = hash['family_key']
|
46
|
+
@name = hash['name']
|
47
|
+
@description = hash['description']
|
48
|
+
end
|
49
|
+
attr_accessor :control_key # str
|
50
|
+
attr_accessor :family_key # str
|
51
|
+
attr_accessor :name # str
|
52
|
+
attr_accessor :description # str
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module OCTool
|
2
|
+
# Custom error to show validation errors.
|
3
|
+
class ValidationError < StandardError
|
4
|
+
attr_reader :errors
|
5
|
+
def initialize(path, errors)
|
6
|
+
@path = path
|
7
|
+
@errors = errors
|
8
|
+
end
|
9
|
+
|
10
|
+
def message
|
11
|
+
msg = ["[ERROR] #{@path}"]
|
12
|
+
@errors.each do |e|
|
13
|
+
msg << "line #{e.linenum} col #{e.column} [#{e.path}] #{e.message}"
|
14
|
+
end
|
15
|
+
msg.join("\n")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Logic to wrap the kwalify parser.
|
20
|
+
class Parser
|
21
|
+
def initialize(path)
|
22
|
+
@config_file = path
|
23
|
+
die "#{File.expand_path(path)} not readable" unless File.readable?(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Class method: check that schemas are valid.
|
27
|
+
def self.validate_schemas
|
28
|
+
metavalidator = Kwalify::MetaValidator.instance
|
29
|
+
kwalify = Kwalify::Yaml::Parser.new(metavalidator)
|
30
|
+
Dir.glob("#{BASE_SCHEMA_DIR}/**/*.yaml").each do |schema|
|
31
|
+
kwalify.parse_file(schema)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def die(message = nil)
|
36
|
+
puts '[FAIL] ' + message if message
|
37
|
+
exit(1)
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_file(path, type)
|
41
|
+
parser = kwalify_parser(type)
|
42
|
+
data = parser.parse_file(path)
|
43
|
+
errors = parser.errors
|
44
|
+
raise ValidationError.new(path, errors) unless errors.empty?
|
45
|
+
|
46
|
+
data
|
47
|
+
rescue SystemCallError, Kwalify::SyntaxError, ValidationError => e
|
48
|
+
die e.message
|
49
|
+
end
|
50
|
+
|
51
|
+
def kwalify_parser(type)
|
52
|
+
schema_file = File.join(schema_dir, "#{type}.yaml")
|
53
|
+
schema = Kwalify::Yaml.load_file(schema_file)
|
54
|
+
validator = Kwalify::Validator.new(schema)
|
55
|
+
Kwalify::Yaml::Parser.new(validator) { |p| p.data_binding = true }
|
56
|
+
end
|
57
|
+
|
58
|
+
def schema_dir
|
59
|
+
@schema_dir ||= begin
|
60
|
+
File.join(BASE_SCHEMA_DIR, schema_version).freeze
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def schema_version
|
65
|
+
@schema_version ||= Kwalify::Yaml.load_file(@config_file)['schema_version']
|
66
|
+
rescue StandardError
|
67
|
+
warn "Setting schema_version to #{LATEST_SCHEMA_VERSION}"
|
68
|
+
LATEST_SCHEMA_VERSION
|
69
|
+
end
|
70
|
+
|
71
|
+
# Check that all data files are valid.
|
72
|
+
def validate_data
|
73
|
+
base_dir = File.dirname(@config_file)
|
74
|
+
config = validate_file(@config_file, 'config')
|
75
|
+
config['includes'].each do |inc|
|
76
|
+
path = File.join(base_dir, inc['path'])
|
77
|
+
type = inc['type']
|
78
|
+
validate_file(path, type)
|
79
|
+
end
|
80
|
+
config
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_system
|
84
|
+
base_dir = File.dirname(@config_file)
|
85
|
+
config = load_file(@config_file, 'config')
|
86
|
+
system = System.new(config)
|
87
|
+
config.includes.each do |inc|
|
88
|
+
path = File.join(base_dir, inc.path)
|
89
|
+
system.data << load_file(path, inc.type)
|
90
|
+
end
|
91
|
+
system
|
92
|
+
end
|
93
|
+
|
94
|
+
def load_file(path, type)
|
95
|
+
die "#{File.expand_path(path)} not readable" unless File.readable?(path)
|
96
|
+
klass = Object.const_get("OCTool::#{type.capitalize}")
|
97
|
+
ydoc = Kwalify::Yaml.load_file(path)
|
98
|
+
klass.new(ydoc)
|
99
|
+
rescue SystemCallError, Kwalify::SyntaxError => e
|
100
|
+
die e.message
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/octool/ssp.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module OCTool
|
4
|
+
# Build DB, CSV, and markdown.
|
5
|
+
class SSP
|
6
|
+
def initialize(system, output_dir)
|
7
|
+
@system = system
|
8
|
+
@output_dir = output_dir
|
9
|
+
render_template
|
10
|
+
write
|
11
|
+
end
|
12
|
+
|
13
|
+
def render_template
|
14
|
+
print "Building markdown #{md_path} ... "
|
15
|
+
template_path = File.join(ERB_DIR, 'ssp.erb')
|
16
|
+
template = File.read(template_path)
|
17
|
+
output = ERB.new(template, nil, '-').result(binding)
|
18
|
+
File.open(md_path, 'w') { |f| f.puts output }
|
19
|
+
puts 'done'
|
20
|
+
end
|
21
|
+
|
22
|
+
def write
|
23
|
+
print "Building #{pdf_path} ... "
|
24
|
+
pandoc = Paru::Pandoc.new
|
25
|
+
converter = pandoc.configure do
|
26
|
+
from 'markdown'
|
27
|
+
to 'pdf'
|
28
|
+
pdf_engine 'lualatex'
|
29
|
+
toc
|
30
|
+
toc_depth 3
|
31
|
+
number_sections
|
32
|
+
highlight_style 'pygments'
|
33
|
+
end
|
34
|
+
output = converter << File.read(md_path)
|
35
|
+
File.new(pdf_path, 'wb').write(output)
|
36
|
+
puts 'done'
|
37
|
+
end
|
38
|
+
|
39
|
+
def md_path
|
40
|
+
@md_path ||= File.join(@output_dir, 'ssp.md')
|
41
|
+
end
|
42
|
+
|
43
|
+
def pdf_path
|
44
|
+
@pdf_path ||= File.join(@output_dir, 'ssp.pdf')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module OCTool
|
2
|
+
# Representation of a system
|
3
|
+
class System
|
4
|
+
attr_accessor :config
|
5
|
+
attr_accessor :data
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
@data = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def certifications
|
13
|
+
@certifications ||= @data.select { |e| e.is_a?(OCTool::Certification) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def components
|
17
|
+
@components ||= @data.select { |e| e.is_a?(OCTool::Component) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def standards
|
21
|
+
@standards ||= @data.select { |e| e.is_a?(OCTool::Standard) }
|
22
|
+
end
|
23
|
+
|
24
|
+
# List of all attestations claimed by components in the system.
|
25
|
+
def attestations
|
26
|
+
@attestations ||= begin
|
27
|
+
@attestations = []
|
28
|
+
components.each do |c|
|
29
|
+
# Add a "component_key" field to each attestation.
|
30
|
+
c.attestations.map! { |e| e['component_key'] = c.component_key; e }
|
31
|
+
@attestations << c.attestations
|
32
|
+
end
|
33
|
+
@attestations.flatten!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# List of all coverages.
|
38
|
+
def satisfies
|
39
|
+
@satisfies ||= begin
|
40
|
+
@satisfies = []
|
41
|
+
attestations.each do |a|
|
42
|
+
# Add an "attestation_key" field to each cover.
|
43
|
+
a.satisfies.map! { |e| e['component_key'] = a.commponent_key; e }
|
44
|
+
a.satisfies.map! { |e| e['attestation_key'] = a.attestation_summary; e }
|
45
|
+
@satisfies << a.satisfies
|
46
|
+
end
|
47
|
+
@satisfies.flatten!
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# List of all controls defined by standards in the system.
|
52
|
+
def controls
|
53
|
+
@controls || begin
|
54
|
+
@controls = []
|
55
|
+
standards.each do |s|
|
56
|
+
# Add a "standard_key" field to each control.
|
57
|
+
s.controls.map! { |e| e['standard_key'] = s.standard_key; e }
|
58
|
+
@controls << s.controls
|
59
|
+
end
|
60
|
+
@controls.flatten!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# List of all families defined by standards in the system.
|
65
|
+
def families
|
66
|
+
@families || begin
|
67
|
+
@families = []
|
68
|
+
standards.each do |s|
|
69
|
+
# Add a "standard_key" field to each family.
|
70
|
+
s.families.map! { |e| e['standard_key'] = s.standard_key; e }
|
71
|
+
@families << s.families
|
72
|
+
end
|
73
|
+
@families.flatten!
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/octool.rdoc
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
== octool - Open Compliance Tool
|
2
|
+
|
3
|
+
v0.0.1
|
4
|
+
|
5
|
+
=== Global Options
|
6
|
+
=== --help
|
7
|
+
Show this message
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
=== --version
|
12
|
+
Display the program version
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
=== Commands
|
17
|
+
==== Command: <tt>help command</tt>
|
18
|
+
Shows a list of commands or help for one command
|
19
|
+
|
20
|
+
Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
|
21
|
+
===== Options
|
22
|
+
===== -c
|
23
|
+
List commands one per line, to assist with shell completion
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
==== Command: <tt>ssp path/to/system/config.yaml</tt>
|
28
|
+
generate System Security Plan
|
29
|
+
|
30
|
+
|
31
|
+
===== Options
|
32
|
+
===== -d|--dir path/to/output/dir
|
33
|
+
|
34
|
+
where to store outputs
|
35
|
+
|
36
|
+
[Default Value] /tmp
|
37
|
+
Default output directory respects env vars TMPDIR, TMP, TEMP
|
38
|
+
|
39
|
+
==== Command: <tt>validate </tt>
|
40
|
+
Check sanity of configuration
|
41
|
+
|
42
|
+
|
43
|
+
===== Commands
|
44
|
+
====== Command: <tt>data path/to/system/config.yaml</tt>
|
45
|
+
validate data
|
46
|
+
|
47
|
+
|
48
|
+
====== Command: <tt>schemas </tt>
|
49
|
+
validate schemas
|
50
|
+
|
51
|
+
|
52
|
+
[Default Command] data
|
@@ -0,0 +1,27 @@
|
|
1
|
+
---
|
2
|
+
type: map
|
3
|
+
class: Certification
|
4
|
+
mapping:
|
5
|
+
certification_key:
|
6
|
+
desc: A short, unique identifier for this certification.
|
7
|
+
required: true
|
8
|
+
type: str
|
9
|
+
unique: true
|
10
|
+
name:
|
11
|
+
desc: A human-friendly name for the certification.
|
12
|
+
required: true
|
13
|
+
type: str
|
14
|
+
requires:
|
15
|
+
desc: List of control IDs required by the certification.
|
16
|
+
required: true
|
17
|
+
type: seq
|
18
|
+
sequence:
|
19
|
+
- type: map
|
20
|
+
class: ControlID
|
21
|
+
mapping:
|
22
|
+
standard_key:
|
23
|
+
required: true
|
24
|
+
type: str
|
25
|
+
control_key:
|
26
|
+
required: true
|
27
|
+
type: str
|
@@ -0,0 +1,60 @@
|
|
1
|
+
---
|
2
|
+
type: map
|
3
|
+
class: Component
|
4
|
+
mapping:
|
5
|
+
name:
|
6
|
+
desc: Human-friendly name to appear in the SSP.
|
7
|
+
type: str
|
8
|
+
required: true
|
9
|
+
component_key:
|
10
|
+
desc: Unique identifier for referential integrity.
|
11
|
+
type: str
|
12
|
+
required: true
|
13
|
+
description:
|
14
|
+
desc: A paragraph or two that describes the component.
|
15
|
+
type: str
|
16
|
+
required: true
|
17
|
+
attestations:
|
18
|
+
desc: List of attestations.
|
19
|
+
type: seq
|
20
|
+
sequence:
|
21
|
+
- type: map
|
22
|
+
class: Attestation
|
23
|
+
mapping:
|
24
|
+
summary:
|
25
|
+
desc: Arbitrary verbiage to appear in SSP as a TLDR.
|
26
|
+
type: str
|
27
|
+
required: true
|
28
|
+
status:
|
29
|
+
desc: To what extent is this attestation "done"?
|
30
|
+
type: str
|
31
|
+
required: true
|
32
|
+
enum:
|
33
|
+
- partial
|
34
|
+
- complete
|
35
|
+
- planned
|
36
|
+
- none
|
37
|
+
date_verified:
|
38
|
+
desc: When was this last verified?
|
39
|
+
type: date
|
40
|
+
required: false
|
41
|
+
satisfies:
|
42
|
+
desc: List of control IDs covered by this attestation.
|
43
|
+
type: seq
|
44
|
+
required: false
|
45
|
+
sequence:
|
46
|
+
- type: map
|
47
|
+
class: ControlID
|
48
|
+
mapping:
|
49
|
+
standard_key:
|
50
|
+
type: text
|
51
|
+
required: true
|
52
|
+
control_key:
|
53
|
+
type: text
|
54
|
+
required: true
|
55
|
+
narrative:
|
56
|
+
desc: |
|
57
|
+
Explain how attestation satisfies the indicated controls.
|
58
|
+
The content should be in markdown format.
|
59
|
+
type: str
|
60
|
+
required: true
|
@@ -0,0 +1,64 @@
|
|
1
|
+
---
|
2
|
+
type: map
|
3
|
+
class: Config
|
4
|
+
mapping:
|
5
|
+
schema_version:
|
6
|
+
desc: |
|
7
|
+
Must match one of the schema directories in the octool source.
|
8
|
+
required: true
|
9
|
+
type: str
|
10
|
+
|
11
|
+
name:
|
12
|
+
desc: Human-friendly to appear in the SSP.
|
13
|
+
required: true
|
14
|
+
type: str
|
15
|
+
|
16
|
+
overview:
|
17
|
+
desc: Human-friendly description to appear in the SSP.
|
18
|
+
required: true
|
19
|
+
type: str
|
20
|
+
|
21
|
+
maintainers:
|
22
|
+
desc: Who should somebody contact for questions about this SSP?
|
23
|
+
required: true
|
24
|
+
type: seq
|
25
|
+
sequence:
|
26
|
+
- type: str
|
27
|
+
|
28
|
+
metadata:
|
29
|
+
desc: Optional metadata.
|
30
|
+
required: false
|
31
|
+
type: map
|
32
|
+
class: Metadata
|
33
|
+
mapping:
|
34
|
+
abstract:
|
35
|
+
desc: Abstract appears in document metadata.
|
36
|
+
required: false
|
37
|
+
type: str
|
38
|
+
description:
|
39
|
+
desc: Description appears in document metadata.
|
40
|
+
required: false
|
41
|
+
type: str
|
42
|
+
'=':
|
43
|
+
desc: Arbitrary key:value pair of strings.
|
44
|
+
type: str
|
45
|
+
|
46
|
+
includes:
|
47
|
+
desc: Additional files to include from the system repo.
|
48
|
+
required: true
|
49
|
+
type: seq
|
50
|
+
sequence:
|
51
|
+
- type: map
|
52
|
+
class: Include
|
53
|
+
mapping:
|
54
|
+
type:
|
55
|
+
required: true
|
56
|
+
type: str
|
57
|
+
enum:
|
58
|
+
- certification
|
59
|
+
- component
|
60
|
+
- standard
|
61
|
+
path:
|
62
|
+
desc: Path must be relative within the repo.
|
63
|
+
required: true
|
64
|
+
type: str
|
@@ -0,0 +1,50 @@
|
|
1
|
+
---
|
2
|
+
type: map
|
3
|
+
class: Standard
|
4
|
+
mapping:
|
5
|
+
name:
|
6
|
+
desc: Human-friendly name to appear in SSP.
|
7
|
+
type: str
|
8
|
+
required: true
|
9
|
+
|
10
|
+
standard_key:
|
11
|
+
desc: Unique ID to use within YAML files.
|
12
|
+
type: str
|
13
|
+
required: true
|
14
|
+
|
15
|
+
families:
|
16
|
+
desc: Optional list of control families.
|
17
|
+
type: seq
|
18
|
+
required: false
|
19
|
+
sequence:
|
20
|
+
- type: map
|
21
|
+
class: ControlFamily
|
22
|
+
mapping:
|
23
|
+
family_key:
|
24
|
+
desc: Unique ID of the family
|
25
|
+
type: str
|
26
|
+
unique: true
|
27
|
+
name:
|
28
|
+
desc: Human-friendly name of the family
|
29
|
+
type: str
|
30
|
+
controls:
|
31
|
+
desc: Mandatory list of controls defined by the standard.
|
32
|
+
required: true
|
33
|
+
type: seq
|
34
|
+
sequence:
|
35
|
+
- type: map
|
36
|
+
class: Control
|
37
|
+
mapping:
|
38
|
+
control_key:
|
39
|
+
type: str
|
40
|
+
unique: true
|
41
|
+
required: true
|
42
|
+
family_key:
|
43
|
+
type: str
|
44
|
+
required: false
|
45
|
+
name:
|
46
|
+
type: str
|
47
|
+
required: true
|
48
|
+
description:
|
49
|
+
type: str
|
50
|
+
required: true
|
data/templates/ssp.erb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
---
|
2
|
+
title: "<%= @system.config.name -%>"
|
3
|
+
subtitle: "System Security Plan"
|
4
|
+
|
5
|
+
author:
|
6
|
+
<% @system.config.maintainers.each do |maintainer| %>
|
7
|
+
- <%= maintainer -%>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
absract: |
|
11
|
+
<%= @system.config.metadata.abstract rescue 'None' %>
|
12
|
+
|
13
|
+
description: |
|
14
|
+
<%= @system.config.metadata.description rescue 'None' %>
|
15
|
+
|
16
|
+
fontsize: 11pt
|
17
|
+
mainfont: NotoSans
|
18
|
+
#monofont: NotoSansMono-ExtraCondensedLight
|
19
|
+
monofont: NotoSansMono-ExtraCondensed
|
20
|
+
mainfontoptions:
|
21
|
+
- Numbers=Lowercase
|
22
|
+
- Numbers=Proportional
|
23
|
+
- UprightFont=*
|
24
|
+
- ItalicFont=*-Italic
|
25
|
+
- BoldFont=*-Bold
|
26
|
+
- BoldItalicFont=*-BoldItalic
|
27
|
+
|
28
|
+
lof: false
|
29
|
+
lot: false
|
30
|
+
colorlinks: true
|
31
|
+
linkcolor: blue
|
32
|
+
urlcolor: blue
|
33
|
+
|
34
|
+
documentclass: report
|
35
|
+
classoption:
|
36
|
+
- onecolumn
|
37
|
+
- oneside
|
38
|
+
- portrait
|
39
|
+
|
40
|
+
pagestyle: headings
|
41
|
+
papersize: letter
|
42
|
+
geometry:
|
43
|
+
- top=2cm
|
44
|
+
- left=2cm
|
45
|
+
- right=2cm
|
46
|
+
- bottom=2cm
|
47
|
+
---
|
48
|
+
|
49
|
+
# <%= @system.config.name %>
|
50
|
+
|
51
|
+
## Overview
|
52
|
+
|
53
|
+
<%= @system.config.overview %>
|
54
|
+
|
55
|
+
## Standards
|
56
|
+
|
57
|
+
This System Security Plan (SSP) addresses these standards:
|
58
|
+
|
59
|
+
<% @system.standards.each do |s| -%>
|
60
|
+
- <%= s.name %>
|
61
|
+
<% end %>
|
62
|
+
|
63
|
+
The full copy of each standard is included in the appendix.
|
64
|
+
|
65
|
+
|
66
|
+
## Components
|
67
|
+
|
68
|
+
<% @system.components.each do |c| %>
|
69
|
+
### <%= c.name %>
|
70
|
+
|
71
|
+
<%= c.description %>
|
72
|
+
|
73
|
+
<% if c.attestations.empty? %>
|
74
|
+
_The organization has not yet documented attestations for this component_.
|
75
|
+
<% else %>
|
76
|
+
The organization offers the following attestations for this component.
|
77
|
+
<% end %>
|
78
|
+
|
79
|
+
<% c.attestations.each do |a| %>
|
80
|
+
#### <%= a.summary %>
|
81
|
+
|
82
|
+
Status: <%= a.status %>
|
83
|
+
|
84
|
+
Date verified: <%= a.date_verified if a.date_verified %>
|
85
|
+
|
86
|
+
Satisfies:
|
87
|
+
|
88
|
+
<% a.satisfies.each do |cid| -%>
|
89
|
+
- <%= cid.standard_key %> control <%= cid.control_key %>
|
90
|
+
<% end -%>
|
91
|
+
|
92
|
+
<%= a.narrative %>
|
93
|
+
|
94
|
+
<% end %>
|
95
|
+
<% end %>
|
96
|
+
|
97
|
+
|
98
|
+
# Appendix: Standards
|
99
|
+
|
100
|
+
<% @system.standards.each do |s| %>
|
101
|
+
## <%=s.name %>
|
102
|
+
|
103
|
+
<% if s.families and !s.families.empty? %>
|
104
|
+
### Families
|
105
|
+
|
106
|
+
<% s.families.each do |family| %>
|
107
|
+
- <%= family.family_key -%>: <%= family.name %>
|
108
|
+
<% end %>
|
109
|
+
|
110
|
+
<% end %>
|
111
|
+
|
112
|
+
### Controls
|
113
|
+
|
114
|
+
<% s.controls.each do |c| %>
|
115
|
+
#### Control <%= c.control_key -%>: <%= c.name %>
|
116
|
+
|
117
|
+
<%= c.description %>
|
118
|
+
|
119
|
+
<% end %>
|
120
|
+
<% end %>
|
metadata
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: octool
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Morgan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '13.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '13.1'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '13.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '13.1'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rdoc
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '6.2'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '6.3'
|
43
|
+
type: :development
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '6.2'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '6.3'
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: daru
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - '='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 0.2.2
|
60
|
+
type: :runtime
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - '='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 0.2.2
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: gli
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - '='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 2.19.0
|
74
|
+
type: :runtime
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 2.19.0
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: kwalify
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - '='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: 0.7.2
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 0.7.2
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: pandoc-ruby
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - '='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.1.4
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - '='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 2.1.4
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: paru
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - '='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: 0.4.0.1
|
116
|
+
type: :runtime
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - '='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 0.4.0.1
|
123
|
+
description:
|
124
|
+
email: jumanjiman@gmail.com
|
125
|
+
executables:
|
126
|
+
- octool
|
127
|
+
extensions: []
|
128
|
+
extra_rdoc_files:
|
129
|
+
- README.rdoc
|
130
|
+
- octool.rdoc
|
131
|
+
files:
|
132
|
+
- README.rdoc
|
133
|
+
- bin/octool
|
134
|
+
- lib/octool.rb
|
135
|
+
- lib/octool/constants.rb
|
136
|
+
- lib/octool/generated/certification.rb
|
137
|
+
- lib/octool/generated/component.rb
|
138
|
+
- lib/octool/generated/config.rb
|
139
|
+
- lib/octool/generated/standard.rb
|
140
|
+
- lib/octool/parser.rb
|
141
|
+
- lib/octool/ssp.rb
|
142
|
+
- lib/octool/system.rb
|
143
|
+
- lib/octool/version.rb
|
144
|
+
- octool.rdoc
|
145
|
+
- schemas/v1.0.0/certification.yaml
|
146
|
+
- schemas/v1.0.0/component.yaml
|
147
|
+
- schemas/v1.0.0/config.yaml
|
148
|
+
- schemas/v1.0.0/standard.yaml
|
149
|
+
- templates/ssp.erb
|
150
|
+
homepage: https://github.com/jumanjiman/octool
|
151
|
+
licenses:
|
152
|
+
- GPL-3.0
|
153
|
+
metadata: {}
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options:
|
156
|
+
- "--title"
|
157
|
+
- octool
|
158
|
+
- "--main"
|
159
|
+
- README.rdoc
|
160
|
+
- "-ri"
|
161
|
+
require_paths:
|
162
|
+
- lib
|
163
|
+
- lib
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubygems_version: 3.1.2
|
176
|
+
signing_key:
|
177
|
+
specification_version: 4
|
178
|
+
summary: Open Compliance Toolkit
|
179
|
+
test_files: []
|