@@ -25,6 +25,7 @@ import sys
2525import pathlib
2626import xml .etree .ElementTree as ET
2727import xml .dom .minidom
28+ import json
2829
2930
3031NS = "http://checklists.nist.gov/xccdf/1.2"
@@ -64,6 +65,8 @@ class Tailoring:
6465 self .value_changes = []
6566 self .rules_to_select = []
6667 self .rules_to_unselect = []
68+ self .groups_to_select = []
69+ self .groups_to_unselect = []
6770 self ._rule_refinements = collections .defaultdict (dict )
6871 self ._value_refinements = collections .defaultdict (dict )
6972
@@ -187,9 +190,23 @@ class Tailoring:
187190 def _full_rule_id (self , string ):
188191 return self ._full_id (string , "rule" )
189192
193+ def _full_group_id (self , string ):
194+ return self ._full_id (string , "group" )
195+
190196 def add_value_change (self , varname , value ):
191197 self .value_changes .append ((varname , value ))
192198
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+
193210 def _add_rule_select_operations (self , container_element ):
194211 for rule_id in self .rules_to_select :
195212 change = ET .SubElement (container_element , "{%s}select" % NS )
@@ -246,6 +263,7 @@ class Tailoring:
246263 title .set ("override" , "false" )
247264 title .text = self .profile_title
248265
266+ self ._add_group_select_operations (profile )
249267 self ._add_rule_select_operations (profile )
250268 self ._add_value_overrides (profile )
251269 self .rule_refinements_to_xml (profile )
@@ -257,12 +275,12 @@ class Tailoring:
257275 f .write (pretty_xml )
258276
259277 def import_json_tailoring (self , json_tailoring ):
260- import json
261278 with open (json_tailoring , "r" ) as jf :
262279 all_tailorings = json .load (jf )
263280
264281 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." )
266284 tailoring = all_tailorings ['profiles' ][0 ]
267285 else :
268286 raise ValueError ("JSON Tailoring does not define any profiles." )
@@ -272,6 +290,14 @@ class Tailoring:
272290 self .profile_id = tailoring .get ("id" , self .profile_id )
273291 self .profile_title = tailoring .get ("title" , self .profile_title )
274292
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+
275301 if "rules" in tailoring :
276302 for rule_id , props in tailoring ["rules" ].items ():
277303 if "evaluate" in props :
@@ -304,7 +330,7 @@ def get_parser():
304330 "either its full ID, or the suffix, in which case the "
305331 "'xccdf_<id-namespace>_profile' prefix will be prepended internally." )
306332 parser .add_argument (
307- "--json-tailoring" , metavar = "JSON_TAILORING_FILENAME" , default = "" ,
333+ "-j" , "- -json-tailoring" , metavar = "JSON_TAILORING_FILENAME" , default = "" ,
308334 help = "JSON Tailoring (https://github.com/ComplianceAsCode/schemas/blob/main/tailoring/schema.json) "
309335 "filename." )
310336 parser .add_argument (
0 commit comments