opencontrol-linter 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c357e597012b9d0a0d3e39d2142641b07da23a7a4b76c3f6354a7a7f71293b2a
4
- data.tar.gz: 56ce59414bc589a19ea9c6d48be26fe7179c157693180314d03ac8bf7537e756
3
+ metadata.gz: ed96388c4ae00d37b78992687c43e054b5a8d043db7f7f28fbb1702723055932
4
+ data.tar.gz: c66468f2db29a8ea4de93d228c3c1d02efa3a6409c5e1e4a3e387977c490570c
5
5
  SHA512:
6
- metadata.gz: 127982ea79db363912df7ae1c6e30dc148b3c7dc6d256d446892fbbb24262c31e2db38faf49ababcde44b51874d3aa8e9e99a1120f5f77d2ef8a453677ecd8dd
7
- data.tar.gz: 86724dbdbda2d39d63239481b064a4c41bb68bf341bbad26fc50a11ce7e540e70a0a2bd432d280ead7e33019b71be342e6959d6794609e500340d72a912f9f1e
6
+ metadata.gz: bd127b83e300f08a26191b67e960f5acc1cc1fb79c8100ec10c9373956017f69ae2833a5aa083211f6cd3b0e2406135ee87641633211553078ec9513feb78ca9
7
+ data.tar.gz: 73289f4f62be55c3e42eee035257790571e832468c2aa3acee38c224bcee3020fdeffe38d9d618f39d1d9a6f2317693ada7285edf1d79ae302b8cab43d7969e8
@@ -1,53 +1,198 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "yaml"
5
- require "json"
6
- require "pp"
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
- DEFAULT_OPENCONTROLS_FILENAME = "./opencontrols/opencontrol.yaml".freeze
12
- CFACTS_COLUMNS = ['Control Key', 'Response']
13
- opencontrols_filename = DEFAULT_OPENCONTROLS_FILENAME
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
- puts "OpenControl conversion to CSV CFACTS format starting".green
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
- if not File.exists?(opencontrols_filename)
19
- puts "Expected an opencontrol.yaml, but could not find opencontrol.yaml " +
20
- "in this directory.".red
21
- exit(1)
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
- puts " . Reading OpenControl components:"
25
- control_responses = {}
26
- opencontrols = YAML.load_file(opencontrols_filename)
27
-
28
- opencontrols['data']['components'].each do |component|
29
- puts " . #{component['name']}"
30
- component['satisfies'].each do |control|
31
- key = "#{control['standard_key']} #{control['control_key']}"
32
- control_responses[key] = {} unless control_responses.has_key?(key)
33
- control_responses[key]['text'] = "" unless control_responses[key].has_key?('text')
34
- control_responses[key]['control_key'] = control['control_key']
35
- control_responses[key]['standard_key'] = control['standard_key']
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
- puts " . Building CFACTS file:"
45
- CSV.open(cfacts_csv_filename, "w") do |csv|
46
- csv << CFACTS_COLUMNS
47
- control_responses.each do |key, control_response|
48
- puts " . #{control_response['control_key']}"
49
- csv << [control_response['control_key'], control_response['text']]
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
- puts "Completed (OpenControl 2 CFACTS)".green
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
@@ -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.4'.freeze
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
@@ -69,7 +69,7 @@ satisfies:
69
69
  - text: "Justification in narrative form for 2.1"
70
70
  standard_key: PCI-DSS-MAY-2015
71
71
  responsible_role: "AWS Staff"
72
- schema_version: 3.0.0
72
+ schema_version: 3.1.0
73
73
  verifications:
74
74
  - key: EC2_Verification_2
75
75
  name: EC2 Governor 2
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
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-03-27 00:00:00.000000000 Z
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