opencontrol-linter 0.1.4 → 0.1.5
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/exe/opencontrol2cfacts +181 -36
- data/lib/opencontrol/inspector.rb +37 -0
- data/lib/opencontrol/version.rb +1 -1
- data/vendor/schemas/examples/component_v3.1.0.yaml +1 -1
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed96388c4ae00d37b78992687c43e054b5a8d043db7f7f28fbb1702723055932
|
4
|
+
data.tar.gz: c66468f2db29a8ea4de93d228c3c1d02efa3a6409c5e1e4a3e387977c490570c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd127b83e300f08a26191b67e960f5acc1cc1fb79c8100ec10c9373956017f69ae2833a5aa083211f6cd3b0e2406135ee87641633211553078ec9513feb78ca9
|
7
|
+
data.tar.gz: 73289f4f62be55c3e42eee035257790571e832468c2aa3acee38c224bcee3020fdeffe38d9d618f39d1d9a6f2317693ada7285edf1d79ae302b8cab43d7969e8
|
data/exe/opencontrol2cfacts
CHANGED
@@ -1,53 +1,198 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
4
|
+
require 'yaml'
|
5
|
+
require 'json'
|
6
|
+
require 'pp'
|
7
7
|
require 'date'
|
8
|
-
require 'csv'
|
9
8
|
require 'colorize'
|
9
|
+
require 'rubyXL'
|
10
|
+
require 'rubyXL/convenience_methods/cell'
|
11
|
+
require 'rationalist'
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
cfacts_csv_filename = "./opencontrols/opencontrol.cfacts.csv".freeze
|
13
|
+
CFACTS_WORKBOOK_INDEX = 0
|
14
|
+
CFACTS_CONTROL_KEY_COLUMN_INDEX = 1
|
15
|
+
CFACTS_NARRATIVE_COLUMN_INDEX = 4
|
15
16
|
|
16
|
-
|
17
|
+
DEFAULT_OPENCONTROLS_FILENAME = './opencontrols/opencontrol.yaml'
|
18
|
+
DEFAULT_CFACTS_XLSX_FILENAME = './assets/opencontrol.cfacts.xlsx'
|
19
|
+
DEFAULT_CFACTS_XLSX_OUTPUT_FILENAME = './opencontrols/opencontrol.cfacts.xlsx'
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
USAGE_TEXT = <<USAGE_TEXT
|
22
|
+
usage: opencontrol2cfacts
|
23
|
+
Usually used with a control masonry process that outputs merged yaml results.
|
24
|
+
|
25
|
+
optional arguments:
|
26
|
+
-h, --help show this help message and exit
|
27
|
+
-i, --cfacts_in_filename --input
|
28
|
+
Specify the xlsx spreadsheet file input from cfacts
|
29
|
+
(this should contain your various id's).
|
30
|
+
defaults to #{DEFAULT_CFACTS_XLSX_FILENAME}
|
31
|
+
-o, --cfacts_out_filename --output
|
32
|
+
Specify the xlsx spreadsheet file output for cfacts
|
33
|
+
(this should contain your various id's).
|
34
|
+
defaults to #{DEFAULT_CFACTS_XLSX_OUTPUT_FILENAME}
|
35
|
+
-v, --version Show the version of this utility.
|
36
|
+
|
37
|
+
Usage examples:
|
38
|
+
|
39
|
+
# convert using default directories #{DEFAULT_OPENCONTROLS_FILENAME}
|
40
|
+
# #{DEFAULT_CFACTS_XLSX_FILENAME} to #{DEFAULT_CFACTS_XLSX_OUTPUT_FILENAME}
|
41
|
+
opencontrol2cfacts
|
42
|
+
|
43
|
+
# lint all components subdir components
|
44
|
+
opencontrol2cfacts --input './cfacts.inputfile.xslx'
|
45
|
+
USAGE_TEXT
|
46
|
+
|
47
|
+
ALIASES = {
|
48
|
+
input: %w[i cfacts_in_filename],
|
49
|
+
output: %w[o cfacts_out_filename],
|
50
|
+
controls: %w[c opencontrols_filename],
|
51
|
+
help: 'h',
|
52
|
+
version: 'v'
|
53
|
+
}.freeze
|
54
|
+
|
55
|
+
VERSION = '0.1'
|
56
|
+
|
57
|
+
def write_when_empty_merge_strategy(row, narrative)
|
58
|
+
if row[CFACTS_NARRATIVE_COLUMN_INDEX].nil? ||
|
59
|
+
row[CFACTS_NARRATIVE_COLUMN_INDEX].value.nil? ||
|
60
|
+
row[CFACTS_NARRATIVE_COLUMN_INDEX].value.strip == ''
|
61
|
+
row[CFACTS_NARRATIVE_COLUMN_INDEX].change_contents(narrative)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def merge_response(row, narrative)
|
66
|
+
write_when_empty_merge_strategy(row, narrative)
|
67
|
+
end
|
68
|
+
|
69
|
+
def read_opencontrols_yaml(opencontrols_filename)
|
70
|
+
puts ' . Reading OpenControl components:'
|
71
|
+
YAML.load_file(opencontrols_filename)
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_narrative(key, control, control_responses)
|
75
|
+
control['narrative'].each do |narrative|
|
76
|
+
control_responses[key]['text'] += key + "\n"
|
77
|
+
control_responses[key]['text'] += narrative['text']
|
78
|
+
control_responses[key]['text'] += "\n"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def narrative?(key, control_responses)
|
83
|
+
control_responses[key].key?('text')
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_control_response(key, control, control_responses)
|
87
|
+
control_responses[key] = {} unless control_responses.key?(key)
|
88
|
+
control_responses[key]['text'] = '' unless narrative?(key, control_responses)
|
89
|
+
control_responses[key]['control_key'] = control['control_key']
|
90
|
+
control_responses[key]['standard_key'] = control['standard_key']
|
91
|
+
build_narrative(key, control, control_responses)
|
92
|
+
end
|
93
|
+
|
94
|
+
def key_for_control(control)
|
95
|
+
"#{control['standard_key']} #{control['control_key']}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def build_control_responses(opencontrols)
|
99
|
+
control_responses = {}
|
100
|
+
opencontrols['data']['components'].each do |component|
|
101
|
+
puts " . #{component['name']}"
|
102
|
+
component['satisfies'].each do |control|
|
103
|
+
key = key_for_control(control)
|
104
|
+
build_control_response(key, control, control_responses)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
control_responses
|
108
|
+
end
|
109
|
+
|
110
|
+
def row_malformed?(row)
|
111
|
+
row.nil? || row.size < CFACTS_CONTROL_KEY_COLUMN_INDEX
|
22
112
|
end
|
23
113
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
control['narrative'].each do |narrative|
|
37
|
-
control_responses[key]['text'] += key + "\n"
|
38
|
-
control_responses[key]['text'] += narrative['text']
|
39
|
-
control_responses[key]['text'] += "\n"
|
114
|
+
def control_key_matches?(control_key, control_response)
|
115
|
+
control_key == control_response['control_key'].strip
|
116
|
+
end
|
117
|
+
|
118
|
+
def maybe_merge_into_row(row, row_number, control_response)
|
119
|
+
if row_malformed?(row)
|
120
|
+
puts "Skip malformed row (line no. #{row_number} [#{row}])" unless row.nil?
|
121
|
+
else
|
122
|
+
control_key = row[CFACTS_CONTROL_KEY_COLUMN_INDEX].value.strip
|
123
|
+
# puts "Searching in row #{control_key}"
|
124
|
+
if control_key_matches?(control_key, control_response)
|
125
|
+
merge_response(row, control_response['text'])
|
40
126
|
end
|
41
127
|
end
|
42
128
|
end
|
43
129
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
130
|
+
def locate_and_merge_control(worksheet, control_response)
|
131
|
+
puts " . #{control_response['control_key']}"
|
132
|
+
# we have to locate the key in the sheet - skip the titles row
|
133
|
+
1.upto(worksheet.sheet_data.size) do |row_number|
|
134
|
+
row = worksheet[row_number]
|
135
|
+
maybe_merge_into_row(row, row_number, control_response)
|
50
136
|
end
|
51
137
|
end
|
52
138
|
|
53
|
-
|
139
|
+
def update_workbook(in_filename, control_responses, out_filename)
|
140
|
+
puts ' . Building CFACTS file:'
|
141
|
+
workbook = RubyXL::Parser.parse(in_filename)
|
142
|
+
worksheet = workbook[CFACTS_WORKBOOK_INDEX]
|
143
|
+
control_responses.each do |_key, control_response|
|
144
|
+
locate_and_merge_control(worksheet, control_response)
|
145
|
+
end
|
146
|
+
workbook.write(out_filename)
|
147
|
+
end
|
148
|
+
|
149
|
+
def no_opencontrol_exit
|
150
|
+
puts 'Expected an opencontrol.yaml, but could not find opencontrol.yaml ' +
|
151
|
+
'in this directory.'.red
|
152
|
+
exit(1)
|
153
|
+
end
|
154
|
+
|
155
|
+
def check_files_exist(opencontrols_filename)
|
156
|
+
no_opencontrol_exit unless File.exist?(opencontrols_filename)
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.parse_args(arguments)
|
160
|
+
opts = Rationalist.parse(arguments, alias: ALIASES)
|
161
|
+
|
162
|
+
exit(show_version) if opts[:version]
|
163
|
+
exit(show_help) if opts[:help]
|
164
|
+
|
165
|
+
opts[:controls] = DEFAULT_OPENCONTROLS_FILENAME unless opts.key?(:controls)
|
166
|
+
opts[:input] = DEFAULT_CFACTS_XLSX_FILENAME unless opts.key?(:input)
|
167
|
+
opts[:output] = DEFAULT_CFACTS_XLSX_OUTPUT_FILENAME unless opts.key?(:output)
|
168
|
+
opts
|
169
|
+
end
|
170
|
+
|
171
|
+
def show_help
|
172
|
+
puts USAGE_TEXT
|
173
|
+
0 # exit with no error
|
174
|
+
end
|
175
|
+
|
176
|
+
def show_version
|
177
|
+
puts 'opecontrol2cfacts: v' + VERSION
|
178
|
+
puts 'CMS 2019 Adrian Kierman '
|
179
|
+
0 # exit with no error
|
180
|
+
end
|
181
|
+
|
182
|
+
def main
|
183
|
+
opts = parse_args(ARGV)
|
184
|
+
opencontrols_filename = opts[:controls]
|
185
|
+
in_filename = opts[:input]
|
186
|
+
out_filename = opts[:output]
|
187
|
+
puts 'OpenControl conversion to CSV CFACTS format starting'.green
|
188
|
+
|
189
|
+
check_files_exist(opencontrols_filename)
|
190
|
+
opencontrols = read_opencontrols_yaml(opencontrols_filename)
|
191
|
+
control_responses = build_control_responses(opencontrols)
|
192
|
+
update_workbook(in_filename, control_responses, out_filename)
|
193
|
+
|
194
|
+
puts 'Completed (OpenControl 2 CFACTS)'.green
|
195
|
+
end
|
196
|
+
|
197
|
+
# only run main if called directly - don't call it if running as a library
|
198
|
+
main if $PROGRAM_NAME == __FILE__
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'opencontrol'
|
4
|
+
require 'rationalist'
|
5
|
+
require 'colorize'
|
6
|
+
require 'pp'
|
7
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
8
|
+
|
9
|
+
certification_key = ''
|
10
|
+
certification_filename = "./certifications/#{certification_key}.yaml"
|
11
|
+
|
12
|
+
data = YAML.load_file('./opencontrol.yaml').with_indifferent_access
|
13
|
+
data[:standards] = data[:standards].collect do |filename|
|
14
|
+
YAML.load_file(filename)
|
15
|
+
end
|
16
|
+
data[:components] = data[:components].collect do |filename|
|
17
|
+
YAML.load_file(filename + '/component.yaml')
|
18
|
+
end
|
19
|
+
# just load the one certification here instead
|
20
|
+
data[:certification] = YAML.load_file(certification_filename)
|
21
|
+
.merge({key: certification_key})
|
22
|
+
|
23
|
+
data = JSON.parse(data.to_json)
|
24
|
+
puts data.to_yaml
|
25
|
+
|
26
|
+
# to get the inspector output we want we'd pull in the controls from components where we have controls in the standard
|
27
|
+
# we might want to invert the data so that each control is identified in the expected way
|
28
|
+
#
|
29
|
+
# We want to count up the completion status of the whole project and see where we are easily
|
30
|
+
|
31
|
+
data[:components].each do |component|
|
32
|
+
component[:satisfies] = component[:satisfies].select do |control|
|
33
|
+
data[:certification][:standards][control[:standard_key]].keys.include? control[:control_key]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# after this we want to iterate the components and add up the status of the components in terms of implementation
|
data/lib/opencontrol/version.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Opencontrol
|
4
4
|
# This module holds the Opencontrol Linter version information.
|
5
5
|
module Version
|
6
|
-
STRING = '0.1.
|
6
|
+
STRING = '0.1.5'.freeze
|
7
7
|
|
8
8
|
MSG = '%<version>s (using Parser %<parser_version>s, running on ' \
|
9
9
|
'%<ruby_engine>s %<ruby_version>s %<ruby_platform>s)'.freeze
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opencontrol-linter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrian Kierman
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-04-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: colorize
|
@@ -143,6 +143,34 @@ dependencies:
|
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
145
|
version: 3.0.4
|
146
|
+
- !ruby/object:Gem::Dependency
|
147
|
+
name: activesupport
|
148
|
+
requirement: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 5.2.2.1
|
153
|
+
type: :runtime
|
154
|
+
prerelease: false
|
155
|
+
version_requirements: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 5.2.2.1
|
160
|
+
- !ruby/object:Gem::Dependency
|
161
|
+
name: rubyXL
|
162
|
+
requirement: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 3.4.3
|
167
|
+
type: :runtime
|
168
|
+
prerelease: false
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 3.4.3
|
146
174
|
- !ruby/object:Gem::Dependency
|
147
175
|
name: bundler
|
148
176
|
requirement: !ruby/object:Gem::Requirement
|
@@ -279,6 +307,7 @@ files:
|
|
279
307
|
- exe/opencontrol2cfacts
|
280
308
|
- lib/opencontrol.rb
|
281
309
|
- lib/opencontrol/cli.rb
|
310
|
+
- lib/opencontrol/inspector.rb
|
282
311
|
- lib/opencontrol/linter.rb
|
283
312
|
- lib/opencontrol/messages.rb
|
284
313
|
- lib/opencontrol/version.rb
|