octool 0.0.5 → 0.0.10

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: 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: