[PATCH] Updated the OVAL audit command to work with OpenSCAP
by Josh Adams
Implemented callback functions etc. using the new probe api in OpenSCAP.
Relies on a patch to OpenSCAP which has not been accepted yet. Also
fixed a few other small problems.
---
src/secstate/main.py | 182 +------------------------------------------------
src/secstate/util.py | 98 +++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 178 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 0e1ab5b..c3b84d7 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -358,80 +358,6 @@ class Secstate:
self.log.debug("Succesfully added %(name)s to target model" % {'name':oscap.oval_definition_get_id(defn)})
return True
- def create_result_model(self, def_model):
- if def_model == None:
- self.log.error("No definition model specified")
- return None
-
- sys_model = oscap.oval_syschar_model_new(def_model)
- if sys_model == None:
- self.log.error("Error creating system characteristics model")
- return None
-
- sess = oscap.oval_probe_session_new(sys_model)
- oscap.oval_psess_probe_sysinfo(sess)
- oscap.oval_psess_probe_objects(sess)
-
- res_model = oscap.oval_results_model_new(def_model, [sys_model, None])
- if res_model == None:
- self.log.error("Error creating results model")
- oscap.oval_syschar_model_free(sys_model)
- return None
-
- return res_model
-
- def create_result_directives(self, res_model):
- res_direct = oscap.oval_result_directives_new(res_model)
- if res_direct == None:
- self.log.error("Error creating directives")
- oscap.oval_syschar_model_free(sys_model)
- oscap.oval_results_model_free(res_model)
- return None
-
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_INVALID, 1)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_TRUE, 1)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_FALSE, 1)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_UNKNOWN, 1)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_ERROR, 1)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_NOT_EVALUATED, 1)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_NOT_APPLICABLE, 1)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_FALSE, oscap.OVAL_DIRECTIVE_CONTENT_FULL)
- oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_TRUE, oscap.OVAL_DIRECTIVE_CONTENT_FULL)
-
- return res_direct
-
- def output_results(self, res_model, res_directives, xml, html):
- if xml:
- if not oscap.oval_results_model_export(res_model, res_directives, xml):
- self.log.error("Error exporting results to %(file)s" % {'file':xml})
- return False
-
- if html:
- tmp = tempfile.mktemp()
- if not oscap.oval_results_model_export(res_model, res_directives, tmp):
- self.log.error("Error exporting results to %(file)s" % {'file':html})
- return False
-
- import libxml2
- import libxslt
-
- styledoc = libxml2.parseFile(self.config.get('secstate', 'results_stylesheet'))
- style = libxslt.parseStylesheetDoc(styledoc)
- doc = libxml2.parseFile(tmp)
- result = style.applyStylesheet(doc, None)
- if not style.saveResultToFilename(html, result, 0):
- self.log.error("Error exporting results to %(file)s" % {'file':html})
- style.freeStylesheet()
- doc.freeDoc()
- result.freeDoc()
- return False
-
- style.freeStylesheet()
- doc.freeDoc()
- result.freeDoc()
-
- return True
-
def audit(self, interpreter, args, oval_only, def_models=None, verbose=False, xml=None, html=None):
"""
Function: Run an audit on the system agains the given definition model
@@ -455,17 +381,8 @@ class Secstate:
oscap.oval_definition_model_free(tmp_model)
if interpreter == "openscap":
- res_model = self.create_result_model(def_model)
- oscap.oval_results_model_eval(res_model)
-
- (def_results, test_results) = self.get_results(res_model)
- self.parse_results(def_results, test_results)
-
- if xml or html:
- res_direct = self.create_result_directives(res_model)
- self.output_results(res_model, res_direct, xml, html)
-
- oscap.oval_definition_model_free(def_model)
+ sess = oscap.oval_agent_new_session(def_model)
+ return evaluate_oval(def_model, sess, xml, html, verbose)
else:
self.log.error("Unsupported interpreter specified: %(inter)s" % {'inter':interpreter})
@@ -489,97 +406,6 @@ class Secstate:
sess = oscap.oval_agent_new_session(def_model)
return evaluate_xccdf(benchmark, def_model, benchmark_path, sess, f_xml=xml, f_html=html)
- def get_result_definitions(self, result_model):
- """
- Function: Get all definition results from an OVAL results model
- Input: oval_results_model
- Output: list of oval_result_definition's
- """
- definitions = []
- # FIXME: This might be unsafe as we are accessing iterators without calling has_more
- system = oscap.oval_result_system_iterator_next(oscap.oval_results_model_get_systems(result_model))
- defs = oscap.oval_result_system_get_definitions(system)
- for definition in oval_result_definition_generator(defs):
- definitions.append(definition)
-
- return definitions
-
- def get_result_tests(self, result_model):
- """
- Function: Get all test results from an OVAL results model
- Input: oval_results_model
- Output: list of oval_result_test's
- """
- test_results = []
- system = oscap.oval_result_system_iterator_next(oscap.oval_results_model_get_systems(result_model))
- tests = oscap.oval_result_system_get_tests(system)
- for result in oval_result_test_generator(tests):
- test_results.append(result)
-
- return test_results
-
- def get_results(self, target_model):
- """
- Function: Get both results and tests from an OVAL test model, mapped to their corresponding result
- Input: oval_results_model
- Output: Retuns two dicts with definitions and tests mapped to the result of the definition or test
- """
- def_results = {}
- defs = self.get_result_definitions(target_model)
- for defn in defs:
- result = oscap.oval_result_definition_get_result(defn)
- if def_results.has_key(result):
- def_results[result].append(defn)
- else:
- def_results[result] = [defn]
-
- test_results = {}
- tests = self.get_result_tests(target_model)
- for defn in tests:
- result = oscap.oval_result_test_get_result(defn)
- if test_results.has_key(result):
- test_results[result].append(defn)
- else:
- test_results[result] = [defn]
-
- return def_results, test_results
-
- def parse_results(self, definitions, tests = None):
- """
- Function: Parse the results and print out information regarding the results
- Input: Two dicts with oval_result_t as key to oval_result_definition/test
- Output: None
- Side Effects: Prints out the number of definitions/tests that passed or failed.
- """
- if definitions.has_key(oscap.OVAL_RESULT_INVALID):
- print "%d definition(s) were invalid" % definitions[oscap.OVAL_RESULT_INVALID].__len__()
- for defn in definitions[oscap.OVAL_RESULT_INVALID]:
- print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
- if definitions.has_key(oscap.OVAL_RESULT_TRUE):
- print "%d definition(s) passed" % definitions[oscap.OVAL_RESULT_TRUE].__len__()
- for defn in definitions[oscap.OVAL_RESULT_TRUE]:
- print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
- if definitions.has_key(oscap.OVAL_RESULT_FALSE):
- print "%d definition(s) failed" % definitions[oscap.OVAL_RESULT_FALSE].__len__()
- for defn in definitions[oscap.OVAL_RESULT_FALSE]:
- print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
- if definitions.has_key(oscap.OVAL_RESULT_UNKNOWN):
- print "%d definition(s) were unknown" % definitions[oscap.OVAL_RESULT_UNKNOWN].__len__()
- for defn in definitions[oscap.OVAL_RESULT_UNKNOWN]:
- print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
- if definitions.has_key(oscap.OVAL_RESULT_ERROR):
- print "%d definition(s) errored" % definitions[oscap.OVAL_RESULT_ERROR].__len__()
- for defn in definitions[oscap.OVAL_RESULT_ERROR]:
- print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
- if definitions.has_key(oscap.OVAL_RESULT_NOT_EVALUATED):
- print "%d definition(s) not evaluated" % definitions[oscap.OVAL_RESULT_NOT_EVALUATED].__len__()
- for defn in definitions[oscap.OVAL_RESULT_NOT_EVALUATED]:
- print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
- if definitions.has_key(oscap.OVAL_RESULT_NOT_APPLICABLE):
- print "%d definition(s) not applicable" % definitions[oscap.OVAL_RESULT_NOT_APPLICABLE].__len__()
- for defn in definitions[oscap.OVAL_RESULT_NOT_APPLICABLE]:
- print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
-
def search(self, search_string, verbose=False):
"""
Function: Searches though all imported benchmarks for a string of text
@@ -776,13 +602,13 @@ class Secstate:
self.log.error("Benchmark was None")
return False
try:
- puppet_content = self.parse_puppet_fixes(benchmark, passing_ids)
+ puppet_content = parse_puppet_fixes(benchmark, passing_ids)
except SecstateException, se:
sys.stderr.write('Error: %s\n' % str(se))
return False
else:
handle, fname = tempfile.mkstemp(suffix='.sh')
- os.write(handle, template % self.dict_to_external(puppet_content))
+ os.write(handle, template % dict_to_external(puppet_content))
os.close(handle)
os.chmod(fname, 755)
puppet_args = ['/usr/bin/puppet', '--external_node', fname, '--node_terminus', 'exec', puppet_lib]
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 203a190..5e84a4a 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -22,6 +22,7 @@
import sys
import xml.dom.minidom
import time
+import re
import openscap as oscap
@@ -235,6 +236,103 @@ def evaluate_xccdf(benchmark, def_model, url_XCCDF, sess, s_profile=None, f_xml=
#oscap.xccdf_policy_model_free(policy_model)
return 0
+def output_oval_results(res_model, res_directives, xml, html):
+ if xml:
+ if not oscap.oval_results_model_export(res_model, res_directives, xml):
+ self.log.error("Error exporting results to %(file)s" % {'file':xml})
+ return False
+
+ if html:
+ tmp = tempfile.mktemp()
+ if not oscap.oval_results_model_export(res_model, res_directives, tmp):
+ self.log.error("Error exporting results to %(file)s" % {'file':html})
+ return False
+
+ import libxml2
+ import libxslt
+
+ styledoc = libxml2.parseFile(self.config.get('secstate', 'results_stylesheet'))
+ style = libxslt.parseStylesheetDoc(styledoc)
+ doc = libxml2.parseFile(tmp)
+ result = style.applyStylesheet(doc, None)
+ if not style.saveResultToFilename(html, result, 0):
+ self.log.error("Error exporting results to %(file)s" % {'file':html})
+ style.freeStylesheet()
+ doc.freeDoc()
+ result.freeDoc()
+ return False
+
+ style.freeStylesheet()
+ doc.freeDoc()
+ result.freeDoc()
+
+ return True
+
+def oval_callback(id, result, usr):
+ if usr['verbose']:
+ print "Evaluated definition %(id)s: %(res)s" % {'id':id, 'res':oscap.oval_result_get_text(result)}
+
+ if result == oscap.OVAL_RESULT_TRUE:
+ usr['true'] += 1
+ elif result == oscap.OVAL_RESULT_FALSE:
+ usr['false'] += 1
+ if result == oscap.OVAL_RESULT_INVALID:
+ usr['invalid'] += 1
+ if result == oscap.OVAL_RESULT_UNKNOWN:
+ usr['unknown'] += 1
+ if result == oscap.OVAL_RESULT_NOT_EVALUATED:
+ usr['neval'] += 1
+ if result == oscap.OVAL_RESULT_NOT_APPLICABLE:
+ usr['napp'] += 1
+
+ return 0
+
+def evaluate_oval(def_model, sess, f_xml, f_html, verbose=False):
+ res_model = oscap.oval_agent_get_results_model(sess)
+ usr = {'verbose':verbose,
+ 'true':0,
+ 'false':0,
+ 'invalid':0,
+ 'unknown':0,
+ 'neval':0,
+ 'napp':0}
+ ret = oscap.oval_agent_eval_system_py(sess, oval_callback, usr)
+
+ if verbose:
+ print "Evaluation Completed"
+
+ if ret == -1:
+ if oscap.oscap_err():
+ sys.stderr.write("Error: (%(code)d) %(desc)s" % {'code':oscap.oscap_err_code(),
+ 'desc':oscap.oscap_err_desc()})
+ return False
+
+ print "Results:"
+ print "True:\t%d" % usr['true']
+ print "False:\t%d" % usr['false']
+ print "Invalid:\t%d" % usr['invalid']
+ print "Unknown:\t%d" % usr['unknown']
+ print "Not Evaluated:\t%d" % usr['neval']
+ print "Not Applicable:\t%d" % usr['napp']
+
+ if f_xml or f_html:
+ res_direct = oscap.oval_result_directives_new(res_model)
+ oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_INVALID |
+ oscap.OVAL_RESULT_TRUE |
+ oscap.OVAL_RESULT_FALSE |
+ oscap.OVAL_RESULT_UNKNOWN |
+ oscap.OVAL_RESULT_ERROR |
+ oscap.OVAL_RESULT_NOT_EVALUATED |
+ oscap.OVAL_RESULT_NOT_APPLICABLE, True)
+ oscap.oval_result_directives_set_content(res_direct, oscap.OVAL_RESULT_FALSE, oscap.OVAL_DIRECTIVE_CONTENT_FULL)
+ oscap.oval_result_directives_set_content(res_direct, oscap.OVAL_RESULT_TRUE, oscap.OVAL_DIRECTIVE_CONTENT_FULL)
+
+ output_oval_results(res_model, res_direct, f_xml, f_html)
+ oscap.oval_result_directives_free(res_direct)
+
+ oscap.oval_agent_destroy_session(sess)
+ return ret
+
def is_benchmark(benchmark):
tree = xml.dom.minidom.parse(benchmark)
return (tree.getElementsByTagName("Benchmark") != [])
--
1.7.0.1
13 years, 10 months
[PATCH] Updated the list command
by Josh Adams
Made changes to the list command to make it more useful. Also cleaned
up a little code.
---
src/bin/secstate | 20 ++++++++++++---
src/secstate/main.py | 64 +++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 69 insertions(+), 15 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index e9a6a7a..8e379cb 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -86,7 +86,7 @@ def main():
search(sys.argv[arg_num:])
elif subcommand == 'list':
- list_defs(sys.argv[arg_num:])
+ list_content(sys.argv[arg_num:])
elif subcommand == 'show':
show(sys.argv[arg_num:])
@@ -199,8 +199,20 @@ def search(arguments):
if not sec_instance.search(args[0], options.verbose):
return -1
-def list_defs(args):
- sec_instance.list_defs(args)
+def list_content(arguments):
+ parser = OptionParser(usage="secstate list [options] <string>")
+ parser.add_option('-r', '--recurse', action='store_true', dest='recurse', default=False,
+ help="Recurse through an items content")
+ (options, args) = parser.parse_args(arguments)
+ if args == []:
+ if not sec_instance.list_content(recurse=options.recurse):
+ return -1
+
+ else:
+ for arg in args:
+ if not sec_instance.list_content(arg, options.recurse):
+ return -1
+ return 0
def show(arguments):
parser = OptionParser(usage="secstate show [options] <item id>")
@@ -210,7 +222,7 @@ def show(arguments):
for arg in args:
if not sec_instance.show(arg, options.verbose):
return -1
- return 0
+ return 0
if __name__ == '__main__':
sys.exit(main())
diff --git a/src/secstate/main.py b/src/secstate/main.py
index bef0507..0e1ab5b 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -41,6 +41,7 @@ class Secstate:
self.setConfigFile(conf_file)
self.database = self.getDatabase()
self.log = self.getLogger()
+ self.benchmark_dir = self.config.get('secstate', 'benchmark_dir')
def setConfigFile(self, conf):
config = ConfigParser.ConfigParser()
@@ -97,7 +98,7 @@ class Secstate:
Output: A tuple containg validated xccdf_benchmark and oval_definition_model
"""
#Create benchmark dir if it does not already exist
- bench_dir = self.config.get('secstate', 'benchmark_dir')
+ bench_dir = self.benchmark_dir
if not os.path.isdir(bench_dir):
try:
os.mkdir(bench_dir)
@@ -239,7 +240,7 @@ class Secstate:
return (None, None)
else:
self.log.info("Imported %(content)s to %(location)s" % {'content':content,
- 'location':self.config.get('secstate', 'benchmark_dir')})
+ 'location':self.benchmark_dir})
return (None, tmp)
if xccdf:
@@ -258,7 +259,7 @@ class Secstate:
if benchmark_id == 'all':
for key in self.database:
try:
- shutil.rmtree(os.path.join(self.config.get('secstate', 'benchmark_dir'), key))
+ shutil.rmtree(os.path.join(self.benchmark_dir, key))
except IOError,e:
self.log.error("Error removing content: %(error)s" % {'error':e})
return False
@@ -269,7 +270,7 @@ class Secstate:
if self.database.has_key(benchmark_id):
try:
- shutil.rmtree(os.path.join(self.config.get('secstate', 'benchmark_dir'), benchmark_id))
+ shutil.rmtree(os.path.join(self.benchmark_dir, benchmark_id))
except (IOError, OSError), e:
self.log.error("Error removing content: %(error)s" % {'error':e})
return False
@@ -292,7 +293,7 @@ class Secstate:
self.log.error("No benchmark %(id)s in datastore" % {'id':benchmark_id})
return False
- benchmark_file = os.path.join(self.config.get('secstate', 'benchmark_dir'), benchmark_id, self.database[benchmark_id])
+ benchmark_file = os.path.join(self.benchmark_dir, benchmark_id, self.database[benchmark_id])
(benchmark, oval) = self.import_benchmark(benchmark_file)
oscap.oval_definition_model_free(oval)
if benchmark == None:
@@ -474,7 +475,7 @@ class Secstate:
for arg in args:
benchmark_path = ""
if self.database.has_key(arg):
- benchmark_path = os.path.join(self.config.get('secstate', 'benchmark_dir'), arg, self.database[arg])
+ benchmark_path = os.path.join(self.benchmark_dir, arg, self.database[arg])
elif os.path.isfile(arg):
benchmark_path = arg
@@ -587,7 +588,7 @@ class Secstate:
Side Effects: Prints out the results of the search
"""
for key in self.database:
- benchmark = oscap.xccdf_benchmark_import(os.path.join(self.config.get('secstate', 'benchmark_dir'), key, self.database[key]))
+ benchmark = oscap.xccdf_benchmark_import(os.path.join(self.benchmark_dir, key, self.database[key]))
if benchmark == None:
self.log.error("Error importing benchmark: %(key)s" % {'key':key})
return False
@@ -637,7 +638,7 @@ class Secstate:
def show(self, item_id, verbose=False):
for key in self.database:
- (benchmark, def_model) = self.import_content(os.path.join(self.config.get('secstate', 'benchmark_dir'), key, self.database[key]))
+ (benchmark, def_model) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
if (benchmark == None) or (def_model == None):
self.log.error("Error importing content: %(file)s" % {'flie':key})
return None
@@ -696,9 +697,50 @@ class Secstate:
oscap.oval_definition_model_free(def_model)
return True
- def list_defs(self, arguments):
+ def list_content(self, arg=None, recurse=False):
for key in self.database:
- print key
+ (benchmark, oval) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
+ if benchmark == None:
+ self.log.error("Error loading benchmark: %(id)s" % {'id':key})
+ return False
+
+ if arg == None:
+ titles = oscap.xccdf_benchmark_get_title(benchmark)
+ for title in oscap_text_generator(titles):
+ print "%(id)s: %(title)s" % {'id':key, 'title':oscap.oscap_text_get_text(title)}
+
+ if recurse:
+ content = oscap.xccdf_benchmark_get_content(benchmark)
+ for sub in xccdf_item_generator(content):
+ titles = oscap.xccdf_item_get_title(sub)
+ for title in oscap_text_generator(titles):
+ print "\t%(id)s:\t%(title)s" % {'id':oscap.xccdf_item_get_id(sub),
+ 'title':oscap.oscap_text_get_text(title)}
+
+ else:
+ item = oscap.xccdf_benchmark_get_item(benchmark, arg)
+ if item == None:
+ pass
+
+ titles = oscap.xccdf_item_get_title(item)
+ for title in oscap_text_generator(titles):
+ print "%(id)s: %(title)s" % {'id':arg, 'title':oscap.oscap_text_get_text(title)}
+
+ if recurse:
+ type = oscap.xccdf_item_get_type(item)
+ if type == oscap.XCCDF_GROUP:
+ content = oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))
+ for sub in xccdf_item_generator(content):
+ titles = oscap.xccdf_item_get_title(sub)
+ for title in oscap_text_generator(titles):
+ print "\t%(id)s:\t%(title)s" % {'id':oscap.xccdf_item_get_id(sub),
+ 'title':oscap.oscap_text_get_text(title)}
+
+
+ oscap.xccdf_benchmark_free(benchmark)
+ oscap.oval_definition_model_free(oval)
+
+ return True
def get_passed_result_ids(self, xccdf_results):
if xccdf_results == None:
@@ -729,7 +771,7 @@ class Secstate:
passing_ids = self.get_passed_result_ids(xccdf_results)
template = '#!/bin/sh\ncat <<"END"\n%s\nEND\nexit 0\n'
if self.database.has_key(bench_id):
- (benchmark, tmp_model) = self.import_content(os.path.join(self.config.get('secstate', 'benchmark_dir'), bench_id, self.database[bench_id]))
+ (benchmark, tmp_model) = self.import_content(os.path.join(self.benchmark_dir, bench_id, self.database[bench_id]))
if not benchmark:
self.log.error("Benchmark was None")
return False
--
1.7.0.1
13 years, 10 months
[PATCH] Audit working with new XCCDF_POLICY api
by Josh Adams
Updated the audit command to work with the changes made to the
XCCDF_POLICY api in OpenSCAP. Will only audit selected rules.
---
src/secstate/main.py | 65 ++----------------------
src/secstate/util.py | 138 +++++++++++++++++++++++++++++++++++--------------
2 files changed, 102 insertions(+), 101 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 61dbf42..bef0507 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -479,71 +479,14 @@ class Secstate:
elif os.path.isfile(arg):
benchmark_path = arg
- (benchmark, tmp_model) = self.import_content(benchmark_path)
- if (benchmark == None) or (tmp_model == None):
+ (benchmark, def_model) = self.import_content(benchmark_path)
+ if (benchmark == None) or (def_model == None):
self.log.error("Error importing benchmark: %(bench)s" % {'bench':arg})
return False
- #Only audit selected rules
- def_model = get_selected_checks(benchmark, tmp_model)
- definitions = oscap.oval_definition_model_get_definitions(def_model)
- print "Getting selected definitions"
- for defn in oval_definition_generator(definitions):
- print oscap.oval_definition_get_id(defn)
-
if interpreter == "openscap":
- policy_model = oscap.xccdf_policy_model_new(benchmark)
-
- #Create a TestResult and attach it to the policy model
- result_id = "secstate_audit"
- ritem = oscap.xccdf_result_new()
- oscap.xccdf_result_set_id(ritem, result_id)
- oscap.xccdf_result_set_benchmark_uri(ritem, benchmark_path)
- title = oscap.oscap_text_new()
- oscap.oscap_text_set_text(title, "Secstate Audit Result")
- oscap.xccdf_result_add_title(ritem, title)
- oscap.xccdf_result_set_start_time(ritem, time.time())
- oscap.xccdf_policy_model_add_result(policy_model, ritem)
-
- policies = oscap.xccdf_policy_model_get_policies(policy_model)
- policy = None
- if (oscap.xccdf_policy_iterator_has_more(policies)):
- policy = oscap.xccdf_policy_iterator_next(policies)
-
- var_model = oscap.xccdf_policy_get_variables(policy, def_model)
-
- oscap.oval_definition_model_bind_variable_model(def_model, var_model)
- oscap.xccdf_policy_iterator_free(policies)
-
- res_model = self.create_result_model(def_model)
- oscap.oval_results_model_eval(res_model)
-
- (def_results, test_results) = self.get_results(res_model)
- self.parse_results(def_results, test_results)
-
- systems = oscap.oval_results_model_get_systems(res_model)
- # FIXME: need to free the iterator and possibly check the has_more method
- # before calling next
- rsystem = oscap.oval_result_system_iterator_next(systems)
-
- usr = {'rsystem':rsystem, 'result_id':result_id}
- reg = oscap.xccdf_policy_model_register_callback_py(policy_model,
- "http://oval.mitre.org/XMLSchema/oval-definitions-5",
- policy_callback, usr)
- if policy != None:
- oscap.xccdf_policy_evaluate(policy)
-
- xccdf_results = oscap.xccdf_policy_model_get_results(policy_model)
- for result in xccdf_result_generator(xccdf_results):
- oscap.xccdf_result_set_end_time(result, time.time())
- oscap.xccdf_benchmark_add_result(benchmark, result)
-
- #oscap.xccdf_policy_model_free(policy_model)
- #oscap.oval_results_model_free(res_model)
- #oscap.xccdf_benchmark_free(benchmark)
- #oscap.oval_definition_model_free(def_model)
-
- return True
+ sess = oscap.oval_agent_new_session(def_model)
+ return evaluate_xccdf(benchmark, def_model, benchmark_path, sess, f_xml=xml, f_html=html)
def get_result_definitions(self, result_model):
"""
diff --git a/src/secstate/util.py b/src/secstate/util.py
index f6ec441..e599eec 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -20,6 +20,7 @@
# This file is the core implementation of the secstate tool.
import xml.dom.minidom
+import time
import openscap as oscap
@@ -50,7 +51,7 @@ def iterator_to_generator_and_list(iterator_type):
xccdf_generator_list_types = ['xccdf_item', 'xccdf_notice', 'xccdf_status', 'xccdf_reference',
'xccdf_identity', 'xccdf_model', 'xccdf_result', 'xccdf_profile',
- 'xccdf_select', 'xccdf_value', 'xccdf_setvalue', 'xccdf_refine_value',
+ 'xccdf_select', 'xccdf_value', 'xccdf_value_binding', 'xccdf_setvalue', 'xccdf_refine_value',
'xccdf_refine_rule', 'xccdf_ident', 'xccdf_check', 'xccdf_profile_note',
'xccdf_fixtext', 'xccdf_check_content_ref', 'xccdf_check_import',
'xccdf_fix', 'xccdf_check_export', 'xccdf_warning', 'xccdf_instance',
@@ -114,37 +115,50 @@ def oval_result_t_to_xccdf(oval):
return oscap.XCCDF_RESULT_NOT_CHECKED
elif oval == oscap.OVAL_RESULT_NOT_APPLICABLE:
return oscap.XCCDF_RESULT_NOT_APPLICABLE
- elif oval == oscap.OVAL_ENUMERATION_INVALID:
+ elif oval == oscap.OVAL_RESULT_INVALID:
return 0
-def oval_result_t_to_xccdf(oval):
- if oval == oscap.OVAL_RESULT_TRUE:
- return oscap.XCCDF_RESULT_PASS
- elif oval == oscap.OVAL_RESULT_FALSE:
- return oscap.XCCDF_RESULT_FAIL
- elif oval == oscap.OVAL_RESULT_UNKNOWN:
- return oscap.XCCDF_RESULT_UNKNOWN
- elif oval == oscap.OVAL_RESULT_ERROR:
- return oscap.XCCDF_RESULT_ERROR
- elif oval == oscap.OVAL_RESULT_NOT_EVALUATED:
- return oscap.XCCDF_RESULT_NOT_CHECKED
- elif oval == oscap.OVAL_RESULT_NOT_APPLICABLE:
- return oscap.XCCDF_RESULT_NOT_APPLICABLE
- elif oval == oscap.OVAL_ENUMERATION_INVALID:
- return 0
+def xccdf_callback(model, rule_id, id, bindings, usr):
+ session = usr['asess']
-def policy_callback(model, href, id, usr):
- defn = oscap.oval_result_system_get_definition(usr['rsystem'], href)
- if defn == None:
+ rule = oscap.xccdf_item_to_rule(oscap.xccdf_benchmark_get_item(oscap.xccdf_policy_model_get_benchmark(model), rule_id))
+ if rule == None:
+ print "Error retrieving rule %(id)s" % {'id':rule_id}
return False
- result = oscap.oval_result_definition_eval(defn)
- print "Definition: %s\tresult: %s" % (href, oscap.oval_result_get_text(result))
- ritem = oscap.xccdf_policy_model_get_result_by_id(model, usr['result_id'])
+ #Skip unselected rules
+ if not oscap.xccdf_rule_get_selected(rule):
+ return True
+
+ def_model = oscap.oval_results_model_get_definition_model(oscap.oval_agent_get_results_model(session))
+
+ #for binding in xccdf_value_binding_generator(bindings):
+ while oscap.xccdf_value_binding_iterator_has_more(bindings):
+ name = oscap.xccdf_value_binding_get_name(binding)
+ value = oscap.xccdf_value_binding_get_value(binding)
+ variable = oscap.oval_definition_model_get_definition(def_model, binding)
+ if variable != None:
+ value_it = oscap.oval_variable_get_values(variable)
+ if oscap.oval_value_iterator_has_more(value_it):
+ oscap.oval_definition_model_clear_external_variables(def_model)
+ oscap.oval_agent_destroy_session(session)
+ usr['asess'] = oscap.oval_agent_session_new(def_model)
+ var_model = oscap.oval_variable_model_new()
+ o_type = oscap.oval_variable_get_type(variable)
+ oscap.oval_variable_model_add(var_model, name, "Unknown", o_type, value)
+ oval_definition_model_bind_variable_model(def_model, var_model)
+
+ result = oscap.oval_agent_eval_definition(session, id)
+
+ print "Rule: %(rid)s -- Definition: %(defid)s\tResult: %(res)s" % { 'rid':rule_id,
+ 'defid':id,
+ 'res':oscap.oval_result_get_text(result) }
+
+ rid = usr['result_id']
+ ritem = oscap.xccdf_policy_model_get_result_by_id(model, rid)
if ritem == None:
pass
else:
- rule = oscap.xccdf_item_to_rule(oscap.xccdf_benchmark_get_item(oscap.xccdf_policy_model_get_benchmark(model), id))
rule_ritem = oscap.xccdf_rule_result_new()
oscap.xccdf_rule_result_set_result(rule_ritem, oval_result_t_to_xccdf(result))
oscap.xccdf_rule_result_set_idref(rule_ritem, id)
@@ -159,22 +173,66 @@ def policy_callback(model, href, id, usr):
return True
-def get_selected_checks(benchmark, oval):
- """
- Function: Get all the selected checks from a benchmark
- Input: xccdf_benchmark
- Ouput: oval_definition_model only containing checks that were selected
- """
- new_oval = oscap.oval_definition_model_new()
- for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
- if oscap.xccdf_rule_get_selected(rule):
- checks = oscap.xccdf_rule_get_checks(rule)
- for check in xccdf_check_generator(checks):
- refs = oscap.xccdf_check_get_content_refs(check)
- for ref in xccdf_check_content_ref_generator(refs):
- oscap.oval_definition_clone(new_oval, oscap.oval_definition_model_get_definition(oval, oscap.xccdf_check_content_ref_get_name(ref)))
-
- return new_oval
+def evaluate_xccdf(benchmark, def_model, url_XCCDF, sess, s_profile=None, f_xml=None, f_html=None):
+ policy = None
+ policy_model = oscap.xccdf_policy_model_new(benchmark)
+
+ if (s_profile != None):
+ policy = oscap.xccdf_policy_model_get_policy_by_id(policy_model, s_profile)
+ else:
+ policies = oscap.xccdf_policy_model_get_policies(policy_model)
+ if (oscap.xccdf_policy_iterator_has_more(policies)):
+ policy = oscap.xccdf_policy_iterator_next(policies)
+ oscap.xccdf_policy_iterator_free(policies)
+
+ if policy == None:
+ sys.stderr.write("No policy to evaluate.")
+ return -1
+
+ usr = { 'result_id':"secstate_audit-test",
+ 'asess':sess }
+
+ ritem = oscap.xccdf_result_new()
+ oscap.xccdf_result_set_id(ritem, usr['result_id'])
+ oscap.xccdf_result_set_benchmark_uri(ritem, url_XCCDF)
+ title = oscap.oscap_text_new()
+ oscap.oscap_text_set_text(title, "Secstate Audit Result")
+ oscap.xccdf_result_add_title(ritem, title)
+ oscap.xccdf_result_set_start_time(ritem, time.time())
+ oscap.xccdf_policy_model_add_result(policy_model, ritem)
+
+ oscap.xccdf_policy_model_register_callback_py(policy_model, "http://oval.mitre.org/XMLSchema/oval-definitions-5",
+ xccdf_callback, usr)
+
+ oscap.xccdf_policy_evaluate(policy)
+
+ res_model = oscap.oval_agent_get_results_model(usr['asess'])
+ res_system = oscap.oval_result_system_iterator_next(oscap.oval_results_model_get_systems(res_model))
+ sys_model = oscap.oval_result_system_get_syschar_model(res_system)
+ sysinfo = oscap.oval_syschar_model_get_sysinfo(sys_model)
+
+ oscap.xccdf_result_set_test_system(ritem, oscap.oval_sysinfo_get_primary_host_name(sysinfo))
+ if (policy != None):
+ profile = oscap.xccdf_policy_get_profile(policy)
+ if oscap.xccdf_profile_get_id(profile) != None:
+ oscap.xccdf_result_set_profile(ritem, oscap.xccdf_profile_get_id(profile))
+
+ sysint_it = oscap.oval_sysinfo_get_interfaces(sysinfo)
+ for sysint in oval_sysint_generator(sysint_it):
+ oscap.xccdf_result_add_target_address(ritem, oscap.oval_sysint_get_ip_address(sysint))
+ if oscap.oval_sysint_get_mac_address(sysint) != None:
+ fact = oscap.xccdf_target_fact_new()
+ oscap.xccdf_target_fact_set_name(fact, "urn:xccdf:fact:ethernet:MAC")
+ oscap.xccdf_target_fact_set_string(fact, oscap.oval_sysint_get_mac_address(sysint))
+
+ oscap.xccdf_result_set_end_time(ritem, time.time())
+
+ if f_xml != None:
+ oscap.xccdf_result_export(ritem, f_xml)
+
+ oscap.oval_agent_destroy_session(usr['asess'])
+ #oscap.xccdf_policy_model_free(policy_model)
+ return 0
def is_benchmark(benchmark):
tree = xml.dom.minidom.parse(benchmark)
--
1.7.0.1
13 years, 10 months
[PATCH] Updated the help string.
by Josh Adams
---
src/bin/secstate | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 76f9cf3..e9a6a7a 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -39,7 +39,8 @@ Sub-commands:
import -- Validates and imports a benchmark and its associated oval into the system
select -- Sets a portion of the benchmark (group/rule) to be selected
deselect -- Sets a portion of the benchmark (group/rule) to be deselected
- show -- Shows the information associated with a definition id
+ list -- List the imported benchmarks
+ show -- Shows the information associated with a group, rule, or definition id
search -- Search through imported content
remediate -- FIXME -- Remediate issues.
audit -- Audit the local system based the specified benchmark\n""" % sys.argv[0]
--
1.7.0.1
13 years, 10 months
[PATCH] Initial commit for the 'show' command
by Josh Adams
Will search through all imported benchmarks and assocaited oval for the
given id and print out information about it.
---
src/bin/secstate | 11 +++++++-
src/secstate/main.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/secstate/util.py | 16 ++++++++++++
3 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 5a10267..76f9cf3 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -201,8 +201,15 @@ def search(arguments):
def list_defs(args):
sec_instance.list_defs(args)
-def show(args):
- print 'show'
+def show(arguments):
+ parser = OptionParser(usage="secstate show [options] <item id>")
+ parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False,
+ help="Show extra information about the item being whown")
+ (options, args) = parser.parse_args(arguments)
+ for arg in args:
+ if not sec_instance.show(arg, options.verbose):
+ return -1
+ return 0
if __name__ == '__main__':
sys.exit(main())
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 78e8918..97deddc 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -689,8 +689,71 @@ class Secstate:
print "\t%(id)s:" % {'id':id}
print "\t\tTitle: %(title)s" % {'title':title}
print "\t\tDescription: %(description)s\n" % {'description':description}
+
+ oscap.xccdf_benchmark_free(benchmark)
return True
+ def show(self, item_id, verbose=False):
+ for key in self.database:
+ (benchmark, def_model) = self.import_content(os.path.join(self.config.get('secstate', 'benchmark_dir'), key, self.database[key]))
+ if (benchmark == None) or (def_model == None):
+ self.log.error("Error importing content: %(file)s" % {'flie':key})
+ return None
+
+ item = oscap.xccdf_benchmark_get_item(benchmark, item_id)
+ if item == None:
+ defn = oscap.oval_definition_model_get_definition(def_model, item_id)
+ if defn == None:
+ oscap.xccdf_benchmark_free(benchmark)
+ oscap.oval_definition_model_free(def_model)
+ break
+ else:
+ print "%(id)s:" % {'id':oscap.oval_definition_get_id(item)}
+ print "\t%(title)s" % {'title':oscap.oval_definition_get_title(defn)}
+ print "\t%(desc)s" % {'desc':oscap.oval_definition_get_description(defn)}
+ oscap.xccdf_benchmark_free(benchmark)
+ oscap.oval_definition_model_free(def_model)
+ return True
+
+ else:
+ print "%(id)s:" % {'id':oscap.xccdf_item_get_id(item)}
+ titles = oscap.xccdf_item_get_title(item)
+ for title in oscap_text_generator(titles):
+ print "\tTitle: %(title)s" % {'title':oscap.oscap_text_get_text(title)}
+
+ descriptions = oscap.xccdf_item_get_description(item)
+ for description in oscap_text_generator(descriptions):
+ print "\tDescription: %(desc)s" % {'desc':oscap.oscap_text_get_text(description)}
+
+ print "\tSelected: %(sel)s" % {'sel':oscap.xccdf_item_get_selected(item)}
+
+ if verbose:
+ print "\tMember of %(parent)s" % {'parent':oscap.xccdf_item_get_id(oscap.xccdf_item_get_parent(item))}
+ type = oscap.xccdf_item_get_type(item)
+ if type == oscap.XCCDF_GROUP:
+ group = oscap.xccdf_item_to_group(item)
+ subgroups = xccdf_get_items(benchmark, oscap.XCCDF_GROUP, oscap.xccdf_group_get_content(group))
+ if subgroups != []:
+ print "\tSubgoups:"
+ for sub in subgroups:
+ print "\t\t%(id)s" % {'id':oscap.xccdf_group_get_id(sub)}
+
+ rules = xccdf_get_items(benchmark, oscap.XCCDF_RULE, oscap.xccdf_group_get_content(group))
+ if rules != []:
+ print "\tRules:"
+ for rule in rules:
+ print "\t\t%(id)s" % {'id':oscap.xccdf_rule_get_id(rule)}
+
+ elif type == oscap.XCCDF_RULE:
+ rule = oscap.xccdf_item_to_rule(item)
+ print "\tReferenced Definitions:"
+ for defn in xccdf_rule_get_defs(rule):
+ print "\t\t%(id)s" % {'id':defn}
+
+ oscap.xccdf_benchmark_free(benchmark)
+ oscap.oval_definition_model_free(def_model)
+ return True
+
def list_defs(self, arguments):
for key in self.database:
print key
diff --git a/src/secstate/util.py b/src/secstate/util.py
index ea9f429..f6ec441 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -95,6 +95,12 @@ for type in oval_generator_list_types:
globals()['%s_generator' % type] = gen_func
globals()['%s_list' % type] = list_func
+oscap_generator_list_types = ['oscap_text', 'oscap_string']
+for type in oscap_generator_list_types:
+ gen_func, list_func = iterator_to_generator_and_list('%s_iterator' % type)
+ globals()['%s_generator' % type] = gen_func
+ globals()['%s_list' % type] = list_func
+
def oval_result_t_to_xccdf(oval):
if oval == oscap.OVAL_RESULT_TRUE:
return oscap.XCCDF_RESULT_PASS
@@ -242,6 +248,16 @@ def xccdf_get_refs(benchmark):
refs.append(oscap.xccdf_check_content_ref_get_href(ref))
return refs
+def xccdf_rule_get_defs(rule):
+ defs = []
+ checks = oscap.xccdf_rule_get_checks(rule)
+ for check in xccdf_check_generator(checks):
+ refs = oscap.xccdf_check_get_content_refs(check)
+ for ref in xccdf_check_content_ref_generator(refs):
+ defs.append(oscap.xccdf_check_content_ref_get_name(ref))
+
+ return defs
+
def xccdf_get_fixes(benchmark, ignore_ids=[]):
"""
Function: Get all fixes for rules in the XCCDF document
--
1.7.0.1
13 years, 10 months
[PATCH] Fixed search capability
by Josh Adams
Can search through the titles/descriptions of groups and rules. Also
fixed an encoding issue when importing files.
---
src/bin/secstate | 9 +++++-
src/secstate/main.py | 62 ++++++++++++++++++++++++++++++++++++++-----------
src/secstate/util.py | 2 +-
3 files changed, 56 insertions(+), 17 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 3783542..5a10267 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -190,8 +190,13 @@ def remediate(arguments):
if not sec_instance.remediate_puppet(**kwargs):
return -1
-def search(args):
- print 'search'
+def search(arguments):
+ parser = OptionParser(usage="secstate search [options] <string>")
+ parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False,
+ help="Show extra information from the search")
+ (options, args) = parser.parse_args(arguments)
+ if not sec_instance.search(args[0], options.verbose):
+ return -1
def list_defs(args):
sec_instance.list_defs(args)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index c3ae36e..78e8918 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -637,25 +637,59 @@ class Secstate:
for defn in definitions[oscap.OVAL_RESULT_NOT_APPLICABLE]:
print "\t%s" % oscap.oval_definition_get_id(oscap.oval_result_definition_get_definition(defn))
- def search(self, benchmark_id, search_string):
+ def search(self, search_string, verbose=False):
"""
- Function: Searches though a benchmark for a string of text
+ Function: Searches though all imported benchmarks for a string of text
Input: A benchmark id and a string to search for
Output: None
Side Effects: Prints out the results of the search
"""
- (benchmark, def_model) = self.import_benchmark(os.path.join(self.config.get('secstate', 'benchmark_dir'), id))
- for group in xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
- title = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_group_get_title(group)))
- description = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_group_get_description(group)))
- if (search_string in title) or (search_string in description):
- print "%(title)s\t%(description)s" % {'title':title, 'description':description}
-
- for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
- title = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_rule_get_title(rule)))
- description = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_rule_get_description(rule)))
- if (search_string in title) or (search_string in description):
- print "%(title)s\t%(description)s" % {'title':title, 'description':description}
+ for key in self.database:
+ benchmark = oscap.xccdf_benchmark_import(os.path.join(self.config.get('secstate', 'benchmark_dir'), key, self.database[key]))
+ if benchmark == None:
+ self.log.error("Error importing benchmark: %(key)s" % {'key':key})
+ return False
+
+ print "In benchmark %(bench)s:" % {'bench':key}
+
+ for group in xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
+ id = oscap.xccdf_group_get_id(group)
+ title = None
+ description = None
+
+ titles = oscap.xccdf_group_get_title(group)
+ if oscap.oscap_text_iterator_has_more(titles):
+ title = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_group_get_title(group)))
+
+ descriptions = oscap.xccdf_group_get_description(group)
+ if oscap.oscap_text_iterator_has_more(descriptions):
+ description = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_group_get_description(group)))
+
+ if (title != None) and (description != None):
+ if (search_string in title) or (search_string in description):
+ print "\t%(id)s:" % {'id':id}
+ print "\t\tTitle: %(title)s" % {'title':title}
+ print "\t\tDescription: %(description)s\n" % {'description':description}
+
+ for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
+ id = oscap.xccdf_rule_get_id(rule)
+ title = None
+ description = None
+
+ titles = oscap.xccdf_rule_get_title(rule)
+ if oscap.oscap_text_iterator_has_more(titles):
+ title = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_rule_get_title(rule)))
+
+ descriptions = oscap.xccdf_rule_get_description(rule)
+ if oscap.oscap_text_iterator_has_more(descriptions):
+ description = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_rule_get_description(rule)))
+
+ if (title != None) and (description != None):
+ if (search_string in title) or (search_string in description):
+ print "\t%(id)s:" % {'id':id}
+ print "\t\tTitle: %(title)s" % {'title':title}
+ print "\t\tDescription: %(description)s\n" % {'description':description}
+ return True
def list_defs(self, arguments):
for key in self.database:
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 60ae360..ea9f429 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -176,7 +176,7 @@ def is_benchmark(benchmark):
def get_benchmark_id(benchmark):
tree = xml.dom.minidom.parse(benchmark)
- return tree.getElementsByTagName("Benchmark")[0].getAttribute("id")
+ return tree.getElementsByTagName("Benchmark")[0].getAttribute("id").encode('utf_8')
def xccdf_get_items(template, type, items=None):
"""
--
1.7.0.1
13 years, 10 months
[PATCH] Initial commit to enable XML and HTML output
by Josh Adams
Adds option to output audit results to an XML and/or HTML file.
---
dist/secstate.spec | 2 +
src/bin/secstate | 6 +-
src/etc/results_to_html.xsl | 688 +++++++++++++++++++++++++++++++++++++++++++
src/etc/secstate.conf | 1 +
src/secstate/main.py | 45 +++-
5 files changed, 738 insertions(+), 4 deletions(-)
create mode 100644 src/etc/results_to_html.xsl
diff --git a/dist/secstate.spec b/dist/secstate.spec
index ee76606..6023ba2 100644
--- a/dist/secstate.spec
+++ b/dist/secstate.spec
@@ -17,6 +17,8 @@ BuildRequires: redhat-rpm-config python2-devel
Requires: openscap-python
Requires: ovaldi
Requires: python
+Requires: libxml2
+Requires: libxslt-python
%description
SecState is a Security Configuration tool that utilizes openscap and puppet
diff --git a/src/bin/secstate b/src/bin/secstate
index 340eadf..3783542 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -140,8 +140,12 @@ def audit(arguments):
help="Audits the system based on the specified OVAL file")
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False,
help="Prints out extra information during the audit process")
+ parser.add_option('--html', action='store', dest='html', default=None,
+ help="Output audit results in HTML")
+ parser.add_option('--xml', action='store', dest='xml', default=None,
+ help="Output audit results in XML")
(options, args) = parser.parse_args(arguments)
- if (not (sec_instance.audit(options.interpreter, args, options.oval, verbose=options.verbose))):
+ if (not (sec_instance.audit(options.interpreter, args, options.oval, verbose=options.verbose, xml=options.xml, html=options.html))):
return -1
def remediate(arguments):
diff --git a/src/etc/results_to_html.xsl b/src/etc/results_to_html.xsl
new file mode 100644
index 0000000..6dbe652
--- /dev/null
+++ b/src/etc/results_to_html.xsl
@@ -0,0 +1,688 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+****************************************************************************************
+ Copyright (c) 2002-2010, The MITRE Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification, are
+ permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list
+ of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+ * Neither the name of The MITRE Corporation nor the names of its contributors may be
+ used to endorse or promote products derived from this software without specific
+ prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************************
+
+ AUTHOR:Matt Burton, The Mitre Corporation
+ DATE: 02 May 2005
+
+ Modified by Loren Bandiera, MMG Security
+ * Updating for v5 results
+ DATE: 10 May 2006
+
+ Reimplemented by Jon Baker, The Mitre Corporation
+ DATE: 12 October 2006
+
+ Modified by Vladimir Giszpenc, DSCI Contractor Supporting CERDEC S&TCD IAD
+ * Allowing for references other than CVE such as Red Hat patches
+ DATE: 18 May 2007
+
+ Modified by Vladimir Giszpenc, DSCI Contractor Supporting CERDEC S&TCD IAD
+ * Added some aggregate data in the Systems Analysed section
+ DATE: 20 Aug 2007
+
+ The results_to_html stylesheet converts an OVAL Results document into a more readable html format.
+ General information about the source of the OVAL Definitions being reported on, and the OVAL Results
+ producer is displayed. Next general information about each system analyzed is presented including a
+ table or result information. The table displays true results then all other results sorted in
+ descending order by result. If the OVAL Results document has results for multiple systems a set
+ of links will be generated near the top of the resulting html to allow users to easily jump to the
+ each system's results.
+
+-->
+<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5"
+ xmlns:oval-res="http://oval.mitre.org/XMLSchema/oval-results-5" xmlns:oval-sc="http://oval.mitre.org/XMLSchema/oval-system-characteristics-5"
+ xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:apache-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#apache"
+ xmlns:ind-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" xmlns:windows-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#windows"
+ xmlns:unix-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix" xmlns:linux-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
+ <xsl:output method="html" indent="yes" omit-xml-declaration="yes"/>
+
+ <xsl:key name="definition-index" use="@id" match="/oval-res:oval_results/oval-def:oval_definitions/oval-def:definitions/oval-def:definition"/>
+
+ <!-- Style declarations for pretty formatting -->
+ <xsl:template name="oval_res_style">
+ <style type="text/css">
+
+TD.title {BACKGROUND-COLOR: #000000; COLOR: #ffc; TEXT-ALIGN: left; font: bold 12pt/14pt "Arial"}
+TD.label {BACKGROUND-COLOR: #99cc99; font: 10pt/12pt "Arial"}
+TD.label2 {font: bold 10pt/14pt "Arial"}
+TD.text {font: 10pt/12pt "Arial"}
+
+.trueA{background-color: #FFBC8F; font: 10pt/12pt "Arial"}
+.trueB{background-color: #FFE0CC; font: 10pt/12pt "Arial"}
+
+.falseA{background-color: #ACD685; font: 10pt/12pt "Arial"}
+.falseB{background-color: #CBE6B3; font: 10pt/12pt "Arial"}
+
+.unknownA{background-color: #AEC8E0; font: 10pt/12pt "Arial"}
+.unknownB{background-color: #DAE6F1; font: 10pt/12pt "Arial"}
+
+.errorA{background-color: #FFDD75; font: 10pt/12pt "Arial"}
+.errorB{background-color: #FFECB3; font: 10pt/12pt "Arial"}
+
+.naA{background-color: #EEEEEE; font: 10pt/12pt "Arial"}
+.naB{background-color: #FFFFFF; font: 10pt/12pt "Arial"}
+
+.neA{background-color: #EEEEEE; font: 10pt/12pt "Arial"}
+.neB{background-color: #FFFFFF; font: 10pt/12pt "Arial"}
+
+ </style>
+ </xsl:template>
+
+ <xsl:template name="ResultColorTable">
+ <table border="0" cellspacing="0" cellpadding="0">
+ <tr>
+ <td>   </td>
+ <td>
+ <!--<td class="label2">Result Color Key:  </td>-->
+ <table border="1" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="trueA" width="10"> </td>
+ <td class="trueB" width="10"> </td>
+ <td class="text"> True  </td>
+ </tr>
+ </table>
+ </td>
+ <td>   </td>
+ <td>
+ <table border="1" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="falseA" width="10"> </td>
+ <td class="falseB" width="10"> </td>
+ <td class="text"> False  </td>
+ </tr>
+ </table>
+ </td>
+ <td>   </td>
+ <td>
+ <table border="1" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="errorA" width="10"> </td>
+ <td class="errorB" width="10"> </td>
+ <td class="text"> Error  </td>
+ </tr>
+ </table>
+ </td>
+ <td>   </td>
+ <td>
+ <table border="1" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="unknownA" width="10"> </td>
+ <td class="unknownB" width="10"> </td>
+ <td class="text"> Unknown  </td>
+ </tr>
+ </table>
+ </td>
+ <td>   </td>
+ <td>
+ <table border="1" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="naA" width="10"> </td>
+ <td class="naB" width="10"> </td>
+ <td class="text"> Not Applicable  </td>
+ </tr>
+ </table>
+ </td>
+ <td>   </td>
+ <td>
+ <table border="1" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="neA" width="10"> </td>
+ <td class="neB" width="10"> </td>
+ <td class="text"> Not Evaluated  </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </xsl:template>
+
+ <!-- Gets the top level node -->
+ <xsl:template match="oval-res:oval_results">
+ <html>
+ <head>
+ <title>OVAL Results</title>
+ <!-- Get stylesheet -->
+ <xsl:call-template name="oval_res_style"/>
+ </head>
+ <body>
+
+ <!-- display results and definition generator information -->
+ <table border="1" cellpadding="0" cellspacing="0" width="100%">
+ <tr>
+ <td width="50%">
+ <xsl:call-template name="ResultGenerator">
+ <xsl:with-param name="generatorElm" select="./oval-res:generator"/>
+ </xsl:call-template>
+ </td>
+ <td width="50%">
+ <xsl:call-template name="DefGenerator">
+ <xsl:with-param name="generatorElm" select="./oval-def:oval_definitions/oval-def:generator"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+ <!--
+ create anchors to each system in the results file
+ if only one systen leave out the anchors
+ -->
+ <!-- 2007-08-20 Added aggregate result detail-->
+ <xsl:if test="not(count(./oval-res:results/oval-res:system) = 1)">
+ <table border="1" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="title" colspan="7">Systems Analyzed</td>
+ </tr>
+ <!-- display the result color coding table -->
+ <tr><td colspan="7"><xsl:call-template name="ResultColorTable"/></td></tr>
+
+ <tr>
+ <td class="label" align="center">System</td>
+ <td class="label" align="center">Trues</td>
+ <td class="label" align="center">Falses</td>
+ <td class="label" align="center">Errors</td>
+ <td class="label" align="center">Unknown</td>
+ <td class="label" align="center">Not Applicables</td>
+ <td class="label" align="center">Not Evaluateds</td>
+ </tr>
+ <xsl:for-each select="./oval-res:results/oval-res:system">
+ <tr>
+ <td class="label">
+ <a href="#{position()}">
+ <xsl:value-of select="./oval-sc:oval_system_characteristics/oval-sc:system_info/oval-sc:primary_host_name"/>
+ </a>
+ </td>
+ <td width="10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 1">
+ <xsl:attribute name="class">trueA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="position() mod 2 = 0">
+ <xsl:attribute name="class">trueB</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="count(./oval-res:definitions/oval-res:definition[@result='true'])"/>
+ </td>
+ <td width="10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 1">
+ <xsl:attribute name="class">falseA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="position() mod 2 = 0">
+ <xsl:attribute name="class">falseB</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="count(./oval-res:definitions/oval-res:definition[@result='false'])"/>
+ </td>
+ <td width="10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 1">
+ <xsl:attribute name="class">errorA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="position() mod 2 = 0">
+ <xsl:attribute name="class">errorB</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="count(./oval-res:definitions/oval-res:definition[@result='error'])"/>
+ </td>
+ <td width="10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 1">
+ <xsl:attribute name="class">unknownA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="position() mod 2 = 0">
+ <xsl:attribute name="class">unknownB</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="count(./oval-res:definitions/oval-res:definition[@result='unknown'])"/>
+ </td>
+ <td width="10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 1">
+ <xsl:attribute name="class">naA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="position() mod 2 = 0">
+ <xsl:attribute name="class">naB</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="count(./oval-res:definitions/oval-res:definition[@result='not applicable'])"/>
+ </td>
+ <td width="10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 1">
+ <xsl:attribute name="class">neA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="position() mod 2 = 0">
+ <xsl:attribute name="class">neB</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="count(./oval-res:definitions/oval-res:definition[@result='not evaluated'])"/>
+ </td>
+ </tr>
+ <!-- 2007-08-20 Added aggregate result detail-->
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+
+ <!--
+ for each system in the results file
+ - display system info
+ - display the sc generator
+ - display results table
+ -->
+ <xsl:for-each select="./oval-res:results/oval-res:system">
+ <!-- display the system info data -->
+ <xsl:call-template name="SystemInfo">
+ <xsl:with-param name="sysInfoElm" select="./oval-sc:oval_system_characteristics/oval-sc:system_info"/>
+ </xsl:call-template>
+
+ <!-- display the generator info for the sc data -->
+ <xsl:call-template name="SCGenerator">
+ <xsl:with-param name="generatorElm" select="./oval-sc:oval_system_characteristics/oval-sc:generator"/>
+ </xsl:call-template>
+
+ <!-- display definition results -->
+ <xsl:call-template name="DefinitionsResults">
+ <xsl:with-param name="definitionsElm" select="./oval-res:definitions"/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </body>
+ </html>
+ </xsl:template>
+
+ <!-- Get the system_info and put it into a table. -->
+ <xsl:template name="SystemInfo">
+ <xsl:param name="sysInfoElm"/>
+ <table border="1" cellspacing="0" cellpaddign="2" width="100%" bgcolor="#cccccc">
+ <tr>
+ <td class="title" colspan="2">
+ <a name="{position()}">System Information</a>
+ </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">Host Name</td>
+ <td class="text" width="80%"><xsl:value-of select="$sysInfoElm/child::oval-sc:primary_host_name/text()"/> </td>
+ <!-- $#160; is used to keep empty cells in the table clean -->
+ </tr>
+ <tr>
+ <td class="label2" width="20%">Operating System</td>
+ <td class="text" width="80%"><xsl:value-of select="$sysInfoElm/child::oval-sc:os_name/text()"/> </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">Operating System Version</td>
+ <td class="text" width="80%"><xsl:value-of select="$sysInfoElm/child::oval-sc:os_version/text()"/> </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">Architecture</td>
+ <td class="text" width="80%"><xsl:value-of select="$sysInfoElm/child::oval-sc:architecture/text()"/> </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">Interfaces</td>
+ <td width="80%">
+ <xsl:call-template name="Interfaces">
+ <xsl:with-param name="iterfacesElm" select="$sysInfoElm[1]/oval-sc:interfaces"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ </table>
+ </xsl:template>
+
+ <!-- Get this interface and put it into the table, using templates to loop through all possible interfaces -->
+ <xsl:template name="Interfaces">
+ <xsl:param name="iterfacesElm"/>
+
+ <xsl:for-each select="$iterfacesElm/oval-sc:interface">
+ <xsl:if test="position() mod 2 = 1">
+ <table border="1" cellpadding="1" cellspacing="0" width="100%" bgcolor="ffffff">
+ <tr>
+ <td class="label2" width="20%">Interface Name</td>
+ <td class="text" width="80%"><xsl:value-of select="./oval-sc:interface_name/text()"/> </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">IP Address</td>
+ <td class="text" width="80%"><xsl:value-of select="./oval-sc:ip_address/text()"/> </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">MAC Address</td>
+ <td class="text" width="80%"><xsl:value-of select="./oval-sc:mac_address/text()"/> </td>
+ </tr>
+ </table>
+ </xsl:if>
+ <xsl:if test="position() mod 2 = 0">
+ <table border="1" cellpadding="1" cellspacing="0" width="100%" bgcolor="eeeeee">
+ <tr>
+ <td class="label2" width="20%">Interface Name</td>
+ <td class="text" width="80%"><xsl:value-of select="./oval-sc:interface_name/text()"/> </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">IP Address</td>
+ <td class="text" width="80%"><xsl:value-of select="./oval-sc:ip_address/text()"/> </td>
+ </tr>
+ <tr>
+ <td class="label2" width="20%">MAC Address</td>
+ <td class="text" width="80%"><xsl:value-of select="./oval-sc:mac_address/text()"/> </td>
+ </tr>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Create a table for the Result Generator and call its children. -->
+ <xsl:template name="ResultGenerator">
+ <xsl:param name="generatorElm"/>
+ <table border="1" cellspacing="0" cellpaddign="2" width="100%" bgcolor="#cccccc">
+ <tr>
+ <td class="title" colspan="5">OVAL Results Generator Information</td>
+ </tr>
+ <tr>
+ <td class="label" nowrap="nowrap">Schema Version</td>
+ <td class="label" nowrap="nowrap">Product Name</td>
+ <td class="label" nowrap="nowrap">Product Version</td>
+ <td class="label">Date</td>
+ <td class="label">Time</td>
+ </tr>
+ <xsl:call-template name="Generator">
+ <xsl:with-param name="generatorElm" select="$generatorElm"/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!-- Create a table for the System Characteristics Generator and call its children. -->
+ <xsl:template name="SCGenerator">
+ <xsl:param name="generatorElm"/>
+ <table border="1" cellspacing="0" cellpaddign="2" width="100%" bgcolor="#cccccc">
+ <tr>
+ <td class="title" colspan="5">OVAL System Characteristics Generator Information</td>
+ </tr>
+ <tr>
+ <td class="label">Schema Version</td>
+ <td class="label">Product Name</td>
+ <td class="label">Product Version</td>
+ <td class="label">Date</td>
+ <td class="label">Time</td>
+ </tr>
+ <xsl:call-template name="Generator">
+ <xsl:with-param name="generatorElm" select="$generatorElm"/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!-- Create a table for the Definitions Generator and call its children. -->
+ <xsl:template name="DefGenerator">
+ <xsl:param name="generatorElm"/>
+ <table border="1" cellspacing="0" cellpaddign="2" width="100%" bgcolor="#cccccc">
+ <tr>
+ <td class="title" colspan="5">OVAL Definition Generator Information</td>
+ </tr>
+ <tr>
+ <td class="label" nowrap="nowrap">Schema Version</td>
+ <td class="label" nowrap="nowrap">Product Name</td>
+ <td class="label" nowrap="nowrap">Product Version</td>
+ <td class="label">Date</td>
+ <td class="label">Time</td>
+ </tr>
+ <xsl:call-template name="Generator">
+ <xsl:with-param name="generatorElm" select="$generatorElm"/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!-- Each child of Generators (oval, system_characteristics, results) is the same, this template gets their children. -->
+ <xsl:template name="Generator">
+ <xsl:param name="generatorElm"/>
+ <xsl:variable name="MessyNumber" select="string($generatorElm/oval:timestamp)"/>
+ <td class="text"><xsl:value-of select="$generatorElm/oval:schema_version"/> </td>
+ <td class="text"><xsl:value-of select="$generatorElm/oval:product_name"/> </td>
+ <td class="text"><xsl:value-of select="$generatorElm/oval:product_version"/> </td>
+ <td class="text">
+ <!--Create variable "MessyNumber" to make time stamp a string and then print it out in a readable version -->
+ <xsl:value-of select="substring($MessyNumber, 1, 4)"/>
+ <!-- year -->
+ <xsl:text>-</xsl:text>
+ <xsl:value-of select="substring($MessyNumber, 6, 2)"/>
+ <!-- month -->
+ <xsl:text>-</xsl:text>
+ <xsl:value-of select="substring($MessyNumber, 9, 2)"/>
+ <!-- day -->
+ </td>
+ <td class="text">
+ <xsl:value-of select="substring($MessyNumber, 12, 2)"/>
+ <xsl:text>:</xsl:text>
+ <!-- hour -->
+ <xsl:value-of select="substring($MessyNumber, 15, 2)"/>
+ <xsl:text>:</xsl:text>
+ <!-- minute -->
+ <xsl:value-of select="substring($MessyNumber, 18, 2)"/>  <!-- second -->
+ </td>
+ </xsl:template>
+
+ <xsl:template name="DefinitionsResults">
+ <xsl:param name="definitionsElm"/>
+ <table border="1" cellspacing="0" cellpaddign="2" width="100%">
+ <tr>
+ <td class="title" colspan="7">OVAL Definition Results</td>
+ </tr>
+ <!-- display the result color coding table -->
+ <tr><td colspan="7"><xsl:call-template name="ResultColorTable"/></td></tr>
+
+ <tr>
+ <td class="label" align="center">ID</td>
+ <td class="label" align="center">Result</td>
+ <td class="label" align="center">Class</td>
+ <td class="label" align="center">Reference ID</td>
+ <td class="label" align="center">Title</td>
+ </tr>
+
+ <!--
+ Process true results then all others
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result = 'true']">
+ <xsl:sort select="@result" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result != 'true']">
+ <xsl:sort select="@result" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+ -->
+
+ <!--
+ Process all results in descending order by result attribute then definition id
+ <xsl:for-each select="$definitionsElm/oval-res:definition">
+ <xsl:sort select="@result" data-type="text" order="descending"/>
+ <xsl:sort select="@id" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+ -->
+
+
+ <!--
+ select the definitions with the desired result
+ sort them by their class
+ display them
+ -->
+
+ <!-- process true results -->
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result='true']">
+ <xsl:sort select="@id" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <!-- process unknown results -->
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result='unknown']">
+ <xsl:sort select="@id" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <!-- process error results -->
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result='error']">
+ <xsl:sort select="@id" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <!-- process not evaluated results -->
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result='not evaluated']">
+ <xsl:sort select="@id" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <!-- process false results -->
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result='false']">
+ <xsl:sort select="@id" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <!-- process not applicable results -->
+ <xsl:for-each select="$definitionsElm/oval-res:definition[@result='not applicable']">
+ <xsl:sort select="@id" data-type="text" order="descending"/>
+ <xsl:call-template name="Definition">
+ <xsl:with-param name="definitionElm" select="."/>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ </table>
+ </xsl:template>
+
+ <xsl:template name="Definition">
+ <xsl:param name="definitionElm"/>
+ <tr>
+ <!-- set results to alternating colors -->
+ <xsl:choose>
+ <xsl:when test="$definitionElm/@result = 'true' and position() mod 2 = 1">
+ <xsl:attribute name="class">trueA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'true' and position() mod 2 = 0">
+ <xsl:attribute name="class">trueB</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'false' and position() mod 2 = 1">
+ <xsl:attribute name="class">falseA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'false' and position() mod 2 = 0">
+ <xsl:attribute name="class">falseB</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'unknown' and position() mod 2 = 1">
+ <xsl:attribute name="class">unknownA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'unknown' and position() mod 2 = 0">
+ <xsl:attribute name="class">unknownB</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'error' and position() mod 2 = 1">
+ <xsl:attribute name="class">errorA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'error' and position() mod 2 = 0">
+ <xsl:attribute name="class">errorB</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'not applicable' and position() mod 2 = 1">
+ <xsl:attribute name="class">naA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'not applicable' and position() mod 2 = 0">
+ <xsl:attribute name="class">naB</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'not evaluated' and position() mod 2 = 1">
+ <xsl:attribute name="class">neA</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$definitionElm/@result = 'not evaluated' and position() mod 2 = 0">
+ <xsl:attribute name="class">neB</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- id -->
+
+ <td class="text" align="center">
+ <xsl:choose>
+ <!-- if the id is an oval repository id add a link otherwise don't -->
+ <xsl:when test="starts-with($definitionElm/@definition_id, 'oval:org.mitre.oval:def:')">
+ <xsl:variable name="idUrl" select="concat('http://oval.mitre.org/repository/data/getDef?id=', $definitionElm/@definition_id)"/>
+ <a>
+ <xsl:attribute name="target">_blank</xsl:attribute>
+ <xsl:attribute name="href">
+ <xsl:value-of select="$idUrl"/>
+ </xsl:attribute>
+ <xsl:value-of select="$definitionElm/@definition_id"/>
+ </a>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$definitionElm/@definition_id"/>
+ </xsl:otherwise>
+
+ </xsl:choose>
+ </td>
+
+ <!-- result -->
+ <td class="text" align="center">
+ <xsl:value-of select="$definitionElm/@result"/>
+ </td>
+
+ <!-- Class -->
+ <td class="text" align="center">
+ <xsl:for-each select="key('definition-index', @definition_id)">
+ <xsl:value-of select="@class"/>
+ </xsl:for-each>
+ </td>
+
+ <!-- reference id -->
+ <td class="text" align="center">
+ <xsl:for-each select="key('definition-index', @definition_id)">
+ <xsl:for-each select="oval-def:metadata/oval-def:reference">
+ <a>
+ <xsl:attribute name="target">_blank</xsl:attribute>
+ <xsl:attribute name="href"><xsl:value-of select="@ref_url"/></xsl:attribute>
+ <xsl:value-of select="@ref_id"/>
+ </a>
+ </xsl:for-each> 
+ </xsl:for-each> 
+ </td>
+
+ <!-- title -->
+ <td class="text">
+ <xsl:for-each select="key('definition-index', @definition_id)">
+ <xsl:value-of select="oval-def:metadata/oval-def:title"/>
+ </xsl:for-each> 
+ </td>
+ </tr>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/src/etc/secstate.conf b/src/etc/secstate.conf
index 1518ac9..9563ebe 100644
--- a/src/etc/secstate.conf
+++ b/src/etc/secstate.conf
@@ -4,6 +4,7 @@ benchmark_database=/etc/secstate/benchmarks.db
oval_schema_dir=/usr/share/ovaldi
oval_interpreter=openscap
agressivness=1
+results_stylesheet=/etc/secstate/results_to_html.xsl
[logging]
debugging=0
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 2eaa425..c3ae36e 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -83,7 +83,7 @@ class Secstate:
db = open(db_file)
database = pickle.load(db)
except IOError,e:
- secstate_log.error("Error loading database: %(file)s" % {'file':db_file})
+ self.log.error("Error loading database: %(file)s" % {'file':db_file})
return None
return database
@@ -377,6 +377,9 @@ class Secstate:
oscap.oval_syschar_model_free(sys_model)
return None
+ return res_model
+
+ def create_result_directives(self, res_model):
res_direct = oscap.oval_result_directives_new(res_model)
if res_direct == None:
self.log.error("Error creating directives")
@@ -394,9 +397,41 @@ class Secstate:
oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_FALSE, oscap.OVAL_DIRECTIVE_CONTENT_FULL)
oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_TRUE, oscap.OVAL_DIRECTIVE_CONTENT_FULL)
- return res_model
+ return res_direct
+
+ def output_results(self, res_model, res_directives, xml, html):
+ if xml:
+ if not oscap.oval_results_model_export(res_model, res_directives, xml):
+ self.log.error("Error exporting results to %(file)s" % {'file':xml})
+ return False
+
+ if html:
+ tmp = tempfile.mktemp()
+ if not oscap.oval_results_model_export(res_model, res_directives, tmp):
+ self.log.error("Error exporting results to %(file)s" % {'file':html})
+ return False
- def audit(self, interpreter, args, oval_only, def_models=None, verbose=False):
+ import libxml2
+ import libxslt
+
+ styledoc = libxml2.parseFile(self.config.get('secstate', 'results_stylesheet'))
+ style = libxslt.parseStylesheetDoc(styledoc)
+ doc = libxml2.parseFile(tmp)
+ result = style.applyStylesheet(doc, None)
+ if not style.saveResultToFilename(html, result, 0):
+ self.log.error("Error exporting results to %(file)s" % {'file':html})
+ style.freeStylesheet()
+ doc.freeDoc()
+ result.freeDoc()
+ return False
+
+ style.freeStylesheet()
+ doc.freeDoc()
+ result.freeDoc()
+
+ return True
+
+ def audit(self, interpreter, args, oval_only, def_models=None, verbose=False, xml=None, html=None):
"""
Function: Run an audit on the system agains the given definition model
Input: Interpreter to use, args for interpreter, schema to use, specific definition or template
@@ -425,6 +460,10 @@ class Secstate:
(def_results, test_results) = self.get_results(res_model)
self.parse_results(def_results, test_results)
+ if xml or html:
+ res_direct = self.create_result_directives(res_model)
+ self.output_results(res_model, res_direct, xml, html)
+
oscap.oval_definition_model_free(def_model)
else:
--
1.7.0.1
13 years, 10 months
[PATCH] Fixes the select command
by Josh Adams
No longer need to specifiy if its a rule or group when selecting
---
src/bin/secstate | 8 ++------
src/secstate/main.py | 47 +++++++++++++++++++++--------------------------
src/secstate/util.py | 3 +--
3 files changed, 24 insertions(+), 34 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 0853ac0..340eadf 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -125,14 +125,10 @@ def remove_content(arguments):
return -1
def select(arguments, value):
- parser = OptionParser(usage="secstate select [options] <benchmark> <all/id>")
- parser.add_option('-r', '--rule', action='store_true', dest='rule', default=False,
- help="Selectes a rule")
- parser.add_option('-g', '--group', action='store_true', dest='group', default=False,
- help="Selectes a group and all it contains")
+ parser = OptionParser(usage="secstate select <benchmark> <all/id>")
(options, args) = parser.parse_args(arguments)
for arg in args[1:]:
- if (not (sec_instance.select(args[0], arg, value, options.rule, options.group))):
+ if (not (sec_instance.select(args[0], arg, value))):
return -1
def audit(arguments):
diff --git a/src/secstate/main.py b/src/secstate/main.py
index ae324f2..2eaa425 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -282,7 +282,7 @@ class Secstate:
self.log.error("Could not find %(benchmark)s in database" % {'benchmark':benchmark_id})
return False
- def select(self, benchmark_id, item_id, selected, rule=False, group=False):
+ def select(self, benchmark_id, item_id, selected):
"""
Function: Set the specified item to be selected, as well as its subelements
Input: Benchmark id, id of rule or group, boolean value to set the items selected status
@@ -310,24 +310,29 @@ class Secstate:
self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_rule_get_id(item),
'val':selected})
- elif rule:
- oscap.xccdf_rule_set_selected(self.xccdf_get_item(benchmark, oscap.XCCDF_RULE, item_id), selected)
+ else:
+ item = oscap.xccdf_benchmark_get_item(benchmark, item_id)
+ if item == None:
+ self.log.error("Benchmark %(bench_id)s does not contain %(item_id)s" % {'bench_id':benchmark_id,
+ 'item_id':item_id})
+ return False
+
+ oscap.xccdf_item_set_selected(item, selected)
self.log.debug("Setting %(id)s to %(val)s" % {'id':item_id,
'val':selected})
- elif group:
- root_group = self.xccdf_get_item(benchmark, oscap.XCCDF_GROUP, item_id)
- oscap.xccdf_group_set_selected(root_group, selected)
- for item in xccdf_get_items(benchmark, oscap.XCCDF_GROUP, oscap.xccdf_group_get_content(item)):
- oscap.xccdf_group_set_selected(item, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(item),
- 'val':selected})
-
- for item in xccdf_get_items(benchmark, oscap.XCCDF_RULE, oscap.xccdf_group_get_content(item)):
- oscap.xccdf_rule_set_selected(item, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_rule_get_id(item),
- 'val':selected})
-
+ if oscap.xccdf_item_get_type(item) == oscap.XCCDF_GROUP:
+ root_group = oscap.xccdf_item_to_group(item)
+ for group in xccdf_get_items(benchmark, oscap.XCCDF_GROUP, oscap.xccdf_group_get_content(root_group)):
+ oscap.xccdf_group_set_selected(group, selected)
+ self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(group),
+ 'val':selected})
+
+ for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE, oscap.xccdf_group_get_content(root_group)):
+ oscap.xccdf_rule_set_selected(rule, selected)
+ self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(group),
+ 'val':selected})
+
if not oscap.xccdf_benchmark_export(benchmark, benchmark_file):
self.log.error("Error writing changes to file: %(file)s" % {'file':benchmark_file})
return False
@@ -335,16 +340,6 @@ class Secstate:
oscap.xccdf_benchmark_free(benchmark)
return True
- def xccdf_get_item(self, benchmark, type, id):
- items = xccdf_get_items(benchmark, type)
- for item in items:
- if type == oscap.XCCDF_RULE:
- if oscap.xccdf_rule_get_id(item) == id:
- return item
- if type == oscap.XCCDF_GROUP:
- if oscap.xccdf_group_get_id(item) == id:
- return item
-
def combine_def_models(self, target, source):
"""
Function: Add all the definitions from the source model to the target model
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 8acc3b1..60ae360 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -212,6 +212,7 @@ def xccdf_get_items(template, type, items=None):
result.extend(xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(oscap.xccdf_item_to_benchmark(item))))
elif item_type == oscap.XCCDF_GROUP:
result.extend(xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
+
elif type == oscap.XCCDF_GROUP:
if item_type == oscap.XCCDF_GROUP:
result.append(oscap.xccdf_item_to_group(item))
@@ -219,8 +220,6 @@ def xccdf_get_items(template, type, items=None):
elif item_type == oscap.XCCDF_BENCHMARK:
result.extend(xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(oscap.xccdf_item_to_benchmark(item))))
- elif item_type == oscap.XCCDF_GROUP:
- result.extend(xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
if type == oscap.XCCDF_CONTENT:
result.extend(xccdf_get_items(template, type, item))
--
1.7.0.1
13 years, 10 months
[PATCH] Audit only selected and some cleanup
by Josh Adams
Running an audit on a benchmark will only evaluate groups/rules that are
selected.
I also moved several functions into util.py as they did not need to part
of the Secstate class.
---
src/secstate/main.py | 275 +++++++------------------------------------------
src/secstate/util.py | 210 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 249 insertions(+), 236 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 987e3b1..0042832 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -26,7 +26,6 @@ import ConfigParser
import pickle
import logging
from logging.handlers import SysLogHandler
-import xml.dom.minidom
import tarfile
import zipfile
import tempfile
@@ -37,64 +36,6 @@ import time
import openscap as oscap
from secstate.util import *
-def oval_result_t_to_xccdf(oval):
- if oval == oscap.OVAL_RESULT_TRUE:
- return oscap.XCCDF_RESULT_PASS
- elif oval == oscap.OVAL_RESULT_FALSE:
- return oscap.XCCDF_RESULT_FAIL
- elif oval == oscap.OVAL_RESULT_UNKNOWN:
- return oscap.XCCDF_RESULT_UNKNOWN
- elif oval == oscap.OVAL_RESULT_ERROR:
- return oscap.XCCDF_RESULT_ERROR
- elif oval == oscap.OVAL_RESULT_NOT_EVALUATED:
- return oscap.XCCDF_RESULT_NOT_CHECKED
- elif oval == oscap.OVAL_RESULT_NOT_APPLICABLE:
- return oscap.XCCDF_RESULT_NOT_APPLICABLE
- elif oval == oscap.OVAL_ENUMERATION_INVALID:
- return 0
-
-def oval_result_t_to_xccdf(oval):
- if oval == oscap.OVAL_RESULT_TRUE:
- return oscap.XCCDF_RESULT_PASS
- elif oval == oscap.OVAL_RESULT_FALSE:
- return oscap.XCCDF_RESULT_FAIL
- elif oval == oscap.OVAL_RESULT_UNKNOWN:
- return oscap.XCCDF_RESULT_UNKNOWN
- elif oval == oscap.OVAL_RESULT_ERROR:
- return oscap.XCCDF_RESULT_ERROR
- elif oval == oscap.OVAL_RESULT_NOT_EVALUATED:
- return oscap.XCCDF_RESULT_NOT_CHECKED
- elif oval == oscap.OVAL_RESULT_NOT_APPLICABLE:
- return oscap.XCCDF_RESULT_NOT_APPLICABLE
- elif oval == oscap.OVAL_ENUMERATION_INVALID:
- return 0
-
-def policy_callback(model, href, id, usr):
- defn = oscap.oval_result_system_get_definition(usr['rsystem'], href)
- if defn == None:
- return False
-
- result = oscap.oval_result_definition_eval(defn)
- print "Definition: %s\tresult: %s" % (href, oscap.oval_result_get_text(result))
- ritem = oscap.xccdf_policy_model_get_result_by_id(model, usr['result_id'])
- if ritem == None:
- pass
- else:
- rule = oscap.xccdf_item_to_rule(oscap.xccdf_benchmark_get_item(oscap.xccdf_policy_model_get_benchmark(model), id))
- rule_ritem = oscap.xccdf_rule_result_new()
- oscap.xccdf_rule_result_set_result(rule_ritem, oval_result_t_to_xccdf(result))
- oscap.xccdf_rule_result_set_idref(rule_ritem, id)
- oscap.xccdf_rule_result_set_time(rule_ritem, time.time())
- oscap.xccdf_rule_result_set_weight(rule_ritem, oscap.xccdf_rule_get_weight(rule))
- oscap.xccdf_rule_result_set_severity(rule_ritem, oscap.xccdf_rule_get_severity(rule))
- oscap.xccdf_rule_result_set_role(rule_ritem, oscap.xccdf_rule_get_role(rule))
- checks = oscap.xccdf_rule_get_checks(rule)
- for check in xccdf_check_generator(checks):
- oscap.xccdf_rule_result_add_check(rule_ritem, check)
- oscap.xccdf_result_add_rule_result(ritem, rule_ritem)
-
- return True
-
class Secstate:
def __init__(self, conf_file):
self.setConfigFile(conf_file)
@@ -149,14 +90,6 @@ class Secstate:
else:
return {}
- def is_benchmark(self, benchmark):
- tree = xml.dom.minidom.parse(benchmark)
- return (tree.getElementsByTagName("Benchmark") != [])
-
- def get_benchmark_id(self, benchmark):
- tree = xml.dom.minidom.parse(benchmark)
- return tree.getElementsByTagName("Benchmark")[0].getAttribute("id")
-
def import_benchmark(self, benchmark_file, oval_path="", store_path=None):
"""
Function: Imports an XCCDF benchmark
@@ -178,7 +111,7 @@ class Secstate:
return (None, None)
def_model = oscap.oval_definition_model_new()
- oval_files = self.xccdf_get_refs(benchmark)
+ oval_files = xccdf_get_refs(benchmark)
for oval in list(set(oval_files)):
tmp = oscap.oval_definition_model_import(os.path.join(oval_path, oval))
if tmp == None:
@@ -199,7 +132,7 @@ class Secstate:
return (None, None)
if store_path != None:
- folder = self.get_benchmark_id(benchmark_file)
+ folder = get_benchmark_id(benchmark_file)
# Map the benchmark id to the actual xccdf document
self.database[folder] = os.path.split(benchmark_file)[1]
try:
@@ -267,7 +200,7 @@ class Secstate:
directory = None
for extracted_file in os.listdir(extract_path):
print extracted_file
- if self.is_benchmark(os.path.join(extract_path, extracted_file)):
+ if is_benchmark(os.path.join(extract_path, extracted_file)):
xccdf = extracted_file
if xccdf == None:
@@ -293,7 +226,7 @@ class Secstate:
file_type = mimetypes.guess_type(content)
if file_type[0] == "text/xml":
if not (oval or xccdf):
- if self.is_benchmark(content):
+ if is_benchmark(content):
xccdf = True
else:
oval = True
@@ -367,12 +300,12 @@ class Secstate:
return False
if item_id == 'all':
- for item in self.xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
+ for item in xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
oscap.xccdf_group_set_selected(item, selected)
self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(item),
'val':selected})
- for item in self.xccdf_get_items(benchmark, oscap.XCCDF_RULE):
+ for item in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
oscap.xccdf_rule_set_selected(item, selected)
self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_rule_get_id(item),
'val':selected})
@@ -390,7 +323,7 @@ class Secstate:
self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(item),
'val':selected})
- for item in self.xccdf_get_items(benchmark, oscap.XCCDF_RULE, oscap.xccdf_group_get_content(item)):
+ for item in xccdf_get_items(benchmark, oscap.XCCDF_RULE, oscap.xccdf_group_get_content(item)):
oscap.xccdf_rule_set_selected(item, selected)
self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_rule_get_id(item),
'val':selected})
@@ -402,59 +335,8 @@ class Secstate:
oscap.xccdf_benchmark_free(benchmark)
return True
- def xccdf_get_items(self, template, type, items=None):
- """
- Function: Get all items of 'type' from an XCCDF document
- Input: xccdf_benchmark, xccdf_type_t, xccdf_item_iterator
- Output: A list of all items of "type" specified in the template
- """
- result = []
-
- if items == None:
- result.extend(self.xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(template)))
-
- else:
- for item in xccdf_item_generator(items):
- item_type = oscap.xccdf_item_get_type(item)
-
- if type == oscap.XCCDF_BENCHMARK:
- if item_type == oscap.XCCDF_BENCHMARK:
- result.append(oscap.xccdf_item_to_benchmark(item))
- elif item_type == oscap.XCCDF_PROFILE:
- result.append(oscap.xccdf_profile_get_benchmark(oscap.xccdf_item_to_protemplate(item)))
-
- elif type == oscap.XCCDF_PROFILE:
- if item_type == oscap.XCCDF_PROFILE:
- pass
-
- # Rule is the base element where we can get checks and definition id's
- elif type == oscap.XCCDF_RULE:
- if item_type == oscap.XCCDF_RULE:
- result.append(oscap.xccdf_item_to_rule(item))
-
- elif item_type == oscap.XCCDF_BENCHMARK:
- result.extend(self.xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(oscap.xccdf_item_to_benchmark(item))))
- elif item_type == oscap.XCCDF_GROUP:
- result.extend(self.xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
- elif type == oscap.XCCDF_GROUP:
- if item_type == oscap.XCCDF_GROUP:
- result.append(oscap.xccdf_item_to_group(item))
- result.extend(self.xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
- elif item_type == oscap.XCCDF_BENCHMARK:
- result.extend(self.xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(oscap.xccdf_item_to_benchmark(item))))
-
- elif item_type == oscap.XCCDF_GROUP:
- result.extend(self.xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
- if type == oscap.XCCDF_CONTENT:
- result.extend(self.xccdf_get_items(template, type, item))
-
- if type == oscap.XCCDF_ITEM:
- result.extend(self.xccdf_get_items(template, type, item))
-
- return result
-
def xccdf_get_item(self, benchmark, type, id):
- items = self.xccdf_get_items(benchmark, type)
+ items = xccdf_get_items(benchmark, type)
for item in items:
if type == oscap.XCCDF_RULE:
if oscap.xccdf_rule_get_id(item) == id:
@@ -463,42 +345,14 @@ class Secstate:
if oscap.xccdf_group_get_id(item) == id:
return item
- def xccdf_get_refs(self, benchmark):
- """
- Function: Get all referenced OVAL files from an XCCDF document
- Input: xccdf_benchmark
- Output: Returns a list of flies referenced in an XCCDF benchmark
- """
- refs = []
- rules = self.xccdf_get_items(benchmark, oscap.XCCDF_RULE)
- for rule in rules:
- for check in xccdf_check_generator(oscap.xccdf_rule_get_checks(rule)):
- for ref in xccdf_check_content_ref_generator(oscap.xccdf_check_get_content_refs(check)):
- refs.append(oscap.xccdf_check_content_ref_get_href(ref))
- return refs
-
- def model_get_defs(self, model):
- """
- Function: Get all the OVAL definitions in a definition model
- Input: oval_definition_model
- Output: Returns a list of definitions in the model
- """
- definitions = []
-
- defs = oscap.oval_definition_model_get_definitions(model)
- for definition in oval_definition_generator(defs):
- definitions.append(definition)
-
- return definitions
-
def combine_def_models(self, target, source):
"""
Function: Add all the definitions from the source model to the target model
Input: Two oval_definition_model's
Output: Success or failure of combination
"""
- new_defs = self.model_get_defs(source)
- for defn in new_defs:
+ definitions = oscap.oval_definition_model_get_definitions(source)
+ for defn in oval_definition_generator(definitions):
if oscap.oval_definition_model_get_definition(target, oscap.oval_definition_get_id(defn)) == None:
new_def = oscap.oval_definition_clone(target, defn)
if new_def == None:
@@ -591,13 +445,32 @@ class Secstate:
elif os.path.isfile(arg):
benchmark_path = arg
- (benchmark, def_model) = self.import_content(benchmark_path)
- if (benchmark == None) or (def_model == None):
+ (benchmark, tmp_model) = self.import_content(benchmark_path)
+ if (benchmark == None) or (tmp_model == None):
self.log.error("Error importing benchmark: %(bench)s" % {'bench':arg})
return False
+ #Only audit selected rules
+ def_model = get_selected_checks(benchmark, tmp_model)
+ definitions = oscap.oval_definition_model_get_definitions(def_model)
+ print "Getting selected definitions"
+ for defn in oval_definition_generator(definitions):
+ print oscap.oval_definition_get_id(defn)
+
if interpreter == "openscap":
policy_model = oscap.xccdf_policy_model_new(benchmark)
+
+ #Create a TestResult and attach it to the policy model
+ result_id = "secstate_audit"
+ ritem = oscap.xccdf_result_new()
+ oscap.xccdf_result_set_id(ritem, result_id)
+ oscap.xccdf_result_set_benchmark_uri(ritem, benchmark_path)
+ title = oscap.oscap_text_new()
+ oscap.oscap_text_set_text(title, "Secstate Audit Result")
+ oscap.xccdf_result_add_title(ritem, title)
+ oscap.xccdf_result_set_start_time(ritem, time.time())
+ oscap.xccdf_policy_model_add_result(policy_model, ritem)
+
policies = oscap.xccdf_policy_model_get_policies(policy_model)
policy = None
if (oscap.xccdf_policy_iterator_has_more(policies)):
@@ -619,32 +492,23 @@ class Secstate:
# before calling next
rsystem = oscap.oval_result_system_iterator_next(systems)
- usr = {'rsystem':rsystem, 'result_id':"secstate_audit"}
- ritem = oscap.xccdf_result_new()
- oscap.xccdf_result_set_id(ritem, usr['result_id'])
- oscap.xccdf_result_set_benchmark_uri(ritem, benchmark_path)
- title = oscap.oscap_text_new()
- oscap.oscap_text_set_text(title, "Secstate Audit Result")
- oscap.xccdf_result_add_title(ritem, title)
- oscap.xccdf_policy_model_add_result(policy_model, ritem)
-
+ usr = {'rsystem':rsystem, 'result_id':result_id}
reg = oscap.xccdf_policy_model_register_callback_py(policy_model,
"http://oval.mitre.org/XMLSchema/oval-definitions-5",
policy_callback, usr)
-
if policy != None:
- print "evaluating"
oscap.xccdf_policy_evaluate(policy)
xccdf_results = oscap.xccdf_policy_model_get_results(policy_model)
for result in xccdf_result_generator(xccdf_results):
+ result = oscap.xccdf_result_iterator_next(xccdf_results)
+ oscap.xccdf_result_set_end_time(result, time.time())
oscap.xccdf_benchmark_add_result(benchmark, result)
- oscap.xccdf_benchmark_export(benchmark, 'test.xml')
- oscap.xccdf_policy_model_free(policy_model)
- oscap.oval_results_model_free(res_model)
- oscap.xccdf_benchmark_free(benchmark)
- oscap.oval_definition_model_free(def_model)
+ #oscap.xccdf_policy_model_free(policy_model)
+ #oscap.oval_results_model_free(res_model)
+ #oscap.xccdf_benchmark_free(benchmark)
+ #oscap.oval_definition_model_free(def_model)
return True
@@ -747,13 +611,13 @@ class Secstate:
Side Effects: Prints out the results of the search
"""
(benchmark, def_model) = self.import_benchmark(os.path.join(self.config.get('secstate', 'benchmark_dir'), id))
- for group in self.xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
+ for group in xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
title = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_group_get_title(group)))
description = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_group_get_description(group)))
if (search_string in title) or (search_string in description):
print "%(title)s\t%(description)s" % {'title':title, 'description':description}
- for rule in self.xccdf_get_items(benchmark, oscap.XCCDF_RULE):
+ for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
title = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_rule_get_title(rule)))
description = oscap.oscap_text_get_text(oscap.oscap_text_iterator_next(oscap.xccdf_rule_get_description(rule)))
if (search_string in title) or (search_string in description):
@@ -763,65 +627,6 @@ class Secstate:
for key in self.database:
print key
- def xccdf_get_fixes(self, benchmark, ignore_ids=[]):
- """
- Function: Get all fixes for rules in the XCCDF document
- Input: xccdf_benchmark
- Output: Returns a list of fixes for all rules in the XCCDF benchmark
- """
-
- rules = self.xccdf_get_items(benchmark, oscap.XCCDF_RULE)
- fixes = []
- for rule in rules:
- if oscap.xccdf_rule_get_id(rule) not in ignore_ids:
- fixes.extend(xccdf_fix_list(oscap.xccdf_rule_get_fixes(rule)))
- return fixes
-
- def parse_puppet_fixes(self, benchmark, ignore_ids=[]):
- fixes = self.xccdf_get_fixes(benchmark, ignore_ids)
- all_puppet = {'classes' : set(), 'environment' : "", 'parameters' : {}}
-
- line_reg = re.compile(r'\s*(class|environment|parameter)\s*:\s*((\S+)\s*:\s*(\S+)|\S+)\s*', re.IGNORECASE)
-
- for fix in fixes:
- if oscap.xccdf_fix_get_system(fix) == 'urn:xccdf:fix:script:puppet':
- content = oscap.xccdf_fix_get_content(fix)
-
- for line in content.split('\n'):
- mtch = line_reg.match(line)
- if mtch:
- if mtch.group(1).lower() == 'class':
- # parsed a single class
- all_puppet['classes'].add(mtch.group(2))
- elif mtch.group(1).lower() == 'environment':
- # parse a single environment
- all_puppet['environment'] = mtch.group(2)
- elif mtch.group(1).lower() == 'parameter':
- # parse a single parameter
- all_puppet['parameters'][mtch.group(3)] = mtch.group(4)
- else:
- #assume comment line
- pass
-
- all_puppet['classes'] = list(all_puppet['classes'])
-
- return all_puppet
-
- @staticmethod
- def dict_to_external(puppet_dict):
- content = ['---','classes:']
- for item in puppet_dict['classes']:
- content.append(' - %s' % item)
- environ = 'production'
- if puppet_dict['environment']:
- environ = puppet_dict['environment']
- content.append('environment: %s' % environ)
- content.append('parameters:')
- for item in puppet_dict['parameters']:
- content.append(' %s: "%s"' % (item, puppet_dict['parameters'][item]))
-
- return '\n'.join(content)
-
def get_passed_result_ids(self, xccdf_results):
if xccdf_results == None:
return set()
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 545418e..ae0aad8 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -19,6 +19,8 @@
# File: util.py
# This file is the core implementation of the secstate tool.
+import xml.dom.minidom
+
import openscap as oscap
def iterator_to_generator_and_list(iterator_type):
@@ -63,4 +65,210 @@ oval_generator_list_types = ['oval_affected', 'oval_behavior', 'oval_component',
for type in oval_generator_list_types:
gen_func, list_func = iterator_to_generator_and_list('%s_iterator' % type)
globals()['%s_generator' % type] = gen_func
- globals()['%s_list' % type] = list_func
\ No newline at end of file
+ globals()['%s_list' % type] = list_func
+
+def oval_result_t_to_xccdf(oval):
+ if oval == oscap.OVAL_RESULT_TRUE:
+ return oscap.XCCDF_RESULT_PASS
+ elif oval == oscap.OVAL_RESULT_FALSE:
+ return oscap.XCCDF_RESULT_FAIL
+ elif oval == oscap.OVAL_RESULT_UNKNOWN:
+ return oscap.XCCDF_RESULT_UNKNOWN
+ elif oval == oscap.OVAL_RESULT_ERROR:
+ return oscap.XCCDF_RESULT_ERROR
+ elif oval == oscap.OVAL_RESULT_NOT_EVALUATED:
+ return oscap.XCCDF_RESULT_NOT_CHECKED
+ elif oval == oscap.OVAL_RESULT_NOT_APPLICABLE:
+ return oscap.XCCDF_RESULT_NOT_APPLICABLE
+ elif oval == oscap.OVAL_ENUMERATION_INVALID:
+ return 0
+
+def oval_result_t_to_xccdf(oval):
+ if oval == oscap.OVAL_RESULT_TRUE:
+ return oscap.XCCDF_RESULT_PASS
+ elif oval == oscap.OVAL_RESULT_FALSE:
+ return oscap.XCCDF_RESULT_FAIL
+ elif oval == oscap.OVAL_RESULT_UNKNOWN:
+ return oscap.XCCDF_RESULT_UNKNOWN
+ elif oval == oscap.OVAL_RESULT_ERROR:
+ return oscap.XCCDF_RESULT_ERROR
+ elif oval == oscap.OVAL_RESULT_NOT_EVALUATED:
+ return oscap.XCCDF_RESULT_NOT_CHECKED
+ elif oval == oscap.OVAL_RESULT_NOT_APPLICABLE:
+ return oscap.XCCDF_RESULT_NOT_APPLICABLE
+ elif oval == oscap.OVAL_ENUMERATION_INVALID:
+ return 0
+
+def policy_callback(model, href, id, usr):
+ defn = oscap.oval_result_system_get_definition(usr['rsystem'], href)
+ if defn == None:
+ return False
+
+ result = oscap.oval_result_definition_eval(defn)
+ print "Definition: %s\tresult: %s" % (href, oscap.oval_result_get_text(result))
+ ritem = oscap.xccdf_policy_model_get_result_by_id(model, usr['result_id'])
+ if ritem == None:
+ pass
+ else:
+ rule = oscap.xccdf_item_to_rule(oscap.xccdf_benchmark_get_item(oscap.xccdf_policy_model_get_benchmark(model), id))
+ rule_ritem = oscap.xccdf_rule_result_new()
+ oscap.xccdf_rule_result_set_result(rule_ritem, oval_result_t_to_xccdf(result))
+ oscap.xccdf_rule_result_set_idref(rule_ritem, id)
+ oscap.xccdf_rule_result_set_time(rule_ritem, time.time())
+ oscap.xccdf_rule_result_set_weight(rule_ritem, oscap.xccdf_rule_get_weight(rule))
+ oscap.xccdf_rule_result_set_severity(rule_ritem, oscap.xccdf_rule_get_severity(rule))
+ oscap.xccdf_rule_result_set_role(rule_ritem, oscap.xccdf_rule_get_role(rule))
+ checks = oscap.xccdf_rule_get_checks(rule)
+ for check in xccdf_check_generator(checks):
+ oscap.xccdf_rule_result_add_check(rule_ritem, check)
+ oscap.xccdf_result_add_rule_result(ritem, rule_ritem)
+
+ return True
+
+def get_selected_checks(benchmark, oval):
+ """
+ Function: Get all the selected checks from a benchmark
+ Input: xccdf_benchmark
+ Ouput: oval_definition_model only containing checks that were selected
+ """
+ new_oval = oscap.oval_definition_model_new()
+ for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
+ if oscap.xccdf_rule_get_selected(rule):
+ checks = oscap.xccdf_rule_get_checks(rule)
+ for check in xccdf_check_generator(checks):
+ refs = oscap.xccdf_check_get_content_refs(check)
+ for ref in xccdf_check_content_ref_generator(refs):
+ oscap.oval_definition_clone(new_oval, oscap.oval_definition_model_get_definition(oval, oscap.xccdf_check_content_ref_get_name(ref)))
+
+ return new_oval
+
+def is_benchmark(benchmark):
+ tree = xml.dom.minidom.parse(benchmark)
+ return (tree.getElementsByTagName("Benchmark") != [])
+
+def get_benchmark_id(benchmark):
+ tree = xml.dom.minidom.parse(benchmark)
+ return tree.getElementsByTagName("Benchmark")[0].getAttribute("id")
+
+def xccdf_get_items(template, type, items=None):
+ """
+ Function: Get all items of 'type' from an XCCDF document
+ Input: xccdf_benchmark, xccdf_type_t, xccdf_item_iterator
+ Output: A list of all items of "type" specified in the template
+ """
+ result = []
+
+ if items == None:
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(template)))
+
+ else:
+ for item in xccdf_item_generator(items):
+ item_type = oscap.xccdf_item_get_type(item)
+
+ if type == oscap.XCCDF_BENCHMARK:
+ if item_type == oscap.XCCDF_BENCHMARK:
+ result.append(oscap.xccdf_item_to_benchmark(item))
+ elif item_type == oscap.XCCDF_PROFILE:
+ result.append(oscap.xccdf_profile_get_benchmark(oscap.xccdf_item_to_protemplate(item)))
+
+ elif type == oscap.XCCDF_PROFILE:
+ if item_type == oscap.XCCDF_PROFILE:
+ pass
+
+ # Rule is the base element where we can get checks and definition id's
+ elif type == oscap.XCCDF_RULE:
+ if item_type == oscap.XCCDF_RULE:
+ result.append(oscap.xccdf_item_to_rule(item))
+
+ elif item_type == oscap.XCCDF_BENCHMARK:
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(oscap.xccdf_item_to_benchmark(item))))
+ elif item_type == oscap.XCCDF_GROUP:
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
+ elif type == oscap.XCCDF_GROUP:
+ if item_type == oscap.XCCDF_GROUP:
+ result.append(oscap.xccdf_item_to_group(item))
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
+ elif item_type == oscap.XCCDF_BENCHMARK:
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_benchmark_get_content(oscap.xccdf_item_to_benchmark(item))))
+
+ elif item_type == oscap.XCCDF_GROUP:
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))))
+ if type == oscap.XCCDF_CONTENT:
+ result.extend(xccdf_get_items(template, type, item))
+
+ if type == oscap.XCCDF_ITEM:
+ result.extend(xccdf_get_items(template, type, item))
+
+ return result
+
+def xccdf_get_refs(benchmark):
+ """
+ Function: Get all referenced OVAL files from an XCCDF document
+ Input: xccdf_benchmark
+ Output: Returns a list of flies referenced in an XCCDF benchmark
+ """
+ refs = []
+ rules = xccdf_get_items(benchmark, oscap.XCCDF_RULE)
+ for rule in rules:
+ for check in xccdf_check_generator(oscap.xccdf_rule_get_checks(rule)):
+ for ref in xccdf_check_content_ref_generator(oscap.xccdf_check_get_content_refs(check)):
+ refs.append(oscap.xccdf_check_content_ref_get_href(ref))
+ return refs
+
+def xccdf_get_fixes(benchmark, ignore_ids=[]):
+ """
+ Function: Get all fixes for rules in the XCCDF document
+ Input: xccdf_benchmark
+ Output: Returns a list of fixes for all rules in the XCCDF benchmark
+ """
+
+ rules = xccdf_get_items(benchmark, oscap.XCCDF_RULE)
+ fixes = []
+ for rule in rules:
+ if oscap.xccdf_rule_get_id(rule) not in ignore_ids:
+ fixes.extend(xccdf_fix_list(oscap.xccdf_rule_get_fixes(rule)))
+ return fixes
+
+def parse_puppet_fixes(benchmark, ignore_ids=[]):
+ fixes = xccdf_get_fixes(benchmark, ignore_ids)
+ all_puppet = {'classes' : set(), 'environment' : "", 'parameters' : {}}
+
+ line_reg = re.compile(r'\s*(class|environment|parameter)\s*:\s*((\S+)\s*:\s*(\S+)|\S+)\s*', re.IGNORECASE)
+
+ for fix in fixes:
+ if oscap.xccdf_fix_get_system(fix) == 'urn:xccdf:fix:script:puppet':
+ content = oscap.xccdf_fix_get_content(fix)
+
+ for line in content.split('\n'):
+ mtch = line_reg.match(line)
+ if mtch:
+ if mtch.group(1).lower() == 'class':
+ # parsed a single class
+ all_puppet['classes'].add(mtch.group(2))
+ elif mtch.group(1).lower() == 'environment':
+ # parse a single environment
+ all_puppet['environment'] = mtch.group(2)
+ elif mtch.group(1).lower() == 'parameter':
+ # parse a single parameter
+ all_puppet['parameters'][mtch.group(3)] = mtch.group(4)
+ else:
+ #assume comment line
+ pass
+
+ all_puppet['classes'] = list(all_puppet['classes'])
+
+ return all_puppet
+
+def dict_to_external(puppet_dict):
+ content = ['---','classes:']
+ for item in puppet_dict['classes']:
+ content.append(' - %s' % item)
+ environ = 'production'
+ if puppet_dict['environment']:
+ environ = puppet_dict['environment']
+ content.append('environment: %s' % environ)
+ content.append('parameters:')
+ for item in puppet_dict['parameters']:
+ content.append(' %s: "%s"' % (item, puppet_dict['parameters'][item]))
+
+ return '\n'.join(content)
--
1.7.0.1
13 years, 10 months
[PATCH] Add util.py to the spec file.
by mkeeler@tresys.com
From: Matt Keeler <mkeeler(a)tresys.com>
---
dist/secstate.spec | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/dist/secstate.spec b/dist/secstate.spec
index 3c897ce..555066c 100644
--- a/dist/secstate.spec
+++ b/dist/secstate.spec
@@ -50,6 +50,7 @@ rm -rf $RPM_BUILD_DIR/%{name}-%{version}
%dir %{python_sitelib}/secstate
%{python_sitelib}/secstate/__init__.py*
%{python_sitelib}/secstate/main.py*
+%{python_sitelib}/secstate/util.py*
%post
--
1.6.5.2
13 years, 10 months