octool 0.0.4 → 0.0.9
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/bin/octool +22 -5
- data/lib/octool.rb +2 -6
- data/lib/octool/constants.rb +1 -1
- data/lib/octool/parser.rb +42 -24
- data/lib/octool/ssp.rb +21 -4
- data/lib/octool/system.rb +45 -39
- data/lib/octool/version.rb +1 -1
- data/octool.rdoc +14 -2
- data/schemas/v1.0.1/certification.yaml +27 -0
- data/schemas/v1.0.1/component.yaml +60 -0
- data/schemas/v1.0.1/config.yaml +79 -0
- data/schemas/v1.0.1/standard.yaml +50 -0
- data/schemas/v1.0.2/certification.yaml +27 -0
- data/schemas/v1.0.2/component.yaml +60 -0
- data/schemas/v1.0.2/config.yaml +111 -0
- data/schemas/v1.0.2/standard.yaml +50 -0
- data/templates/ssp.erb +123 -35
- metadata +24 -6
- data/lib/octool/generated/certification.rb +0 -35
- data/lib/octool/generated/component.rb +0 -57
- data/lib/octool/generated/config.rb +0 -55
- data/lib/octool/generated/standard.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f2733d9e7a9f5a736a283139540cdae45cf887c2bd54e0de2461a30bc4602da
|
4
|
+
data.tar.gz: 87854e857038bafc19bdcbb34e23fe8a58f9c0d9e7836d3297d50b3cbe032367
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adce4065a12f2de271ce3c5c3faf2ffb28b6bf5c652b95e6c62826fa327b8a08ef2b8b7ee85bf7674a116ff2ea85cc604e124f83107adff5bac53374992c18fa
|
7
|
+
data.tar.gz: ed4f5e6de7c5463ae7a287bd3cf3189a393e6d458bd13813dfce82eab3c120031bbab94608b185bf2f5731664f9c32aeca767da8e0bcf01048b1f5beb75904a3
|
data/bin/octool
CHANGED
@@ -21,7 +21,7 @@ class App
|
|
21
21
|
def self.find_config(args)
|
22
22
|
path = args.first || OCTool::DEFAULT_CONFIG_FILENAME
|
23
23
|
path = File.join(path, OCTool::DEFAULT_CONFIG_FILENAME) if File.directory?(path)
|
24
|
-
path
|
24
|
+
File.expand_path(path)
|
25
25
|
end
|
26
26
|
|
27
27
|
program_desc 'Open Compliance Tool'
|
@@ -51,7 +51,24 @@ class App
|
|
51
51
|
v.default_command :data
|
52
52
|
end
|
53
53
|
|
54
|
-
desc '
|
54
|
+
desc 'export data to CSV'
|
55
|
+
arg_name 'path/to/system/config.yaml'
|
56
|
+
command :csv do |csv|
|
57
|
+
csv.desc 'where to store outputs'
|
58
|
+
csv.default_value data_dir
|
59
|
+
csv.long_desc 'Default output directory respects env vars TMPDIR, TMP, TEMP'
|
60
|
+
csv.arg_name 'path/to/output/dir'
|
61
|
+
csv.flag [:d, :dir]
|
62
|
+
|
63
|
+
csv.action do |global_options, options, args|
|
64
|
+
export_dir = options[:dir]
|
65
|
+
config_file = find_config(args)
|
66
|
+
system = OCTool::Parser.new(config_file).load_system
|
67
|
+
system.dump export_dir
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
desc 'validate data and generate System Security Plan'
|
55
72
|
arg_name 'path/to/system/config.yaml'
|
56
73
|
command :ssp do |s|
|
57
74
|
s.desc 'where to store outputs'
|
@@ -92,12 +109,12 @@ class App
|
|
92
109
|
# Error logic here
|
93
110
|
# Return false to skip default error handling.
|
94
111
|
if ENV['DEBUG']
|
95
|
-
|
96
|
-
|
112
|
+
warn exception.message
|
113
|
+
warn exception.backtrace
|
97
114
|
pp exception
|
98
115
|
false
|
99
116
|
end
|
100
|
-
|
117
|
+
warn '[FAIL]'
|
101
118
|
true
|
102
119
|
end
|
103
120
|
end
|
data/lib/octool.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'octool/version.rb'
|
4
4
|
|
5
5
|
# Built-ins.
|
6
|
+
require 'csv'
|
7
|
+
require 'json'
|
6
8
|
require 'pp'
|
7
9
|
|
8
10
|
# 3rd-party libs.
|
@@ -15,12 +17,6 @@ require 'octool/parser'
|
|
15
17
|
require 'octool/ssp'
|
16
18
|
require 'octool/system'
|
17
19
|
|
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
20
|
# Mixins.
|
25
21
|
module OCTool
|
26
22
|
include Kwalify::Util::HashLike # defines [], []=, and keys?
|
data/lib/octool/constants.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module OCTool
|
4
|
-
LATEST_SCHEMA_VERSION = 'v1.0.
|
4
|
+
LATEST_SCHEMA_VERSION = 'v1.0.2'
|
5
5
|
BASE_SCHEMA_DIR = File.join(File.dirname(__FILE__), '..', '..', 'schemas').freeze
|
6
6
|
ERB_DIR = File.join(File.dirname(__FILE__), '..', '..', 'templates').freeze
|
7
7
|
DEFAULT_CONFIG_FILENAME = 'config.yaml'
|
data/lib/octool/parser.rb
CHANGED
@@ -4,6 +4,7 @@ module OCTool
|
|
4
4
|
# Custom error to show validation errors.
|
5
5
|
class ValidationError < StandardError
|
6
6
|
attr_reader :errors
|
7
|
+
|
7
8
|
def initialize(path, errors)
|
8
9
|
@path = path
|
9
10
|
@errors = errors
|
@@ -40,9 +41,9 @@ module OCTool
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def validate_file(path, type)
|
43
|
-
|
44
|
-
data =
|
45
|
-
errors =
|
44
|
+
kwalify = kwalifyer(type)
|
45
|
+
data = kwalify.parse_file(path)
|
46
|
+
errors = kwalify.errors
|
46
47
|
raise ValidationError.new(path, errors) unless errors.empty?
|
47
48
|
|
48
49
|
data
|
@@ -50,7 +51,7 @@ module OCTool
|
|
50
51
|
die e.message
|
51
52
|
end
|
52
53
|
|
53
|
-
def
|
54
|
+
def kwalifyer(type)
|
54
55
|
schema_file = File.join(schema_dir, "#{type}.yaml")
|
55
56
|
schema = Kwalify::Yaml.load_file(schema_file)
|
56
57
|
validator = Kwalify::Validator.new(schema)
|
@@ -64,40 +65,57 @@ module OCTool
|
|
64
65
|
def schema_version
|
65
66
|
@schema_version ||= Kwalify::Yaml.load_file(@config_file)['schema_version']
|
66
67
|
rescue StandarError
|
67
|
-
|
68
|
+
warn '[FAIL] Unable to read schema_version'
|
68
69
|
exit(1)
|
69
70
|
end
|
70
71
|
|
71
|
-
#
|
72
|
+
# Validate and load data in one pass.
|
72
73
|
def validate_data
|
73
74
|
base_dir = File.dirname(@config_file)
|
74
75
|
config = validate_file(@config_file, 'config')
|
76
|
+
sys = System.new(config)
|
75
77
|
config['includes'].each do |inc|
|
76
78
|
path = File.join(base_dir, inc['path'])
|
77
|
-
|
78
|
-
validate_file(path, type)
|
79
|
+
sys.data << include_data(path, inc['type'])
|
79
80
|
end
|
80
|
-
|
81
|
+
sys
|
81
82
|
end
|
82
83
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
84
|
+
def include_data(path, type)
|
85
|
+
data = validate_file(path, type)
|
86
|
+
data['type'] = type
|
87
|
+
method("parsed_#{type}".to_sym).call(data)
|
88
|
+
end
|
89
|
+
|
90
|
+
def parsed_component(component)
|
91
|
+
component['attestations'].map! do |a|
|
92
|
+
# Add a "component_key" field to each attestation.
|
93
|
+
a['component_key'] = component['component_key']
|
94
|
+
a['satisfies'].map! do |s|
|
95
|
+
# Add "attestation_key" to each control satisfied by this attestation.
|
96
|
+
s['attestation_key'] = a['summary']
|
97
|
+
# Add "component_key" to each control satisfied by this attestation.
|
98
|
+
s['component_key'] = component['component_key']
|
99
|
+
s
|
100
|
+
end
|
101
|
+
a
|
90
102
|
end
|
91
|
-
|
103
|
+
component
|
92
104
|
end
|
93
105
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
106
|
+
def parsed_standard(standard)
|
107
|
+
# Add 'standard_key' to each control family and to each control.
|
108
|
+
standard['families'].map! { |f| f['standard_key'] = standard['standard_key']; f }
|
109
|
+
standard['controls'].map! { |c| c['standard_key'] = standard['standard_key']; c }
|
110
|
+
standard
|
111
|
+
end
|
112
|
+
|
113
|
+
def parsed_certification(cert)
|
114
|
+
cert['requires'].map! { |r| r['certification_key'] = cert['certification_key']; r }
|
115
|
+
cert
|
101
116
|
end
|
117
|
+
|
118
|
+
alias load_system validate_data
|
119
|
+
alias load_file validate_file
|
102
120
|
end
|
103
121
|
end
|
data/lib/octool/ssp.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
4
|
|
3
5
|
module OCTool
|
@@ -15,17 +17,18 @@ module OCTool
|
|
15
17
|
require 'paru/pandoc'
|
16
18
|
Paru::Pandoc.new
|
17
19
|
end
|
18
|
-
rescue UncaughtThrowError
|
19
|
-
|
20
|
+
rescue UncaughtThrowError
|
21
|
+
warn '[FAIL] octool requires pandoc to generate the SSP. Is pandoc installed?'
|
20
22
|
exit(1)
|
21
23
|
end
|
22
24
|
|
23
25
|
def generate
|
24
|
-
|
25
|
-
|
26
|
+
unless File.writable?(@output_dir)
|
27
|
+
warn "[FAIL] #{@output_dir} is not writable"
|
26
28
|
exit(1)
|
27
29
|
end
|
28
30
|
render_template
|
31
|
+
write_acronyms
|
29
32
|
write 'pdf'
|
30
33
|
write 'docx'
|
31
34
|
end
|
@@ -39,6 +42,15 @@ module OCTool
|
|
39
42
|
puts 'done'
|
40
43
|
end
|
41
44
|
|
45
|
+
def write_acronyms
|
46
|
+
return unless @system.acronyms
|
47
|
+
|
48
|
+
out_path = File.join(@output_dir, 'acronyms.json')
|
49
|
+
File.open(out_path, 'w') { |f| f.write JSON.pretty_generate(@system.acronyms) }
|
50
|
+
ENV['PANDOC_ACRONYMS_ACRONYMS'] = out_path
|
51
|
+
end
|
52
|
+
|
53
|
+
# rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
42
54
|
def write(type = 'pdf')
|
43
55
|
out_path = File.join(@output_dir, "ssp.#{type}")
|
44
56
|
print "Building #{out_path} ... "
|
@@ -50,11 +62,16 @@ module OCTool
|
|
50
62
|
toc_depth 3
|
51
63
|
number_sections
|
52
64
|
highlight_style 'pygments'
|
65
|
+
filter 'pandoc-acronyms' if ENV['PANDOC_ACRONYMS_ACRONYMS']
|
66
|
+
# https://en.wikibooks.org/wiki/LaTeX/Source_Code_Listings#Encoding_issue
|
67
|
+
# Uncomment the following line after the "listings" package is compatible with utf8
|
68
|
+
# listings
|
53
69
|
end
|
54
70
|
output = converter << File.read(md_path)
|
55
71
|
File.new(out_path, 'wb').write(output)
|
56
72
|
puts 'done'
|
57
73
|
end
|
74
|
+
# rubocop:enable Metrics/AbcSize,Metrics/MethodLength
|
58
75
|
|
59
76
|
def md_path
|
60
77
|
@md_path ||= File.join(@output_dir, 'ssp.md')
|
data/lib/octool/system.rb
CHANGED
@@ -6,73 +6,79 @@ module OCTool
|
|
6
6
|
attr_accessor :config
|
7
7
|
attr_accessor :data
|
8
8
|
|
9
|
+
TABLE_NAMES = [
|
10
|
+
'components',
|
11
|
+
'satisfies',
|
12
|
+
'attestations',
|
13
|
+
'standards',
|
14
|
+
'controls',
|
15
|
+
'families',
|
16
|
+
'certifications',
|
17
|
+
'requires',
|
18
|
+
].freeze
|
19
|
+
|
9
20
|
def initialize(config)
|
10
21
|
@config = config
|
11
22
|
@data = []
|
12
23
|
end
|
13
24
|
|
25
|
+
def acronyms
|
26
|
+
@acronyms ||= config['acronyms']
|
27
|
+
end
|
28
|
+
|
14
29
|
def certifications
|
15
|
-
@certifications ||=
|
30
|
+
@certifications ||= data.select { |e| e['type'] == 'certification' }
|
16
31
|
end
|
17
32
|
|
18
33
|
def components
|
19
|
-
@components ||=
|
34
|
+
@components ||= data.select { |e| e['type'] == 'component' }
|
20
35
|
end
|
21
36
|
|
22
37
|
def standards
|
23
|
-
@standards ||=
|
38
|
+
@standards ||= data.select { |e| e['type'] == 'standard' }
|
24
39
|
end
|
25
40
|
|
26
41
|
# List of all attestations claimed by components in the system.
|
27
42
|
def attestations
|
28
|
-
@attestations ||=
|
29
|
-
@attestations = []
|
30
|
-
components.each do |c|
|
31
|
-
# Add a "component_key" field to each attestation.
|
32
|
-
c.attestations.map! { |e| e['component_key'] = c.component_key; e }
|
33
|
-
@attestations << c.attestations
|
34
|
-
end
|
35
|
-
@attestations.flatten!
|
36
|
-
end
|
43
|
+
@attestations ||= components.map { |c| c['attestations'] }.flatten
|
37
44
|
end
|
38
45
|
|
39
46
|
# List of all coverages.
|
40
47
|
def satisfies
|
41
|
-
@satisfies ||=
|
42
|
-
@satisfies = []
|
43
|
-
attestations.each do |a|
|
44
|
-
# Add an "attestation_key" field to each cover.
|
45
|
-
a.satisfies.map! { |e| e['component_key'] = a.commponent_key; e }
|
46
|
-
a.satisfies.map! { |e| e['attestation_key'] = a.attestation_summary; e }
|
47
|
-
@satisfies << a.satisfies
|
48
|
-
end
|
49
|
-
@satisfies.flatten!
|
50
|
-
end
|
48
|
+
@satisfies ||= attestations.map { |a| a['satisfies'] }.flatten
|
51
49
|
end
|
52
50
|
|
53
51
|
# List of all controls defined by standards in the system.
|
54
52
|
def controls
|
55
|
-
@controls
|
56
|
-
@controls = []
|
57
|
-
standards.each do |s|
|
58
|
-
# Add a "standard_key" field to each control.
|
59
|
-
s.controls.map! { |e| e['standard_key'] = s.standard_key; e }
|
60
|
-
@controls << s.controls
|
61
|
-
end
|
62
|
-
@controls.flatten!
|
63
|
-
end
|
53
|
+
@controls ||= standards.map { |s| s['controls'] }.flatten
|
64
54
|
end
|
65
55
|
|
66
56
|
# List of all families defined by standards in the system.
|
67
57
|
def families
|
68
|
-
@families
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
58
|
+
@families ||= standards.map { |s| s['families'] }.flatten
|
59
|
+
end
|
60
|
+
|
61
|
+
# List of required controls for all certifications.
|
62
|
+
def requires
|
63
|
+
@requires ||= certifications.map { |c| c['requires'] }.flatten
|
64
|
+
end
|
65
|
+
|
66
|
+
def dump(writable_dir)
|
67
|
+
TABLE_NAMES.each do |table|
|
68
|
+
write_csv method(table.to_sym).call, File.join(writable_dir, "#{table}.csv")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convert array of hashes into a CSV.
|
73
|
+
def write_csv(ary, filename)
|
74
|
+
# Throw away nested hashes. The parser already created separate tables for them.
|
75
|
+
ary = ary.map { |e| e.reject { |_, val| val.is_a?(Enumerable) } }
|
76
|
+
|
77
|
+
warn "[INFO] write #{filename}"
|
78
|
+
CSV.open(filename, 'wb') do |csv|
|
79
|
+
column_names = ary.first.keys
|
80
|
+
csv << column_names
|
81
|
+
ary.each { |hash| csv << hash.values_at(*column_names) }
|
76
82
|
end
|
77
83
|
end
|
78
84
|
end
|
data/lib/octool/version.rb
CHANGED
data/octool.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
== octool - Open Compliance Tool
|
2
2
|
|
3
|
-
v0.0.
|
3
|
+
v0.0.9
|
4
4
|
|
5
5
|
=== Global Options
|
6
6
|
=== --help
|
@@ -14,6 +14,18 @@ Display the program version
|
|
14
14
|
|
15
15
|
|
16
16
|
=== Commands
|
17
|
+
==== Command: <tt>csv path/to/system/config.yaml</tt>
|
18
|
+
export data to CSV
|
19
|
+
|
20
|
+
|
21
|
+
===== Options
|
22
|
+
===== -d|--dir path/to/output/dir
|
23
|
+
|
24
|
+
where to store outputs
|
25
|
+
|
26
|
+
[Default Value] /tmp
|
27
|
+
Default output directory respects env vars TMPDIR, TMP, TEMP
|
28
|
+
|
17
29
|
==== Command: <tt>help command</tt>
|
18
30
|
Shows a list of commands or help for one command
|
19
31
|
|
@@ -25,7 +37,7 @@ List commands one per line, to assist with shell completion
|
|
25
37
|
|
26
38
|
|
27
39
|
==== Command: <tt>ssp path/to/system/config.yaml</tt>
|
28
|
-
generate System Security Plan
|
40
|
+
validate data and generate System Security Plan
|
29
41
|
|
30
42
|
|
31
43
|
===== Options
|
@@ -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
|