13
13
module InspecTools
14
14
# Methods for converting from XLS to various formats
15
15
class XLSXTool
16
+ CIS_2_NIST_XLSX = Roo ::Spreadsheet . open ( File . join ( File . dirname ( __FILE__ ) , '../data/NIST_Map_02052020_CIS_Controls_Version_7.1_Implementation_Groups_1.2.xlsx' ) )
17
+ LATEST_NIST_REV = 'Rev_4' . freeze
18
+
16
19
def initialize ( xlsx , mapping , name , verbose = false )
17
20
@name = name
18
21
@xlsx = xlsx
19
22
@mapping = mapping
20
23
@verbose = verbose
24
+ @cis_to_nist = get_cis_to_nist_control_mapping ( CIS_2_NIST_XLSX )
21
25
end
22
26
23
27
def to_ckl
@@ -41,6 +45,18 @@ def to_inspec(control_prefix)
41
45
42
46
private
43
47
48
+ def get_cis_to_nist_control_mapping ( spreadsheet )
49
+ cis_to_nist = { }
50
+ spreadsheet . sheet ( 3 ) . each do |row |
51
+ if row [ 3 ] . is_a? Numeric
52
+ cis_to_nist [ row [ 3 ] . to_s ] = row [ 0 ]
53
+ else
54
+ cis2Nist [ row [ 2 ] . to_s ] = row [ 0 ] unless ( row [ 2 ] == '' ) || row [ 2 ] . to_i . nil?
55
+ end
56
+ end
57
+ cis_to_nist
58
+ end
59
+
44
60
def insert_json_metadata
45
61
@profile [ 'name' ] = @name
46
62
@profile [ 'title' ] = 'InSpec Profile'
@@ -59,67 +75,73 @@ def insert_json_metadata
59
75
end
60
76
61
77
def parse_cis_controls ( control_prefix )
62
- cis2NistXls = Roo ::Spreadsheet . open ( File . join ( File . dirname ( __FILE__ ) , "../data/NIST_Map_02052020_CIS_Controls_Version_7.1_Implementation_Groups_1.2.xlsx" ) )
63
- cis2Nist = { }
64
- cis2NistXls . sheet ( 3 ) . each do |row |
65
- if row [ 3 ] . is_a? Numeric
66
- cis2Nist [ row [ 3 ] . to_s ] = row [ 0 ]
67
- else
68
- cis2Nist [ row [ 2 ] . to_s ] = row [ 0 ] unless ( row [ 2 ] == "" ) || ( row [ 2 ] . to_i . nil? )
69
- end
70
- end
71
- [ 1 , 2 ] . each do |level |
78
+ [ 1 , 2 ] . each do |level |
72
79
@xlsx . sheet ( level ) . each_row_streaming do |row |
73
80
if row [ @mapping [ 'control.id' ] ] . nil? || !/^\d +(\. ?\d )*$/ . match ( row [ @mapping [ 'control.id' ] ] . formatted_value )
74
81
next
75
82
end
83
+
76
84
tag_pos = @mapping [ 'control.tags' ]
77
85
control = { }
78
86
control [ 'tags' ] = { }
79
- control [ 'id' ] = control_prefix + '-' + row [ @mapping [ 'control.id' ] ] . formatted_value unless @mapping [ 'control.id' ] . nil? || row [ @mapping [ 'control.id' ] ] . nil?
80
- control [ 'title' ] = row [ @mapping [ 'control.title' ] ] . formatted_value unless @mapping [ 'control.title' ] . nil? || row [ @mapping [ 'control.title' ] ] . nil?
81
- control [ 'desc' ] = ""
82
- control [ 'desc' ] = row [ @mapping [ 'control.desc' ] ] . formatted_value unless row [ @mapping [ 'control.desc' ] ] . nil?
83
- control [ 'tags' ] [ 'rationale' ] = row [ tag_pos [ 'rationale' ] ] . formatted_value unless row [ tag_pos [ 'rationale' ] ] . empty?
87
+ control [ 'id' ] = control_prefix + '-' + row [ @mapping [ 'control.id' ] ] . formatted_value unless cell_empty? ( @mapping [ 'control.id' ] ) || cell_empty? ( row [ @mapping [ 'control.id' ] ] )
88
+ control [ 'title' ] = row [ @mapping [ 'control.title' ] ] . formatted_value unless cell_empty? ( @mapping [ 'control.title' ] ) || cell_empty? ( row [ @mapping [ 'control.title' ] ] )
89
+ control [ 'desc' ] = ''
90
+ control [ 'desc' ] = row [ @mapping [ 'control.desc' ] ] . formatted_value unless cell_empty? ( row [ @mapping [ 'control.desc' ] ] )
91
+ control [ 'tags' ] [ 'rationale' ] = row [ tag_pos [ 'rationale' ] ] . formatted_value unless cell_empty? ( row [ tag_pos [ 'rationale' ] ] )
84
92
85
93
control [ 'tags' ] [ 'severity' ] = level == 1 ? 'medium' : 'high'
86
94
control [ 'impact' ] = Utils ::InspecUtil . get_impact ( control [ 'tags' ] [ 'severity' ] )
87
- control [ 'tags' ] [ 'ref' ] = row [ @mapping [ 'control.ref' ] ] . formatted_value unless @mapping [ 'control.ref' ] . nil? || row [ @mapping [ 'control.ref' ] ] . nil?
95
+ control [ 'tags' ] [ 'ref' ] = row [ @mapping [ 'control.ref' ] ] . formatted_value unless cell_empty? ( @mapping [ 'control.ref' ] ) || cell_empty? ( row [ @mapping [ 'control.ref' ] ] )
88
96
control [ 'tags' ] [ 'cis_level' ] = level unless level . nil?
89
97
90
- unless row [ tag_pos [ 'cis_controls' ] ] . empty?
98
+ unless cell_empty? ( row [ tag_pos [ 'cis_controls' ] ] )
91
99
# cis_control must be extracted from CIS control column via regex
92
- control = handle_cis_tags ( control , row [ tag_pos [ 'cis_controls' ] ] . formatted_value . scan ( /CONTROL:v(\d ) (\d +)\. ?(\d *)/ ) )
100
+ cis_tags_array = row [ tag_pos [ 'cis_controls' ] ] . formatted_value . scan ( /CONTROL:v(\d ) (\d +)\. ?(\d *)/ ) . flatten
101
+ cis_tags = %i( revision section sub_section ) . zip ( cis_tags_array ) . to_h
102
+ control = apply_cis_and_nist_controls ( control , cis_tags )
93
103
end
94
104
95
- control [ 'tags' ] [ 'cis_rid' ] = row [ @mapping [ 'control.id' ] ] . formatted_value unless @mapping [ 'control.id' ] . nil? || row [ @mapping [ 'control.id' ] ] . nil?
96
- control [ 'tags' ] [ 'check' ] = row [ tag_pos [ 'check' ] ] . formatted_value unless tag_pos [ 'check' ] . nil? || row [ tag_pos [ 'check' ] ] . empty?
97
- control [ 'tags' ] [ 'fix' ] = row [ tag_pos [ 'fix' ] ] . formatted_value unless tag_pos [ 'fix' ] . nil? || row [ tag_pos [ 'fix' ] ] . empty?
105
+ control [ 'tags' ] [ 'cis_rid' ] = row [ @mapping [ 'control.id' ] ] . formatted_value unless cell_empty? ( @mapping [ 'control.id' ] ) || cell_empty? ( row [ @mapping [ 'control.id' ] ] )
106
+ control [ 'tags' ] [ 'check' ] = row [ tag_pos [ 'check' ] ] . formatted_value unless cell_empty? ( tag_pos [ 'check' ] ) || cell_empty? ( row [ tag_pos [ 'check' ] ] )
107
+ control [ 'tags' ] [ 'fix' ] = row [ tag_pos [ 'fix' ] ] . formatted_value unless cell_empty? ( tag_pos [ 'fix' ] ) || cell_empty? ( row [ tag_pos [ 'fix' ] ] )
98
108
99
109
@controls << control
100
110
end
101
111
end
102
112
end
103
113
104
- def handle_cis_tags ( control , cis_tags )
105
- control [ 'tags' ] [ 'cis_controls' ] = [ ]
106
- control [ 'tags' ] [ 'nist' ] = [ ]
114
+ def cell_empty? ( cell )
115
+ return cell . empty? if cell . respond_to? ( :empty? )
107
116
108
- cis_tags . each do |cis_tag |
109
- if cis_tag [ 2 ] . nil? || cis_tag [ 2 ] == ""
110
- control [ 'tags' ] [ 'cis_controls' ] << cis_tag [ 1 ] . to_s
111
- control [ 'tags' ] [ 'nist' ] << cis2Nist [ cis_tag [ 1 ] ]
112
- else
113
- control [ 'tags' ] [ 'cis_controls' ] << cis_tag [ 1 ] . to_s + "." + cis_tag [ 2 ] . to_s
114
- control [ 'tags' ] [ 'nist' ] << cis2Nist [ cis_tag [ 1 ] . to_s + "." + cis_tag [ 2 ] . to_s ]
115
- end
116
- end
117
+ cell . nil?
118
+ end
119
+
120
+ def apply_cis_and_nist_controls ( control , cis_tags )
121
+ control [ 'tags' ] [ 'cis_controls' ] , control [ 'tags' ] [ 'nist' ] = [ ] , [ ]
117
122
118
- if not control [ 'tags' ] [ 'nist' ] . nil?
119
- control [ 'tags' ] [ 'nist' ] << "Rev_4"
123
+ if cis_tags [ :sub_section ] . nil? || cis_tags [ :sub_section ] . blank?
124
+ control [ 'tags' ] [ 'cis_controls' ] << cis_tags [ :section ]
125
+ control [ 'tags' ] [ 'nist' ] << get_nist_control_for_cis ( [ cis_tags [ :section ] ] )
126
+ else
127
+ control [ 'tags' ] [ 'cis_controls' ] << "#{ cis_tags [ :section ] } .#{ cis_tags [ :sub_section ] } "
128
+ control [ 'tags' ] [ 'nist' ] << get_nist_control_for_cis ( [ cis_tags [ :section ] , cis_tags [ :sub_section ] ] )
120
129
end
121
- control [ 'tags' ] [ 'cis_controls' ] << "Rev_" + cis_tags . first [ 0 ] unless cis_tags [ 0 ] . nil?
130
+
131
+ control [ 'tags' ] [ 'nist' ] << LATEST_NIST_REV unless control [ 'tags' ] [ 'nist' ] . nil?
132
+ control [ 'tags' ] [ 'cis_controls' ] << "Rev_#{ cis_tags [ :revision ] } " unless cis_tags [ :revision ] . nil?
133
+
122
134
control
123
135
end
136
+
137
+ def get_nist_control_for_cis ( section , sub_section = nil )
138
+ return @cis_to_nist [ section ] if sub_section . nil?
139
+
140
+ @cis_to_nist [ "#{ section } .#{ sub_section } " ]
141
+ end
124
142
end
125
143
end
144
+
145
+ # rubocop:enable Metrics/AbcSize
146
+ # rubocop:enable Metrics/PerceivedComplexity
147
+ # rubocop:enable Metrics/CyclomaticComplexity
0 commit comments