@@ -25,6 +25,7 @@ import sys
25
25
import pathlib
26
26
import xml .etree .ElementTree as ET
27
27
import xml .dom .minidom
28
+ import json
28
29
29
30
30
31
NS = "http://checklists.nist.gov/xccdf/1.2"
@@ -64,6 +65,8 @@ class Tailoring:
64
65
self .value_changes = []
65
66
self .rules_to_select = []
66
67
self .rules_to_unselect = []
68
+ self .groups_to_select = []
69
+ self .groups_to_unselect = []
67
70
self ._rule_refinements = collections .defaultdict (dict )
68
71
self ._value_refinements = collections .defaultdict (dict )
69
72
@@ -187,9 +190,23 @@ class Tailoring:
187
190
def _full_rule_id (self , string ):
188
191
return self ._full_id (string , "rule" )
189
192
193
+ def _full_group_id (self , string ):
194
+ return self ._full_id (string , "group" )
195
+
190
196
def add_value_change (self , varname , value ):
191
197
self .value_changes .append ((varname , value ))
192
198
199
+ def _add_group_select_operations (self , container_element ):
200
+ for group_id in self .groups_to_select :
201
+ change = ET .SubElement (container_element , "{%s}select" % NS )
202
+ change .set ("idref" , self ._full_group_id (group_id ))
203
+ change .set ("selected" , "true" )
204
+
205
+ for group_id in self .groups_to_unselect :
206
+ change = ET .SubElement (container_element , "{%s}select" % NS )
207
+ change .set ("idref" , self ._full_group_id (group_id ))
208
+ change .set ("selected" , "false" )
209
+
193
210
def _add_rule_select_operations (self , container_element ):
194
211
for rule_id in self .rules_to_select :
195
212
change = ET .SubElement (container_element , "{%s}select" % NS )
@@ -246,6 +263,7 @@ class Tailoring:
246
263
title .set ("override" , "false" )
247
264
title .text = self .profile_title
248
265
266
+ self ._add_group_select_operations (profile )
249
267
self ._add_rule_select_operations (profile )
250
268
self ._add_value_overrides (profile )
251
269
self .rule_refinements_to_xml (profile )
@@ -257,12 +275,12 @@ class Tailoring:
257
275
f .write (pretty_xml )
258
276
259
277
def import_json_tailoring (self , json_tailoring ):
260
- import json
261
278
with open (json_tailoring , "r" ) as jf :
262
279
all_tailorings = json .load (jf )
263
280
264
281
if 'profiles' in all_tailorings and all_tailorings ['profiles' ]:
265
- # We currently support tailoring of one profile only
282
+ if len (all_tailorings ['profiles' ]) > 1 :
283
+ raise ValueError ("The autotailor tool currently does not support multi-profile JSON tailoring." )
266
284
tailoring = all_tailorings ['profiles' ][0 ]
267
285
else :
268
286
raise ValueError ("JSON Tailoring does not define any profiles." )
@@ -272,6 +290,14 @@ class Tailoring:
272
290
self .profile_id = tailoring .get ("id" , self .profile_id )
273
291
self .profile_title = tailoring .get ("title" , self .profile_title )
274
292
293
+ if "groups" in tailoring :
294
+ for group_id , props in tailoring ["groups" ].items ():
295
+ if "evaluate" in props :
296
+ if props ["evaluate" ]:
297
+ self .groups_to_select .append (group_id )
298
+ else :
299
+ self .groups_to_unselect .append (group_id )
300
+
275
301
if "rules" in tailoring :
276
302
for rule_id , props in tailoring ["rules" ].items ():
277
303
if "evaluate" in props :
@@ -304,7 +330,7 @@ def get_parser():
304
330
"either its full ID, or the suffix, in which case the "
305
331
"'xccdf_<id-namespace>_profile' prefix will be prepended internally." )
306
332
parser .add_argument (
307
- "--json-tailoring" , metavar = "JSON_TAILORING_FILENAME" , default = "" ,
333
+ "-j" , "- -json-tailoring" , metavar = "JSON_TAILORING_FILENAME" , default = "" ,
308
334
help = "JSON Tailoring (https://github.com/ComplianceAsCode/schemas/blob/main/tailoring/schema.json) "
309
335
"filename." )
310
336
parser .add_argument (
0 commit comments