Skip to content

Commit

Permalink
autotailor: Add groups override to JSON tailoring
Browse files Browse the repository at this point in the history
Also fix some small issues.
  • Loading branch information
evgenyz committed Feb 26, 2024
1 parent 4112838 commit 73e6fea
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 21 deletions.
4 changes: 2 additions & 2 deletions tests/utils/autotailor_integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ assert_exists 1 '/Benchmark/TestResult/set-value[@idref="xccdf_com.example.www_v
assert_exists 1 '/Benchmark/TestResult/set-value[@idref="xccdf_com.example.www_value_V2" and text()="Some Value"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R1"]/result[text()="notselected"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R2"]/result[text()="pass"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3"]/result[text()="notchecked"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3" and @role="unchecked"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3"]/result[text()="notselected"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R3" and @severity="unknown"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R4"]/result[text()="notselected"]'
assert_exists 1 '/Benchmark/TestResult/rule-result[@idref="xccdf_com.example.www_rule_R4" and @role="unchecked"]'
10 changes: 9 additions & 1 deletion tests/utils/custom.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@
"id": "JSON_P1",
"title": "JSON Tailored Profile P1",
"base_profile_id": "P1",
"groups": {
"G34": {
"evaluate": false
}
},
"rules": {
"R1": {
"evaluate": false
},
"R3": {
"evaluate": true,
"role": "unchecked",
"severity": "unknown"
},
"R4": {
"evaluate": true,
"role": "unchecked"
}
},
"variables": {
Expand Down
34 changes: 19 additions & 15 deletions tests/utils/data_stream.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,24 @@
<check-content-ref href="test_single_rule.oval.xml" name="oval:x:def:1"/>
</check>
</Rule>
<Rule selected="false" id="xccdf_com.example.www_rule_R3">
<title>Rule R3</title>
<description>Description</description>
<check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
<check-content-ref href="test_single_rule.oval.xml" name="oval:x:def:1"/>
</check>
</Rule>
<Rule selected="false" id="xccdf_com.example.www_rule_R4">
<title>Rule R4</title>
<description>Description</description>
<check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
<check-content-ref href="test_single_rule.oval.xml" name="oval:x:def:1"/>
</check>
</Rule>
</Benchmark>
<Group selected="true" id="xccdf_com.example.www_group_G34">
<title>group R3, R4</title>
<description>description</description>
<Rule selected="false" id="xccdf_com.example.www_rule_R3">
<title>Rule R3</title>
<description>Description</description>
<check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
<check-content-ref href="test_single_rule.oval.xml" name="oval:x:def:1"/>
</check>
</Rule>
<Rule selected="false" id="xccdf_com.example.www_rule_R4">
<title>Rule R4</title>
<description>Description</description>
<check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
<check-content-ref href="test_single_rule.oval.xml" name="oval:x:def:1"/>
</check>
</Rule>
</Group>
</Benchmark>
</ds:component>
</ds:data-stream-collection>
32 changes: 29 additions & 3 deletions utils/autotailor
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import sys
import pathlib
import xml.etree.ElementTree as ET
import xml.dom.minidom
import json


NS = "http://checklists.nist.gov/xccdf/1.2"
Expand Down Expand Up @@ -64,6 +65,8 @@ class Tailoring:
self.value_changes = []
self.rules_to_select = []
self.rules_to_unselect = []
self.groups_to_select = []
self.groups_to_unselect = []
self._rule_refinements = collections.defaultdict(dict)
self._value_refinements = collections.defaultdict(dict)

Expand Down Expand Up @@ -187,9 +190,23 @@ class Tailoring:
def _full_rule_id(self, string):
return self._full_id(string, "rule")

def _full_group_id(self, string):
return self._full_id(string, "group")

def add_value_change(self, varname, value):
self.value_changes.append((varname, value))

def _add_group_select_operations(self, container_element):
for group_id in self.groups_to_select:
change = ET.SubElement(container_element, "{%s}select" % NS)
change.set("idref", self._full_group_id(group_id))
change.set("selected", "true")

for group_id in self.groups_to_unselect:
change = ET.SubElement(container_element, "{%s}select" % NS)
change.set("idref", self._full_group_id(group_id))
change.set("selected", "false")

def _add_rule_select_operations(self, container_element):
for rule_id in self.rules_to_select:
change = ET.SubElement(container_element, "{%s}select" % NS)
Expand Down Expand Up @@ -246,6 +263,7 @@ class Tailoring:
title.set("override", "false")
title.text = self.profile_title

self._add_group_select_operations(profile)
self._add_rule_select_operations(profile)
self._add_value_overrides(profile)
self.rule_refinements_to_xml(profile)
Expand All @@ -257,12 +275,12 @@ class Tailoring:
f.write(pretty_xml)

def import_json_tailoring(self, json_tailoring):
import json
with open(json_tailoring, "r") as jf:
all_tailorings = json.load(jf)

if 'profiles' in all_tailorings and all_tailorings['profiles']:
# We currently support tailoring of one profile only
if len(all_tailorings['profiles']) > 1:
raise ValueError("The autotailor tool currently does not support multi-profile JSON tailoring.")
tailoring = all_tailorings['profiles'][0]
else:
raise ValueError("JSON Tailoring does not define any profiles.")
Expand All @@ -272,6 +290,14 @@ class Tailoring:
self.profile_id = tailoring.get("id", self.profile_id)
self.profile_title = tailoring.get("title", self.profile_title)

if "groups" in tailoring:
for group_id, props in tailoring["groups"].items():
if "evaluate" in props:
if props["evaluate"]:
self.groups_to_select.append(group_id)
else:
self.groups_to_unselect.append(group_id)

if "rules" in tailoring:
for rule_id, props in tailoring["rules"].items():
if "evaluate" in props:
Expand Down Expand Up @@ -304,7 +330,7 @@ def get_parser():
"either its full ID, or the suffix, in which case the "
"'xccdf_<id-namespace>_profile' prefix will be prepended internally.")
parser.add_argument(
"--json-tailoring", metavar="JSON_TAILORING_FILENAME", default="",
"-j", "--json-tailoring", metavar="JSON_TAILORING_FILENAME", default="",
help="JSON Tailoring (https://github.com/ComplianceAsCode/schemas/blob/main/tailoring/schema.json) "
"filename.")
parser.add_argument(
Expand Down

0 comments on commit 73e6fea

Please sign in to comment.