Skip to content

Commit

Permalink
Merge pull request #10085 from jan-cerny/shorthand_to_oval
Browse files Browse the repository at this point in the history
Introduce script shorthand to OVAL
  • Loading branch information
Mab879 authored Jan 25, 2023
2 parents 029e11d + 5e6ca9e commit be88d6a
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 53 deletions.
15 changes: 15 additions & 0 deletions docs/manual/developer/05_tools_and_utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,3 +596,18 @@ Example:
$ ./utils/srg_diff.py --target submission.xlsx --base build/cac_stig_output.xlsx --output build/diff.html -p rhel9
Wrote output to build/diff.html.
```
### Convert shorthand OVAL to a full OVAL - `utils/shorthand_to_oval.py`
This script converts (resolved) shorthand OVAL files to a valid OVAL file.
It can be useful for example when creating minimal bug reproducers.
If you know that a problem is located in OVAL for a specific rule, you can use it to convert a resolved shorthand OVAL files from the `build/product/checks/oval` directory to a standalone valid OVAL file that you can then pass to `oscap`.
Example:
```bash
$ ./build_product rhel9
$ utils/shorthand_to_oval.py build/rhel9/checks/oval/accounts_tmout.xml oval.xml
$ oscap oval eval oval.xml
```
31 changes: 30 additions & 1 deletion ssg/build_ovals.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,40 @@
from .constants import oval_footer
from .constants import oval_header
from .constants import MULTI_PLATFORM_LIST
from .id_translate import IDTranslator
from .jinja import process_file_with_macros
from .rule_yaml import parse_prodtype
from .rules import get_rule_dir_id, get_rule_dir_ovals, find_rule_dirs_in_paths
from . import utils
from .xml import ElementTree
from .xml import ElementTree, oval_generated_header


def _create_subtree(shorthand_tree, category):
parent_tag = "{%s}%ss" % (oval_ns, category)
parent = ElementTree.Element(parent_tag)
for node in shorthand_tree.findall(".//{%s}def-group/*" % oval_ns):
if node.tag is ElementTree.Comment:
continue
elif node.tag.endswith(category):
append(parent, node)
return parent


def expand_shorthand(shorthand_path, oval_path, env_yaml):
shorthand_file_content = process_file_with_macros(shorthand_path, env_yaml)
wrapped_shorthand = (oval_header + shorthand_file_content + oval_footer)
shorthand_tree = ElementTree.fromstring(wrapped_shorthand.encode("utf-8"))
header = oval_generated_header("test", "5.11", "1.0")
skeleton = header + oval_footer
root = ElementTree.fromstring(skeleton.encode("utf-8"))
for category in ["definition", "test", "object", "state", "variable"]:
subtree = _create_subtree(shorthand_tree, category)
if list(subtree):
root.append(subtree)
id_translator = IDTranslator("test")
root_translated = id_translator.translate(root)

ElementTree.ElementTree(root_translated).write(oval_path)


def _check_is_applicable_for_product(oval_check_def, product):
Expand Down
6 changes: 6 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ add_test(
)
set_tests_properties("stable-profile-ids" PROPERTIES LABELS quick)

add_test(
NAME "shorthand-to-oval"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/utils/shorthand_to_oval.py" "${CMAKE_CURRENT_SOURCE_DIR}/data/utils/shorthand_oval.xml" "${CMAKE_CURRENT_BINARY_DIR}/oval.xml"
)
set_tests_properties("shorthand-to-oval" PROPERTIES LABELS quick)

add_test(
NAME "stable-profiles"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/test_profile_stability.py" "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/data/profile_stability"
Expand Down
22 changes: 22 additions & 0 deletions tests/data/utils/shorthand_oval.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<def-group>
<definition class="compliance" id="no_empty_passwords" version="1">
<metadata>
<title>Prevent Login to Accounts With Empty Password</title>
<affected family="unix">
<platform>multi_platform_all</platform>
</affected>
<description>The file /etc/pam.d/system-auth should not contain the nullok option</description>
</metadata>
<criteria>
<criterion comment="make sure the nullok option is not used in /etc/pam.d/system-auth" test_ref="test_no_empty_passwords"/>
</criteria>
</definition>
<ind:textfilecontent54_test check="all" check_existence="none_exist" version="1" id="test_no_empty_passwords" comment="make sure nullok is not used in /etc/pam.d/system-auth">
<ind:object object_ref="object_no_empty_passwords"/>
</ind:textfilecontent54_test>
<ind:textfilecontent54_object id="object_no_empty_passwords" version="1">
<ind:filepath operation="pattern match">^/etc/pam.d/(system|password)-auth$</ind:filepath>
<ind:pattern operation="pattern match">^[^#]*\bnullok\b.*$</ind:pattern>
<ind:instance datatype="int">1</ind:instance>
</ind:textfilecontent54_object>
</def-group>
54 changes: 2 additions & 52 deletions tests/oval_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,57 +27,6 @@ def __init__(self, verbose):
self.result = True
self.verbose = verbose

def _expand_shorthand(self, shorthand_path, oval_path, env_yaml):
shorthand_file_content = ssg.jinja.process_file_with_macros(
shorthand_path, env_yaml)
wrapped_shorthand = (ssg.constants.oval_header +
shorthand_file_content +
ssg.constants.oval_footer)
shorthand_tree = ssg.xml.ElementTree.fromstring(
wrapped_shorthand.encode("utf-8"))

definitions = ssg.xml.ElementTree.Element(
"{%s}definitions" % ssg.constants.oval_namespace)
tests = ssg.xml.ElementTree.Element(
"{%s}tests" % ssg.constants.oval_namespace)
objects = ssg.xml.ElementTree.Element(
"{%s}objects" % ssg.constants.oval_namespace)
states = ssg.xml.ElementTree.Element(
"{%s}states" % ssg.constants.oval_namespace)
variables = ssg.xml.ElementTree.Element(
"{%s}variables" % ssg.constants.oval_namespace)
for childnode in shorthand_tree.findall(".//{%s}def-group/*" %
ssg.constants.oval_namespace):
if childnode.tag is ssg.xml.ElementTree.Comment:
continue
elif childnode.tag.endswith("definition"):
ssg.build_ovals.append(definitions, childnode)
elif childnode.tag.endswith("_test"):
ssg.build_ovals.append(tests, childnode)
elif childnode.tag.endswith("_object"):
ssg.build_ovals.append(objects, childnode)
elif childnode.tag.endswith("_state"):
ssg.build_ovals.append(states, childnode)
elif childnode.tag.endswith("_variable"):
ssg.build_ovals.append(variables, childnode)
else:
sys.stderr.write(
"Warning: Unknown element '%s'\n" % (childnode.tag))

header = ssg.xml.oval_generated_header("test", "5.11", "1.0")
skeleton = header + ssg.constants.oval_footer
root = ssg.xml.ElementTree.fromstring(skeleton.encode("utf-8"))
root.append(definitions)
root.append(tests)
root.append(objects)
root.append(states)
if list(variables):
root.append(variables)
id_translator = ssg.id_translate.IDTranslator("test")
root_translated = id_translator.translate(root)

ssg.xml.ElementTree.ElementTree(root_translated).write(oval_path)

def _get_result(self, oscap_output):
pattern = re.compile(
r"^Definition oval:[A-Za-z0-9_\-\.]+:def:[1-9][0-9]*: (\w+)$")
Expand All @@ -100,7 +49,8 @@ def _create_oval(self, oval_content, config_file_path, tmp_dir):
with open(shorthand_path, "w") as f:
f.write(oval_content)
oval_path = os.path.join(tmp_dir, "oval.xml")
self._expand_shorthand(shorthand_path, oval_path, self.mock_env_yaml)
ssg.build_ovals.expand_shorthand(
shorthand_path, oval_path, self.mock_env_yaml)
return oval_path

def _evaluate_oval(self, oval_path):
Expand Down
20 changes: 20 additions & 0 deletions utils/shorthand_to_oval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/python3

import argparse
import os

import ssg.build_ovals


def main():
parser = argparse.ArgumentParser(
description="Convert shorthand OVAL file to a valid full OVAL.")
parser.add_argument("input", help="Input shorthand OVAL file")
parser.add_argument("output", help="Output OVAL file")
args = parser.parse_args()
env_yaml = {"rule_id": os.path.basename(args.input)}
ssg.build_ovals.expand_shorthand(args.input, args.output, env_yaml)


if __name__ == "__main__":
main()

0 comments on commit be88d6a

Please sign in to comment.