octool 0.0.4 → 0.0.9

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: 6a99d4ae79b106f19c3a49be0fd5fa84de52aaf790494a0f78e55c16fd00bbc5
4
- data.tar.gz: d50899b6e334df9a7f40071f053824af296c842e7d11c8add1ae8e0b17c8313b
3
+ metadata.gz: 5f2733d9e7a9f5a736a283139540cdae45cf887c2bd54e0de2461a30bc4602da
4
+ data.tar.gz: 87854e857038bafc19bdcbb34e23fe8a58f9c0d9e7836d3297d50b3cbe032367
5
5
  SHA512:
6
- metadata.gz: d447539ed45f3c6d55eaabdc04763b21a15bcfb1c9a25bcc8db050c885dba0d030634693353614243e59cec9c9622f09289c35d71aec52fdc56a1fc2922e896e
7
- data.tar.gz: 728c1d363e0f30c4c3f52149088d867cefdb0b63e3b3594fbe5aa1e70b04e66749fe2466ed90643fd84e94b416367f59f26e9d4f049741f8b0bb2d61b15f6306
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 'generate System Security Plan'
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
- STDERR.puts exception.message
96
- STDERR.puts exception.backtrace
112
+ warn exception.message
113
+ warn exception.backtrace
97
114
  pp exception
98
115
  false
99
116
  end
100
- STDERR.puts '[FAIL]'
117
+ warn '[FAIL]'
101
118
  true
102
119
  end
103
120
  end
@@ -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?
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OCTool
4
- LATEST_SCHEMA_VERSION = 'v1.0.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'
@@ -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
- parser = kwalify_parser(type)
44
- data = parser.parse_file(path)
45
- errors = parser.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 kwalify_parser(type)
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
- STDERR.puts '[FAIL] Unable to read schema_version'
68
+ warn '[FAIL] Unable to read schema_version'
68
69
  exit(1)
69
70
  end
70
71
 
71
- # Check that all data files are valid.
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
- type = inc['type']
78
- validate_file(path, type)
79
+ sys.data << include_data(path, inc['type'])
79
80
  end
80
- config
81
+ sys
81
82
  end
82
83
 
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)
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
- system
103
+ component
92
104
  end
93
105
 
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
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
@@ -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 => e
19
- STDERR.puts '[FAIL] octool requires pandoc to generate the SSP. Is pandoc installed?'
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
- if not File.writable?(@output_dir)
25
- STDERR.puts "[FAIL] #{@output_dir} is not writable"
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')
@@ -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 ||= @data.select { |e| e.is_a?(OCTool::Certification) }
30
+ @certifications ||= data.select { |e| e['type'] == 'certification' }
16
31
  end
17
32
 
18
33
  def components
19
- @components ||= @data.select { |e| e.is_a?(OCTool::Component) }
34
+ @components ||= data.select { |e| e['type'] == 'component' }
20
35
  end
21
36
 
22
37
  def standards
23
- @standards ||= @data.select { |e| e.is_a?(OCTool::Standard) }
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 ||= begin
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 ||= begin
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 || begin
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 || begin
69
- @families = []
70
- standards.each do |s|
71
- # Add a "standard_key" field to each family.
72
- s.families.map! { |e| e['standard_key'] = s.standard_key; e }
73
- @families << s.families
74
- end
75
- @families.flatten!
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OCTool
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.9'
5
5
  end
@@ -1,6 +1,6 @@
1
1
  == octool - Open Compliance Tool
2
2
 
3
- v0.0.4
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