octool 0.0.5 → 0.0.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03d4e6cdc68059584e26dd26983e2085aed2bb5593b1bb78d5307bc9938401d7
4
- data.tar.gz: 15f8bf0c448fb1e1091f7e6ab78fbffba50925a2a7799dd66ff939674d135cea
3
+ metadata.gz: 35ad41804c6a3eb09cde544a272ea3b6e9108355ce0d32799846335e6d3d6345
4
+ data.tar.gz: 8573306612f8760275735e38e12e4295ccfec60ed731569558d7386063086850
5
5
  SHA512:
6
- metadata.gz: a944191a813ab2ed2d122862466d79e65df9230e64ade9e73b83aefb8fcb98069bfaba0fec0876c539f63b7f229a16311b0721bd81eeb17b49b7564da2995051
7
- data.tar.gz: e3b99bc6618b4192ba99a570bf6dd6a364c52540b73b39d80817a43e7364fdaf730596578756149e60cda5c0e56d3e75f643abdb11838a983f1d8943df9f0712
6
+ metadata.gz: 70a54c2b4a2224ee8933f2a09dd32d252c0ef18170a0e129fdeab5387eea01e42574ed40107efd80a5ca28648e9c0d52afa0d996d227d0fa28e0bfa3173c96e6
7
+ data.tar.gz: 352859097909847bf476ad7ab307eed31764ee0a41a388da32d9f112c47bacdacf155636ac7fb5e7eea89b9ba3c61348bf301459f45e81e2dca48b2e447b91b9
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,6 +51,23 @@ class App
51
51
  v.default_command :data
52
52
  end
53
53
 
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
+
54
71
  desc 'validate data and generate System Security Plan'
55
72
  arg_name 'path/to/system/config.yaml'
56
73
  command :ssp do |s|
@@ -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,12 +3,13 @@
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.
9
11
  require 'kwalify'
10
12
  require 'kwalify/util/hashlike'
11
- require 'recursive-open-struct'
12
13
 
13
14
  # OCTool libs.
14
15
  require 'octool/constants'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OCTool
4
- LATEST_SCHEMA_VERSION = 'v1.0.1'
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,18 +41,17 @@ 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
- data['type'] = type
46
- errors = parser.errors
44
+ kwalify = kwalifyer(type)
45
+ data = kwalify.parse_file(path)
46
+ errors = kwalify.errors
47
47
  raise ValidationError.new(path, errors) unless errors.empty?
48
48
 
49
- RecursiveOpenStruct.new(data, recurse_over_arrays: true, preserve_original_keys: true)
49
+ data
50
50
  rescue SystemCallError, Kwalify::SyntaxError, ValidationError => e
51
51
  die e.message
52
52
  end
53
53
 
54
- def kwalify_parser(type)
54
+ def kwalifyer(type)
55
55
  schema_file = File.join(schema_dir, "#{type}.yaml")
56
56
  schema = Kwalify::Yaml.load_file(schema_file)
57
57
  validator = Kwalify::Validator.new(schema)
@@ -65,7 +65,7 @@ module OCTool
65
65
  def schema_version
66
66
  @schema_version ||= Kwalify::Yaml.load_file(@config_file)['schema_version']
67
67
  rescue StandarError
68
- STDERR.puts '[FAIL] Unable to read schema_version'
68
+ warn '[FAIL] Unable to read schema_version'
69
69
  exit(1)
70
70
  end
71
71
 
@@ -76,11 +76,45 @@ module OCTool
76
76
  sys = System.new(config)
77
77
  config['includes'].each do |inc|
78
78
  path = File.join(base_dir, inc['path'])
79
- sys.data << validate_file(path, inc['type'])
79
+ sys.data << include_data(path, inc['type'])
80
80
  end
81
81
  sys
82
82
  end
83
83
 
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
102
+ end
103
+ component
104
+ end
105
+
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
116
+ end
117
+
84
118
  alias load_system validate_data
85
119
  alias load_file validate_file
86
120
  end
@@ -18,16 +18,17 @@ module OCTool
18
18
  Paru::Pandoc.new
19
19
  end
20
20
  rescue UncaughtThrowError
21
- STDERR.puts '[FAIL] octool requires pandoc to generate the SSP. Is pandoc installed?'
21
+ warn '[FAIL] octool requires pandoc to generate the SSP. Is pandoc installed?'
22
22
  exit(1)
23
23
  end
24
24
 
25
25
  def generate
26
26
  unless File.writable?(@output_dir)
27
- STDERR.puts "[FAIL] #{@output_dir} is not writable"
27
+ warn "[FAIL] #{@output_dir} is not writable"
28
28
  exit(1)
29
29
  end
30
30
  render_template
31
+ write_acronyms
31
32
  write 'pdf'
32
33
  write 'docx'
33
34
  end
@@ -41,18 +42,30 @@ module OCTool
41
42
  puts 'done'
42
43
  end
43
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
+
44
53
  # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
45
54
  def write(type = 'pdf')
46
55
  out_path = File.join(@output_dir, "ssp.#{type}")
47
56
  print "Building #{out_path} ... "
48
57
  converter = pandoc.configure do
49
- from 'markdown'
58
+ from 'markdown+autolink_bare_uris'
50
59
  to type
51
60
  pdf_engine 'lualatex'
52
61
  toc
53
62
  toc_depth 3
54
63
  number_sections
55
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
56
69
  end
57
70
  output = converter << File.read(md_path)
58
71
  File.new(out_path, 'wb').write(output)
@@ -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.type == 'certification' }
30
+ @certifications ||= data.select { |e| e['type'] == 'certification' }
16
31
  end
17
32
 
18
33
  def components
19
- @components ||= @data.select { |e| e.type == 'component' }
34
+ @components ||= data.select { |e| e['type'] == 'component' }
20
35
  end
21
36
 
22
37
  def standards
23
- @standards ||= @data.select { |e| e.type == '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.5'
4
+ VERSION = '0.0.10'
5
5
  end
@@ -1,6 +1,6 @@
1
1
  == octool - Open Compliance Tool
2
2
 
3
- v0.0.5
3
+ v0.0.10
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
 
@@ -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,111 @@
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
+ logo:
12
+ desc: Image for title page.
13
+ required: false
14
+ type: map
15
+ class: Logo
16
+ mapping:
17
+ path:
18
+ desc: Path to image.
19
+ type: str
20
+ required: true
21
+ width:
22
+ desc: Width of image, such as "1in" or "254mm"
23
+ type: str
24
+ required: true
25
+
26
+ name:
27
+ desc: Human-friendly to appear in the SSP.
28
+ required: true
29
+ type: str
30
+
31
+ overview:
32
+ desc: Human-friendly description to appear in the SSP.
33
+ required: true
34
+ type: str
35
+
36
+ maintainers:
37
+ desc: Who should somebody contact for questions about this SSP?
38
+ required: true
39
+ type: seq
40
+ sequence:
41
+ - type: str
42
+
43
+ metadata:
44
+ desc: Optional metadata.
45
+ required: false
46
+ type: map
47
+ class: Metadata
48
+ mapping:
49
+ abstract:
50
+ desc: Abstract appears in document metadata.
51
+ required: false
52
+ type: str
53
+ description:
54
+ desc: Description appears in document metadata.
55
+ required: false
56
+ type: str
57
+ '=':
58
+ desc: Arbitrary key:value pair of strings.
59
+ type: str
60
+
61
+ includes:
62
+ desc: Additional files to include from the system repo.
63
+ required: true
64
+ type: seq
65
+ sequence:
66
+ - type: map
67
+ class: Include
68
+ mapping:
69
+ type:
70
+ required: true
71
+ type: str
72
+ enum:
73
+ - certification
74
+ - component
75
+ - standard
76
+ path:
77
+ desc: Path must be relative within the repo.
78
+ required: true
79
+ type: str
80
+
81
+ acronyms:
82
+ desc: |
83
+ List of acronyms to be referenced in the doc.
84
+
85
+ The acronyms follow the forms and usage described by the pandoc filter
86
+ https://gitlab.com/mirkoboehm/pandoc-acronyms
87
+
88
+ If your config.yaml includes acronyms, the filter is automatically invoked.
89
+ required: false
90
+ type: map
91
+ mapping:
92
+ '=':
93
+ desc: |
94
+ Acronym as used in the doc source, such as "bba".
95
+ The source usually refers to the acronym with syntax "[!bba]",
96
+ but other syntax forms are possible (see upstream doc).
97
+ type: map
98
+ class: Acronym
99
+ mapping:
100
+ shortform:
101
+ desc: The short form of the expanded acronym, such as "BBA".
102
+ required: true
103
+ type: str
104
+ longform:
105
+ desc: |
106
+ The expanded form of the abbreviation, such as "Beer Brewing Attitude".
107
+ The first instance of "[!bba]" in the doc is automatically expanded to
108
+ "<longform> (<shortform>)".
109
+ Example: "[!bba]" expands to "Beer Brewing Attitude (BBA)".
110
+ required: true
111
+ 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
@@ -1,29 +1,28 @@
1
1
  ---
2
- <% if @system.config.logo -%>
2
+ <% if @system.config['logo'] -%>
3
3
  title: |
4
- ![](<%= @system.config.logo.path -%>){width=<%= @system.config.logo.width %>}
4
+ ![](<%= @system.config['logo']['path'] -%>){width=<%= @system.config['logo']['width'] %>}
5
5
 
6
- <%= @system.config.name %>
6
+ <%= @system.config['name'] %>
7
7
  <% else %>
8
- title: "<%= @system.config.name -%>"
8
+ title: "<%= @system.config['name'] -%>"
9
9
  <% end %>
10
10
 
11
11
  subtitle: "System Security Plan"
12
12
 
13
13
  author:
14
- <% @system.config.maintainers.each do |maintainer| %>
14
+ <% @system.config['maintainers'].each do |maintainer| %>
15
15
  - <%= maintainer -%>
16
16
  <% end %>
17
17
 
18
18
  absract: |
19
- <%= @system.config.metadata.abstract rescue 'None' %>
19
+ <%= @system.config['metadata']['abstract'] rescue 'None' %>
20
20
 
21
21
  description: |
22
- <%= @system.config.metadata.description rescue 'None' %>
22
+ <%= @system.config['metadata']['description'] rescue 'None' %>
23
23
 
24
24
  fontsize: 11pt
25
25
  mainfont: NotoSans
26
- #monofont: NotoSansMono-ExtraCondensedLight
27
26
  monofont: NotoSansMono-ExtraCondensed
28
27
  mainfontoptions:
29
28
  - Numbers=Lowercase
@@ -52,52 +51,133 @@ geometry:
52
51
  - left=2cm
53
52
  - right=2cm
54
53
  - bottom=2cm
54
+
55
+ header-includes:
56
+ - |
57
+ ```{=latex}
58
+ % https://ctan.org/pkg/metalogo?lang=en
59
+ \usepackage{metalogo}
60
+ ```
61
+ - |
62
+ ```{=latex}
63
+ % https://github.com/jgm/pandoc/wiki/Pandoc-Tricks#left-aligning-tables-in-latex
64
+ \usepackage[margins=raggedright]{floatrow}
65
+ ```
66
+ - |
67
+ ```{=latex}
68
+ % https://github.com/jgm/pandoc/wiki/Pandoc-Tricks#definition-list-terms-on-their-own-line-in-latex
69
+ % "Clone" the original \item command
70
+ \let\originalitem\item
71
+
72
+ % Redefine the \item command using the "clone"
73
+ \makeatletter
74
+ \renewcommand{\item}[1][\@nil]{%
75
+ \def\tmp{#1}%
76
+ \ifx\tmp\@nnil\originalitem\else\originalitem[#1]\hfill\par\fi}
77
+ \makeatother
78
+ ```
79
+ - |
80
+ ```{=latex}
81
+ % The are at least two ways to configure how LaTeX floats figures.
82
+ %
83
+ % 1. One approach is described in section 17.2 of
84
+ % http://tug.ctan.org/tex-archive/info/epslatex/english/epslatex.pdf
85
+ % However, the approach described there requires to teach people
86
+ % how to write LaTeX cross-references in markdown.
87
+ %
88
+ % 2. Force figures, listings, etc., to float "[H]ere".
89
+ % This is a LaTeX anti-pattern because it causes large gaps of whitespace on some pages.
90
+ % This approach avoids having to teach people to create LaTeX cross-references.
91
+ % https://tex.stackexchange.com/a/101726
92
+ %
93
+ % Use option 2.
94
+ \usepackage{float}
95
+ \floatplacement{figure}{H}
96
+ ```
55
97
  ---
56
98
 
57
- # <%= @system.config.name %>
99
+ # Introduction
58
100
 
59
- ## Overview
101
+ ## About this document
102
+
103
+ A System Security Plan (SSP) is a document to describe security controls in use
104
+ on an information system and their implementation. An SSP provides:
105
+
106
+ - Narrative of security control implementation
107
+ - Description of components and services
108
+ - System data flows and authorization boundaries
60
109
 
61
- <%= @system.config.overview %>
62
110
 
63
111
  ## Standards
64
112
 
65
- This System Security Plan (SSP) addresses these standards:
113
+ This SSP draws from these standards:
66
114
 
67
115
  <% @system.standards.each do |s| -%>
68
- - <%= s.name %>
116
+ - <%= s['name'] %>
69
117
  <% end %>
70
118
 
71
119
  The full copy of each standard is included in the appendix.
72
120
 
73
121
 
74
- ## Components
122
+ ## Certifications
75
123
 
76
- <% @system.components.each do |c| %>
77
- ### <%= c.name %>
124
+ A certification is a logical grouping of controls that are of interest to
125
+ a given subject. A particular certification does not necessarily target all
126
+ controls from a standard, nor does a particular certification need to draw
127
+ from a single standard.
78
128
 
79
- <%= c.description %>
129
+ This SSP addresses these certifications:
130
+
131
+ <% @system.certifications.each do |c| -%>
132
+ - <%=c['name']%>
133
+
134
+ <% c['requires'].each do |r| -%>
135
+ - <%=r['standard_key']-%> control <%=r['control_key']%>
136
+ <% end -%>
80
137
 
81
- <% if c.attestations.empty? %>
82
- _The organization has not yet documented attestations for this component_.
83
- <% else %>
84
- The organization offers the following attestations for this component.
85
138
  <% end %>
86
139
 
87
- <% c.attestations.each do |a| %>
88
- #### <%= a.summary %>
89
140
 
90
- Status: <%= a.status %>
141
+ # <%= @system.config['name'] %>
142
+
143
+ ## Overview
91
144
 
92
- Date verified: <%= a.date_verified if a.date_verified %>
145
+ <%= @system.config['overview'] %>
93
146
 
94
- Satisfies:
95
147
 
96
- <% a.satisfies.each do |cid| -%>
97
- - <%= cid.standard_key %> control <%= cid.control_key %>
98
- <% end -%>
148
+ ## Components
149
+
150
+ <% @system.components.each do |c| %>
151
+ ### <%= c['name'] %>
99
152
 
100
- <%= a.narrative %>
153
+ <%= c['description'] %>
154
+
155
+ <% if c['attestations'].empty? %>
156
+ _The organization has not yet documented attestations for this component_.
157
+ <% else %>
158
+ The organization offers the following attestations for this component.
159
+ <% end %>
160
+
161
+ <% c['attestations'].compact.each do |a| %>
162
+ #### <%= a['summary'] %>
163
+
164
+ +----------+---------------+--------------------------------------------------------------+
165
+ | Status | Date verified | Satisfies |
166
+ +==========+===============+==============================================================+
167
+ <%
168
+ s = a['satisfies'][0]
169
+ verbiage = sprintf('%-58s', [s['standard_key'], 'control', s['control_key']].join(' '))
170
+ -%>
171
+ | <%=sprintf('%-8s', a['status'])-%> | <%=sprintf('%-13s', a['date_verified'])-%> | - <%=verbiage-%> |
172
+ <%
173
+ a['satisfies'][1..].each do |s|
174
+ verbiage = sprintf('%-58s', [s['standard_key'], 'control', s['control_key']].join(' '))
175
+ -%>
176
+ | | | - <%=verbiage-%> |
177
+ <% end -%>
178
+ +----------+---------------+--------------------------------------------------------------+
179
+
180
+ <%= a['narrative'] %>
101
181
 
102
182
  <% end %>
103
183
  <% end %>
@@ -106,25 +186,60 @@ Satisfies:
106
186
  # Appendix: Standards
107
187
 
108
188
  <% @system.standards.each do |s| %>
109
- ## <%=s.name %>
189
+ ## <%=s['name'] %>
110
190
 
111
- <% if s.families and !s.families.empty? %>
191
+ <% if s['families'] and !s['families'].empty? %>
112
192
  ### Families
113
193
 
114
- <% s.families.each do |family| %>
115
- <%= family.family_key %>
116
- ~ <%= family.name %>
194
+ <%=s['name']-%> categorizes controls into logical groups called families.
117
195
 
118
- <% end %>
196
+ | Family abbreviation | Family name |
197
+ | -------------------------- | -------------------- |
198
+ <% s['families'].each do |family| -%>
199
+ | <%=family['family_key']-%> | <%=family['name']-%> |
200
+ <% end -%>
201
+
202
+ : Control families for <%=s['name']%>
119
203
 
120
204
  <% end %>
121
205
 
122
206
  ### Controls
123
207
 
124
- <% s.controls.each do |c| %>
125
- #### Control <%= c.control_key -%>: <%= c.name %>
208
+ <% s['controls'].each do |c| %>
209
+ #### Control <%= c['control_key'] -%>: <%= c['name'] %>
126
210
 
127
- <%= c.description %>
211
+ <%= c['description'] %>
128
212
 
129
213
  <% end %>
130
214
  <% end %>
215
+
216
+
217
+ # Colophon
218
+
219
+ This document was typeset in NotoSans with \LuaTeX\.
220
+ The main body font is 11-point, and
221
+ code snippets use NotoSansMono-ExtraCondensed.
222
+
223
+ The Noto family of fonts is freely available and developed by Google,
224
+ which describes Noto as:
225
+
226
+ > When text is rendered by a computer, sometimes characters are displayed as
227
+ > "tofu". They are little boxes to indicate your device doesn't have a
228
+ > font to display the text.
229
+ >
230
+ > Google has been developing a font family called Noto, which aims to support
231
+ > all languages with a harmonious look and feel. Noto is Google's answer to
232
+ > tofu. The name noto is to convey the idea that Google's goal is to see
233
+ > "no more tofu". Noto has multiple styles and weights, and is freely
234
+ > available to all.
235
+
236
+ Core tools used to produce this document:
237
+
238
+ - [Docker](https://www.docker.com/) provides a repeatable environment in
239
+ which to run the tools.
240
+ - [OCTool](https://github.com/jumanjihouse/octool)
241
+ provides a schema and wrapper to express compliance data as configuration.
242
+ - [Pandoc](https://pandoc.org/) converts extended markdown to PDF output.
243
+ - [Python](https://www.python.org/) is a core language for automation.
244
+ - [Ruby](https://www.ruby-lang.org/en/) is a core language for automation.
245
+ - [TeXLive](https://www.tug.org/texlive/) provides the \TeX\ family of tools.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: octool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Morgan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-18 00:00:00.000000000 Z
11
+ date: 2020-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -93,61 +93,61 @@ dependencies:
93
93
  - !ruby/object:Gem::Version
94
94
  version: 2.19.0
95
95
  - !ruby/object:Gem::Dependency
96
- name: kwalify
96
+ name: json_pure
97
97
  requirement: !ruby/object:Gem::Requirement
98
98
  requirements:
99
99
  - - '='
100
100
  - !ruby/object:Gem::Version
101
- version: 0.7.2
101
+ version: 2.3.0
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - '='
107
107
  - !ruby/object:Gem::Version
108
- version: 0.7.2
108
+ version: 2.3.0
109
109
  - !ruby/object:Gem::Dependency
110
- name: pandoc-ruby
110
+ name: kwalify
111
111
  requirement: !ruby/object:Gem::Requirement
112
112
  requirements:
113
113
  - - '='
114
114
  - !ruby/object:Gem::Version
115
- version: 2.1.4
115
+ version: 0.7.2
116
116
  type: :runtime
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - '='
121
121
  - !ruby/object:Gem::Version
122
- version: 2.1.4
122
+ version: 0.7.2
123
123
  - !ruby/object:Gem::Dependency
124
- name: paru
124
+ name: pandoc-ruby
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - '='
128
128
  - !ruby/object:Gem::Version
129
- version: 0.4.0.1
129
+ version: 2.1.4
130
130
  type: :runtime
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - '='
135
135
  - !ruby/object:Gem::Version
136
- version: 0.4.0.1
136
+ version: 2.1.4
137
137
  - !ruby/object:Gem::Dependency
138
- name: recursive-open-struct
138
+ name: paru
139
139
  requirement: !ruby/object:Gem::Requirement
140
140
  requirements:
141
141
  - - '='
142
142
  - !ruby/object:Gem::Version
143
- version: 1.1.1
143
+ version: 0.4.0.1
144
144
  type: :runtime
145
145
  prerelease: false
146
146
  version_requirements: !ruby/object:Gem::Requirement
147
147
  requirements:
148
148
  - - '='
149
149
  - !ruby/object:Gem::Version
150
- version: 1.1.1
150
+ version: 0.4.0.1
151
151
  description:
152
152
  email: jumanjiman@gmail.com
153
153
  executables:
@@ -174,6 +174,10 @@ files:
174
174
  - schemas/v1.0.1/component.yaml
175
175
  - schemas/v1.0.1/config.yaml
176
176
  - schemas/v1.0.1/standard.yaml
177
+ - schemas/v1.0.2/certification.yaml
178
+ - schemas/v1.0.2/component.yaml
179
+ - schemas/v1.0.2/config.yaml
180
+ - schemas/v1.0.2/standard.yaml
177
181
  - templates/ssp.erb
178
182
  homepage: https://github.com/jumanjiman/octool
179
183
  licenses: