def append(element, newchild): """Append new child ONLY if it's not a duplicate""" global element_child_cache newid = newchild.get("id") existing = element_child_cache[element].get(newid, None) if existing is not None: # ID is identical and OVAL entities are identical if oval_entities_are_identical(existing, newchild): # Moreover the entity is OVAL <external_variable> if oval_entity_is_extvar(newchild): # If OVAL entity is identical to some already included # in the benchmark and represents an OVAL <external_variable> # it's safe to ignore this ID (since external variables are # in multiple checks just to notify 'testoval.py' helper to # substitute the ID with <local_variable> entity when testing # the OVAL for the rule) pass # Some other OVAL entity else: # If OVAL entity is identical, but not external_variable, the # implementation should be rewritten each entity to be present # just once sys.stderr.write("ERROR: OVAL ID '%s' is used multiple times " "and should represent the same elements.\n" % (newid)) sys.stderr.write("Rewrite the OVAL checks. Place the identical " "IDs into their own definition and extend " "this definition by it.\n") sys.exit(1) # ID is identical, but OVAL entities are semantically difference => # report and error and exit with failure # Fixes: https://github.com/OpenSCAP/scap-security-guide/issues/1275 else: if not oval_entity_is_extvar(existing) and \ not oval_entity_is_extvar(newchild): # This is an error scenario - since by skipping second # implementation and using the first one for both references, # we might evaluate wrong requirement for the second entity # => report an error and exit with failure in that case # See # https://github.com/OpenSCAP/scap-security-guide/issues/1275 # for a reproducer and what could happen in this case sys.stderr.write("ERROR: it's not possible to use the " + "same ID: %s " % newid + "for two " + "semantically different OVAL entities:\n") sys.stderr.write("First entity %s\n" % ElementTree.tostring(existing)) sys.stderr.write("Second entity %s\n" % ElementTree.tostring(newchild)) sys.stderr.write("Use different ID for the second entity!!!\n") sys.exit(1) else: element.append(newchild) element_child_cache[element][newid] = newchild
def main(): global definitions global tests global objects global states global variables global silent_mode args = parse_options() silent_mode = args.silent_mode oval_version = args.oval_version testfile = args.xmlfile header = oval_generated_header("testoval.py", oval_version, "0.0.1") testfile = find_testfile(testfile) body = read_ovaldefgroup_file(testfile) defname = _add_elements(body, header) if defname is None: print("Error while evaluating oval: defname not set; missing " "definitions section?") sys.exit(1) ovaltree = ET.fromstring(header + footer) # append each major element type, if it has subelements for element in [definitions, tests, objects, states, variables]: if list(element) > 0: ovaltree.append(element) # re-map all the element ids from meaningful names to meaningless # numbers testtranslator = IDTranslator("scap-security-guide.testing") ovaltree = testtranslator.translate(ovaltree) (ovalfile, fname) = tempfile.mkstemp(prefix=defname, suffix=".xml") os.write(ovalfile, ET.tostring(ovaltree)) os.close(ovalfile) if not silent_mode: print("Evaluating with OVAL tempfile: " + fname) print("OVAL Schema Version: %s" % oval_version) print("Writing results to: " + fname + "-results") cmd = "oscap oval eval --results " + fname + "-results " + fname oscap_child = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) cmd_out = oscap_child.communicate()[0] if not silent_mode: print(cmd_out) if oscap_child.returncode != 0: if not silent_mode: print("Error launching 'oscap' command: \n\t" + cmd) sys.exit(2) if 'false' in cmd_out: # at least one from the evaluated OVAL definitions evaluated to # 'false' result, exit with '1' to indicate OVAL scan FAIL result sys.exit(1) # perhaps delete tempfile? definitions = ET.Element("definitions") tests = ET.Element("tests") objects = ET.Element("objects") states = ET.Element("states") variables = ET.Element("variables") # 'false' keyword wasn't found in oscap's command output # exit with '0' to indicate OVAL scan TRUE result sys.exit(0)