inspec_tools 0.0.0.1.ENOTAG
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +662 -0
- data/LICENSE.md +15 -0
- data/README.md +329 -0
- data/Rakefile +30 -0
- data/exe/inspec_tools +14 -0
- data/lib/data/NIST_Map_02052020_CIS_Controls_Version_7.1_Implementation_Groups_1.2.xlsx +0 -0
- data/lib/data/NIST_Map_09212017B_CSC-CIS_Critical_Security_Controls_VER_6.1_Excel_9.1.2016.xlsx +0 -0
- data/lib/data/README.TXT +25 -0
- data/lib/data/U_CCI_List.xml +38403 -0
- data/lib/data/attributes.yml +23 -0
- data/lib/data/cci2html.xsl +136 -0
- data/lib/data/mapping.yml +17 -0
- data/lib/data/stig.csv +1 -0
- data/lib/data/threshold.yaml +83 -0
- data/lib/exceptions/impact_input_error.rb +6 -0
- data/lib/exceptions/severity_input_error.rb +6 -0
- data/lib/happy_mapper_tools/benchmark.rb +161 -0
- data/lib/happy_mapper_tools/cci_attributes.rb +66 -0
- data/lib/happy_mapper_tools/stig_attributes.rb +196 -0
- data/lib/happy_mapper_tools/stig_checklist.rb +99 -0
- data/lib/inspec_tools.rb +17 -0
- data/lib/inspec_tools/ckl.rb +20 -0
- data/lib/inspec_tools/cli.rb +31 -0
- data/lib/inspec_tools/csv.rb +101 -0
- data/lib/inspec_tools/help.rb +9 -0
- data/lib/inspec_tools/help/compliance.md +7 -0
- data/lib/inspec_tools/help/csv2inspec.md +5 -0
- data/lib/inspec_tools/help/inspec2ckl.md +5 -0
- data/lib/inspec_tools/help/inspec2csv.md +5 -0
- data/lib/inspec_tools/help/inspec2xccdf.md +5 -0
- data/lib/inspec_tools/help/pdf2inspec.md +6 -0
- data/lib/inspec_tools/help/summary.md +5 -0
- data/lib/inspec_tools/help/xccdf2inspec.md +5 -0
- data/lib/inspec_tools/inspec.rb +331 -0
- data/lib/inspec_tools/pdf.rb +136 -0
- data/lib/inspec_tools/plugin.rb +15 -0
- data/lib/inspec_tools/plugin_cli.rb +278 -0
- data/lib/inspec_tools/summary.rb +126 -0
- data/lib/inspec_tools/version.rb +8 -0
- data/lib/inspec_tools/xccdf.rb +155 -0
- data/lib/inspec_tools/xlsx_tool.rb +148 -0
- data/lib/inspec_tools_plugin.rb +7 -0
- data/lib/overrides/false_class.rb +5 -0
- data/lib/overrides/nil_class.rb +5 -0
- data/lib/overrides/object.rb +5 -0
- data/lib/overrides/string.rb +5 -0
- data/lib/overrides/true_class.rb +5 -0
- data/lib/utilities/csv_util.rb +14 -0
- data/lib/utilities/extract_nist_cis_mapping.rb +57 -0
- data/lib/utilities/extract_pdf_text.rb +20 -0
- data/lib/utilities/inspec_util.rb +435 -0
- data/lib/utilities/parser.rb +373 -0
- data/lib/utilities/text_cleaner.rb +69 -0
- metadata +359 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'happymapper'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
# rubocop:disable Naming/ClassAndModuleCamelCase
|
7
|
+
|
8
|
+
module HappyMapperTools
|
9
|
+
module CCIAttributes
|
10
|
+
class Reference
|
11
|
+
include HappyMapper
|
12
|
+
tag 'reference'
|
13
|
+
|
14
|
+
attribute :creator, String, tag: 'creator'
|
15
|
+
attribute :title, String, tag: 'title'
|
16
|
+
attribute :version, String, tag: 'version'
|
17
|
+
attribute :location, String, tag: 'location'
|
18
|
+
attribute :index, String, tag: 'index'
|
19
|
+
end
|
20
|
+
|
21
|
+
class CCI_Item
|
22
|
+
include HappyMapper
|
23
|
+
tag 'cci_item'
|
24
|
+
|
25
|
+
attribute :id, String, tag: 'id'
|
26
|
+
element :status, String, tag: 'status'
|
27
|
+
element :publishdate, String, tag: 'publishdate'
|
28
|
+
element :contributor, String, tag: 'contributor'
|
29
|
+
element :definition, String, tag: 'definition'
|
30
|
+
element :type, String, tag: 'type'
|
31
|
+
has_many :references, Reference, xpath: 'xmlns:references'
|
32
|
+
end
|
33
|
+
|
34
|
+
class Metadata
|
35
|
+
include HappyMapper
|
36
|
+
tag 'metadata'
|
37
|
+
|
38
|
+
element :version, String, tag: 'version'
|
39
|
+
element :publishdate, String, tag: 'publishdate'
|
40
|
+
end
|
41
|
+
|
42
|
+
class CCI_List
|
43
|
+
include HappyMapper
|
44
|
+
tag 'cci_list'
|
45
|
+
|
46
|
+
attribute :xsi, String, tag: 'xsi', namespace: 'xmlns'
|
47
|
+
attribute :schemaLocation, String, tag: 'schemaLocation', namespace: 'xmlns'
|
48
|
+
has_one :metadata, Metadata, tag: 'metadata'
|
49
|
+
has_many :cci_items, CCI_Item, xpath: 'xmlns:cci_items'
|
50
|
+
|
51
|
+
def fetch_nists(ccis)
|
52
|
+
ccis = [ccis] unless ccis.is_a?(Array)
|
53
|
+
|
54
|
+
# some of the XCCDF files were having CCE- tags show up which
|
55
|
+
# we don't support, not sure if this is a typo on their part or
|
56
|
+
# we need to see about supporting CCE tags but ... for now
|
57
|
+
filtered_ccis = ccis.select { |f| /CCI-/.match(f) }
|
58
|
+
filtered_ccis.map do |cci|
|
59
|
+
cci_items.find { |item| item.id == cci }.references.max_by(&:version).index
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# rubocop:enable Naming/ClassAndModuleCamelCase
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HappyMapperTools
|
4
|
+
module StigAttributes
|
5
|
+
require 'happymapper'
|
6
|
+
require 'nokogiri'
|
7
|
+
require 'colorize'
|
8
|
+
|
9
|
+
class ContentRef
|
10
|
+
include HappyMapper
|
11
|
+
tag 'check-content-ref'
|
12
|
+
attribute :name, String, tag: 'name'
|
13
|
+
attribute :href, String, tag: 'href'
|
14
|
+
end
|
15
|
+
|
16
|
+
class Check
|
17
|
+
include HappyMapper
|
18
|
+
tag 'check'
|
19
|
+
|
20
|
+
element :content_ref, ContentRef, tag: 'check-content-ref'
|
21
|
+
element :content, String, tag: 'check-content'
|
22
|
+
end
|
23
|
+
|
24
|
+
class Fix
|
25
|
+
include HappyMapper
|
26
|
+
tag 'fix'
|
27
|
+
|
28
|
+
attribute :id, String, tag: 'id'
|
29
|
+
end
|
30
|
+
|
31
|
+
class DescriptionDetails
|
32
|
+
include HappyMapper
|
33
|
+
tag 'Details'
|
34
|
+
|
35
|
+
element :vuln_discussion, String, tag: 'VulnDiscussion'
|
36
|
+
element :false_positives, String, tag: 'FalsePositives'
|
37
|
+
element :false_negatives, String, tag: 'FalseNegatives'
|
38
|
+
element :documentable, Boolean, tag: 'Documentable'
|
39
|
+
element :mitigations, String, tag: 'Mitigations'
|
40
|
+
element :severity_override_guidance, String, tag: 'SeverityOverrideGuidance'
|
41
|
+
element :potential_impacts, String, tag: 'PotentialImpacts'
|
42
|
+
element :third_party_tools, String, tag: 'ThirdPartyTools'
|
43
|
+
element :mitigation_controls, String, tag: 'MitigationControl'
|
44
|
+
element :responsibility, String, tag: 'Responsibility'
|
45
|
+
element :ia_controls, String, tag: 'IAControls'
|
46
|
+
end
|
47
|
+
|
48
|
+
class Description
|
49
|
+
include HappyMapper
|
50
|
+
tag 'description'
|
51
|
+
|
52
|
+
content :details, DescriptionDetails
|
53
|
+
|
54
|
+
detail_tags = %i(vuln_discussion false_positives false_negatives documentable
|
55
|
+
mitigations severity_override_guidance potential_impacts
|
56
|
+
third_party_tools mitigation_controls responsibility ia_controls)
|
57
|
+
|
58
|
+
detail_tags.each do |name|
|
59
|
+
define_method name do
|
60
|
+
details.send(name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class ReferenceInfo
|
66
|
+
include HappyMapper
|
67
|
+
tag 'reference'
|
68
|
+
|
69
|
+
attribute :href, String, tag: 'href'
|
70
|
+
element :dc_publisher, String, tag: 'publisher', namespace: 'dc'
|
71
|
+
element :dc_source, String, tag: 'source', namespace: 'dc'
|
72
|
+
element :dc_title, String, tag: 'title', namespace: 'dc'
|
73
|
+
element :dc_type, String, tag: 'type', namespace: 'dc'
|
74
|
+
element :dc_subject, String, tag: 'subject', namespace: 'dc'
|
75
|
+
element :dc_identifier, String, tag: 'identifier', namespace: 'dc'
|
76
|
+
end
|
77
|
+
|
78
|
+
class Rule
|
79
|
+
include HappyMapper
|
80
|
+
tag 'Rule'
|
81
|
+
|
82
|
+
attribute :id, String, tag: 'id'
|
83
|
+
attribute :severity, String, tag: 'severity'
|
84
|
+
element :version, String, tag: 'version'
|
85
|
+
element :title, String, tag: 'title'
|
86
|
+
has_one :description, Description, tag: 'description'
|
87
|
+
element :reference, ReferenceInfo, tag: 'reference'
|
88
|
+
has_many :idents, String, tag: 'ident'
|
89
|
+
element :fixtext, String, tag: 'fixtext'
|
90
|
+
has_one :fix, Fix, tag: 'fix'
|
91
|
+
has_one :check, Check, tag: 'check'
|
92
|
+
end
|
93
|
+
|
94
|
+
class Group
|
95
|
+
include HappyMapper
|
96
|
+
tag 'Group'
|
97
|
+
|
98
|
+
attribute :id, String, tag: 'id'
|
99
|
+
element :title, String, tag: 'title'
|
100
|
+
element :description, String, tag: 'description'
|
101
|
+
has_one :rule, Rule, tag: 'Rule'
|
102
|
+
end
|
103
|
+
|
104
|
+
class ReleaseDate
|
105
|
+
include HappyMapper
|
106
|
+
tag 'status'
|
107
|
+
|
108
|
+
attribute :release_date, String, tag: 'date'
|
109
|
+
end
|
110
|
+
|
111
|
+
class Notice
|
112
|
+
include HappyMapper
|
113
|
+
tag 'notice'
|
114
|
+
attribute :id, String, tag: 'id'
|
115
|
+
attribute :xml_lang, String, namespace: 'xml', tag: 'lang'
|
116
|
+
content :notice, String, tag: 'notice'
|
117
|
+
end
|
118
|
+
|
119
|
+
class Plaintext
|
120
|
+
include HappyMapper
|
121
|
+
tag 'plain-text'
|
122
|
+
attribute :id, String, tag: 'id'
|
123
|
+
content :plaintext, String
|
124
|
+
end
|
125
|
+
|
126
|
+
class Benchmark
|
127
|
+
include HappyMapper
|
128
|
+
tag 'Benchmark'
|
129
|
+
|
130
|
+
has_one :release_date, ReleaseDate, tag: 'status'
|
131
|
+
attribute :id, String, tag: 'id'
|
132
|
+
element :status, String, tag: 'status'
|
133
|
+
element :title, String, tag: 'title'
|
134
|
+
element :description, String, tag: 'description'
|
135
|
+
element :version, String, tag: 'version'
|
136
|
+
element :notice, Notice, tag: 'notice'
|
137
|
+
has_one :reference, ReferenceInfo, tag: 'reference'
|
138
|
+
element :plaintext, Plaintext, tag: 'plain-text'
|
139
|
+
has_many :group, Group, tag: 'Group'
|
140
|
+
end
|
141
|
+
|
142
|
+
class DescriptionDetailsType
|
143
|
+
def self.type
|
144
|
+
DescriptionDetails
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.apply(value) # rubocop:disable Metrics/AbcSize
|
148
|
+
value = value.gsub('&', 'and')
|
149
|
+
DescriptionDetails.parse "<Details>#{value}</Details>"
|
150
|
+
rescue Nokogiri::XML::SyntaxError
|
151
|
+
allowed_tags = %w{VulnDiscussion FalsePositives FalseNegatives Documentable
|
152
|
+
Mitigations SeverityOverrideGuidance PotentialImpacts
|
153
|
+
PotentialImpacts ThirdPartyTools MitigationControl
|
154
|
+
Responsibility IAControls}
|
155
|
+
|
156
|
+
tags_found = value.scan(%r{(?<=<)([^\/]*?)((?= \/>)|(?=>))}).to_a
|
157
|
+
|
158
|
+
tags_found = tags_found.uniq.flatten.reject!(&:empty?)
|
159
|
+
offending_tags = tags_found - allowed_tags
|
160
|
+
|
161
|
+
if offending_tags.count > 1
|
162
|
+
puts "\n\nThe non-standard tags: #{offending_tags.to_s.colorize(:red)}" \
|
163
|
+
' were found in: ' + "\n\n#{value}"
|
164
|
+
else
|
165
|
+
puts "\n\nThe non-standard tag: #{offending_tags.to_s.colorize(:red)}" \
|
166
|
+
' was found in: ' + "\n\n#{value}"
|
167
|
+
end
|
168
|
+
puts "\n\nPlease:\n "
|
169
|
+
option_one = '(1) ' + '(best)'.colorize(:green) + ' Use the ' +
|
170
|
+
'`-r --replace-tags array` '.colorize(:light_yellow) +
|
171
|
+
'(case sensitive) option to replace the offending tags ' \
|
172
|
+
'during processing of the XCCDF ' \
|
173
|
+
'file to use the ' +
|
174
|
+
"`$#{offending_tags[0]}` " .colorize(:light_green) +
|
175
|
+
'syntax in your InSpec profile.'
|
176
|
+
option_two = '(2) Update your XCCDF file to *not use* non-standard XCCDF ' \
|
177
|
+
'elements within ' +
|
178
|
+
'`<`,`>`, `<` '.colorize(:red) +
|
179
|
+
'or '.colorize(:default) +
|
180
|
+
'`>` '.colorize(:red) +
|
181
|
+
'as "placeholders", and use something that doesn\'t confuse ' \
|
182
|
+
'the XML parser, such as : ' +
|
183
|
+
"`$#{offending_tags[0]}`" .colorize(:light_green)
|
184
|
+
puts option_one
|
185
|
+
puts "\n"
|
186
|
+
puts option_two
|
187
|
+
# exit
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.apply?(value, _convert_to_type)
|
191
|
+
value.is_a?(String)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
HappyMapper::SupportedTypes.register DescriptionDetailsType
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'happymapper'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
module HappyMapperTools
|
7
|
+
module StigChecklist
|
8
|
+
# see: https://github.com/dam5s/happymapper
|
9
|
+
# Class Asset maps from the 'Asset' from Checklist XML file using HappyMapper
|
10
|
+
class Asset
|
11
|
+
include HappyMapper
|
12
|
+
tag 'ASSET'
|
13
|
+
element :role, String, tag: 'ROLE'
|
14
|
+
element :type, String, tag: 'ASSET_TYPE'
|
15
|
+
element :host_name, String, tag: 'HOST_NAME'
|
16
|
+
element :host_ip, String, tag: 'HOST_IP'
|
17
|
+
element :host_mac, String, tag: 'HOST_MAC'
|
18
|
+
element :host_guid, String, tag: 'HOST_GUID'
|
19
|
+
element :host_fqdn, String, tag: 'HOST_FQDN'
|
20
|
+
element :tech_area, String, tag: 'TECH_AREA'
|
21
|
+
element :target_key, String, tag: 'TARGET_KEY'
|
22
|
+
element :web_or_database, String, tag: 'WEB_OR_DATABASE'
|
23
|
+
element :web_db_site, String, tag: 'WEB_DB_SITE'
|
24
|
+
element :web_db_instance, String, tag: 'WEB_DB_INSTANCE'
|
25
|
+
end
|
26
|
+
|
27
|
+
# Class Asset maps from the 'SI_DATA' from Checklist XML file using HappyMapper
|
28
|
+
class SiData
|
29
|
+
include HappyMapper
|
30
|
+
tag 'SI_DATA'
|
31
|
+
element :name, String, tag: 'SID_NAME'
|
32
|
+
element :data, String, tag: 'SID_DATA'
|
33
|
+
end
|
34
|
+
|
35
|
+
# Class Asset maps from the 'STIG_INFO' from Checklist XML file using HappyMapper
|
36
|
+
class StigInfo
|
37
|
+
include HappyMapper
|
38
|
+
tag 'STIG_INFO'
|
39
|
+
has_many :si_data, SiData, tag: 'SI_DATA'
|
40
|
+
end
|
41
|
+
|
42
|
+
# Class Asset maps from the 'STIG_DATA' from Checklist XML file using HappyMapper
|
43
|
+
class StigData
|
44
|
+
include HappyMapper
|
45
|
+
|
46
|
+
def initialize(attrib = nil, data = nil)
|
47
|
+
self.attrib = attrib
|
48
|
+
self.data = data
|
49
|
+
end
|
50
|
+
|
51
|
+
tag 'STIG_DATA'
|
52
|
+
has_one :attrib, String, tag: 'VULN_ATTRIBUTE'
|
53
|
+
has_one :data, String, tag: 'ATTRIBUTE_DATA'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Class Asset maps from the 'VULN' from Checklist XML file using HappyMapper
|
57
|
+
class Vuln
|
58
|
+
include HappyMapper
|
59
|
+
tag 'VULN'
|
60
|
+
has_many :stig_data, StigData, tag: 'STIG_DATA'
|
61
|
+
has_one :status, String, tag: 'STATUS'
|
62
|
+
has_one :finding_details, String, tag: 'FINDING_DETAILS'
|
63
|
+
has_one :comments, String, tag: 'COMMENTS'
|
64
|
+
has_one :severity_override, String, tag: 'SEVERITY_OVERRIDE'
|
65
|
+
has_one :severity_justification, String, tag: 'SEVERITY_JUSTIFICATION'
|
66
|
+
end
|
67
|
+
|
68
|
+
# Class Asset maps from the 'iSTIG' from Checklist XML file using HappyMapper
|
69
|
+
class IStig
|
70
|
+
include HappyMapper
|
71
|
+
tag 'iSTIG'
|
72
|
+
has_one :stig_info, StigInfo, tag: 'STIG_INFO'
|
73
|
+
has_many :vuln, Vuln, tag: 'VULN'
|
74
|
+
end
|
75
|
+
|
76
|
+
# Class Asset maps from the 'STIGS' from Checklist XML file using HappyMapper
|
77
|
+
class Stigs
|
78
|
+
include HappyMapper
|
79
|
+
tag 'STIGS'
|
80
|
+
has_one :istig, IStig, tag: 'iSTIG'
|
81
|
+
end
|
82
|
+
|
83
|
+
class Checklist
|
84
|
+
include HappyMapper
|
85
|
+
tag 'CHECKLIST'
|
86
|
+
has_one :asset, Asset, tag: 'ASSET'
|
87
|
+
has_one :stig, Stigs, tag: 'STIGS'
|
88
|
+
|
89
|
+
def where(attrib, data)
|
90
|
+
stig.istig.vuln.each do |vuln|
|
91
|
+
if vuln.stig_data.any? { |element| element.attrib == attrib && element.data == data }
|
92
|
+
# TODO: Handle multiple objects that match the condition
|
93
|
+
return vuln
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/inspec_tools.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(__dir__))
|
2
|
+
require 'inspec_tools/version'
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
module InspecTools
|
6
|
+
autoload :Help, 'inspec_tools/help'
|
7
|
+
autoload :Command, 'inspec_tools/command'
|
8
|
+
autoload :CLI, 'inspec_tools/cli'
|
9
|
+
autoload :XCCDF, 'inspec_tools/xccdf'
|
10
|
+
autoload :PDF, 'inspec_tools/pdf'
|
11
|
+
autoload :CSV, 'inspec_tools/csv'
|
12
|
+
autoload :CKL, 'inspec_tools/ckl'
|
13
|
+
autoload :Inspec, 'inspec_tools/inspec'
|
14
|
+
autoload :Summary, 'inspec_tools/summary'
|
15
|
+
autoload :Threshold, 'inspec_tools/threshold'
|
16
|
+
autoload :XLSXTool, 'inspec_tools/xlsx_tool'
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module InspecTools
|
2
|
+
# Methods for converting from CKL to various formats
|
3
|
+
class CKL
|
4
|
+
def initialize(ckl)
|
5
|
+
@ckl = ckl
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_csv
|
9
|
+
# TODO: to_csv
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_xccdf
|
13
|
+
# TODO: to_xccdf
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_inspec
|
17
|
+
# TODO: to_inspec
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require 'inspec-objects'
|
5
|
+
require 'inspec'
|
6
|
+
require_relative './plugin_cli.rb'
|
7
|
+
|
8
|
+
# This tells the ruby cli app to use the same argument parsing as the plugin
|
9
|
+
module InspecTools
|
10
|
+
CLI = InspecPlugins::InspecToolsPlugin::CliCommand
|
11
|
+
end
|
12
|
+
|
13
|
+
#=====================================================================#
|
14
|
+
# Pre-Flight Code
|
15
|
+
#=====================================================================#
|
16
|
+
help_commands = ['-h', '--help', 'help']
|
17
|
+
log_commands = ['-l', '--log-directory']
|
18
|
+
version_commands = ['-v', '--version', 'version']
|
19
|
+
|
20
|
+
#---------------------------------------------------------------------#
|
21
|
+
# Adjustments for non-required version commands
|
22
|
+
#---------------------------------------------------------------------#
|
23
|
+
unless (version_commands & ARGV).empty?
|
24
|
+
puts InspecTools::VERSION
|
25
|
+
exit 0
|
26
|
+
end
|
27
|
+
|
28
|
+
#---------------------------------------------------------------------#
|
29
|
+
# Adjustments for non-required log-directory
|
30
|
+
#---------------------------------------------------------------------#
|
31
|
+
ARGV.push("--log-directory=#{Dir.pwd}/logs") if (log_commands & ARGV).empty? && (help_commands & ARGV).empty?
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'word_wrap'
|
4
|
+
require 'yaml'
|
5
|
+
require 'digest'
|
6
|
+
|
7
|
+
require_relative '../utilities/inspec_util'
|
8
|
+
|
9
|
+
# rubocop:disable Metrics/AbcSize
|
10
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
11
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
12
|
+
|
13
|
+
module InspecTools
|
14
|
+
# Methods for converting from CSV to various formats
|
15
|
+
class CSVTool
|
16
|
+
def initialize(csv, mapping, name, verbose = false)
|
17
|
+
@name = name
|
18
|
+
@csv = csv
|
19
|
+
@mapping = mapping
|
20
|
+
@verbose = verbose
|
21
|
+
@csv.shift if @mapping['skip_csv_header']
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_ckl
|
25
|
+
# TODO
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_xccdf
|
29
|
+
# TODO
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_inspec
|
33
|
+
@controls = []
|
34
|
+
@cci_xml = nil
|
35
|
+
@profile = {}
|
36
|
+
read_cci_xml
|
37
|
+
insert_json_metadata
|
38
|
+
parse_controls
|
39
|
+
@profile['controls'] = @controls
|
40
|
+
@profile['sha256'] = Digest::SHA256.hexdigest @profile.to_s
|
41
|
+
@profile
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def insert_json_metadata
|
47
|
+
@profile['name'] = @name
|
48
|
+
@profile['title'] = 'InSpec Profile'
|
49
|
+
@profile['maintainer'] = 'The Authors'
|
50
|
+
@profile['copyright'] = 'The Authors'
|
51
|
+
@profile['copyright_email'] = 'you@example.com'
|
52
|
+
@profile['license'] = 'Apache-2.0'
|
53
|
+
@profile['summary'] = 'An InSpec Compliance Profile'
|
54
|
+
@profile['version'] = '0.1.0'
|
55
|
+
@profile['supports'] = []
|
56
|
+
@profile['attributes'] = []
|
57
|
+
@profile['generator'] = {
|
58
|
+
'name': 'inspec_tools',
|
59
|
+
'version': VERSION
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def read_cci_xml
|
64
|
+
cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
|
65
|
+
@cci_xml = Nokogiri::XML(File.open(cci_list_path))
|
66
|
+
@cci_xml.remove_namespaces!
|
67
|
+
rescue StandardError => e
|
68
|
+
puts "Exception: #{e.message}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_nist_reference(cci_number)
|
72
|
+
item_node = @cci_xml.xpath("//cci_list/cci_items/cci_item[@id='#{cci_number}']")[0] unless @cci_xml.nil?
|
73
|
+
unless item_node.nil?
|
74
|
+
nist_ref = item_node.xpath('./references/reference[not(@version <= preceding-sibling::reference/@version) and not(@version <=following-sibling::reference/@version)]/@index').text
|
75
|
+
nist_ver = item_node.xpath('./references/reference[not(@version <= preceding-sibling::reference/@version) and not(@version <=following-sibling::reference/@version)]/@version').text
|
76
|
+
end
|
77
|
+
[nist_ref, nist_ver]
|
78
|
+
end
|
79
|
+
|
80
|
+
def parse_controls
|
81
|
+
@csv.each do |row|
|
82
|
+
print '.'
|
83
|
+
control = {}
|
84
|
+
control['id'] = row[@mapping['control.id']] unless @mapping['control.id'].nil? || row[@mapping['control.id']].nil?
|
85
|
+
control['title'] = row[@mapping['control.title']] unless @mapping['control.title'].nil? || row[@mapping['control.title']].nil?
|
86
|
+
control['desc'] = row[@mapping['control.desc']] unless @mapping['control.desc'].nil? || row[@mapping['control.desc']].nil?
|
87
|
+
control['tags'] = {}
|
88
|
+
nist, nist_rev = get_nist_reference(row[@mapping['control.tags']['cci']]) unless @mapping['control.tags']['cci'].nil? || row[@mapping['control.tags']['cci']].nil?
|
89
|
+
control['tags']['nist'] = [nist, 'Rev_' + nist_rev] unless nist.nil? || nist_rev.nil?
|
90
|
+
@mapping['control.tags'].each do |tag|
|
91
|
+
control['tags'][tag.first.to_s] = row[tag.last] unless row[tag.last].nil?
|
92
|
+
end
|
93
|
+
unless @mapping['control.tags']['severity'].nil? || row[@mapping['control.tags']['severity']].nil?
|
94
|
+
control['impact'] = Utils::InspecUtil.get_impact(row[@mapping['control.tags']['severity']])
|
95
|
+
control['tags']['severity'] = Utils::InspecUtil.get_impact_string(control['impact'])
|
96
|
+
end
|
97
|
+
@controls << control
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|