Blob Blame History Raw
From f90f02bfa2d31f56b25238d750c4746d964aea42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Tue, 26 Nov 2019 13:52:36 +0100
Subject: [PATCH 1/2] Show check identifiers at multi-check rules

Shows OVAL Definition ID and Title when an XCCDF rule uses
multi-check='true'. The multi-check is used in rule
security_patches_up_to_date in SCAP 1.3 datastreams. It wasn't possible
to see which vulnerabilities have been found because all the checks have
the same XCCDF rule ID.

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1771438

Fixes: #1320
---
 src/OVAL/oval_agent.c                         | 35 ++++++++++----
 src/XCCDF_POLICY/public/xccdf_policy.h        | 18 ++++++-
 src/XCCDF_POLICY/xccdf_policy.c               | 48 +++++++++++--------
 src/XCCDF_POLICY/xccdf_policy_engine.c        |  4 +-
 src/XCCDF_POLICY/xccdf_policy_engine_priv.h   |  2 +-
 src/XCCDF_POLICY/xccdf_policy_model_priv.h    |  1 +
 ...eck_content_ref_without_name_attr.oval.xml |  4 +-
 .../test_xccdf_check_multi_check2.sh          |  6 +++
 .../test_xccdf_check_multi_check2.xccdf.xml   |  1 +
 utils/oscap-xccdf.c                           |  8 ++++
 10 files changed, 92 insertions(+), 35 deletions(-)

diff --git a/src/OVAL/oval_agent.c b/src/OVAL/oval_agent.c
index a962fe379..986295bf6 100644
--- a/src/OVAL/oval_agent.c
+++ b/src/OVAL/oval_agent.c
@@ -593,19 +593,34 @@ _oval_agent_list_definitions(void *usr, xccdf_policy_engine_query_t query_type,
 {
 	__attribute__nonnull__(usr);
 	struct oval_agent_session *sess = (struct oval_agent_session *) usr;
-	if (query_type != POLICY_ENGINE_QUERY_NAMES_FOR_HREF || (query_data != NULL && strcmp(sess->filename, (const char *) query_data)))
+	if (query_data != NULL && strcmp(sess->filename, (const char *) query_data)) {
 		return NULL;
-	struct oval_definition_iterator *iterator = oval_definition_model_get_definitions(sess->def_model);
-	struct oscap_stringlist *result = oscap_stringlist_new();
-	struct oval_definition *oval_def;
-
-	while (oval_definition_iterator_has_more(iterator)) {
-		oval_def = oval_definition_iterator_next(iterator);
-		oscap_stringlist_add_string(result, oval_definition_get_id(oval_def));
 	}
+	if (query_type == POLICY_ENGINE_QUERY_NAMES_FOR_HREF) {
+		struct oval_definition_iterator *iterator = oval_definition_model_get_definitions(sess->def_model);
+		struct oscap_stringlist *result = oscap_stringlist_new();
+
+		while (oval_definition_iterator_has_more(iterator)) {
+			struct oval_definition *oval_def = oval_definition_iterator_next(iterator);
+			oscap_stringlist_add_string(result, oval_definition_get_id(oval_def));
+		}
+
+		oval_definition_iterator_free(iterator);
+		return result;
+	} else if (query_type == POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF) {
+		struct oval_definition_iterator *iterator = oval_definition_model_get_definitions(sess->def_model);
+		struct oscap_list *result = oscap_list_new();
 
-	oval_definition_iterator_free(iterator);
-	return result;
+		while (oval_definition_iterator_has_more(iterator)) {
+			struct oval_definition *oval_def = oval_definition_iterator_next(iterator);
+			oscap_list_add(result, oval_def);
+		}
+
+		oval_definition_iterator_free(iterator);
+		return result;
+	} else {
+		return NULL;
+	}
 }
 
 bool xccdf_policy_model_register_engine_oval(struct xccdf_policy_model * model, struct oval_agent_session * usr)
diff --git a/src/SCE/Makefile.am b/src/SCE/Makefile.am
index 0c90c6da0..5849cf1a0 100644
--- a/src/SCE/Makefile.am
+++ b/src/SCE/Makefile.am
@@ -12,7 +12,8 @@ AM_CPPFLAGS =	@xml2_CFLAGS@ \
 		-I$(top_srcdir)/src/source/public \
 		-I$(top_srcdir)/src/XCCDF_POLICY/public \
 		-I$(top_srcdir)/src/XCCDF/public \
-		-I$(top_srcdir)/src/CPE/public
+		-I$(top_srcdir)/src/CPE/public \
+		-I$(top_srcdir)/src/OVAL/public
 
 AM_LDFLAGS = @xml2_LIBS@
 
diff --git a/src/XCCDF_POLICY/public/xccdf_policy.h b/src/XCCDF_POLICY/public/xccdf_policy.h
index aa2525e2b..5e85c476c 100644
--- a/src/XCCDF_POLICY/public/xccdf_policy.h
+++ b/src/XCCDF_POLICY/public/xccdf_policy.h
@@ -35,6 +35,7 @@
 #include <stdbool.h>
 #include <time.h>
 #include <oscap.h>
+#include "oval_definitions.h"
 
 /**
  * @struct xccdf_policy_model
@@ -69,6 +70,7 @@ struct xccdf_policy_iterator;
  */
 typedef enum {
 	POLICY_ENGINE_QUERY_NAMES_FOR_HREF = 1,		/// Considering xccdf:check-content-ref, what are possible @name attributes for given href?
+	POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF = 2,	/// Considering xccdf:check-content-ref, what are OVAL definitions for given href?
 } xccdf_policy_engine_query_t;
 
 /**
@@ -80,9 +82,11 @@ typedef enum {
  * is always user data as registered. Second argument defines the query. Third argument is
  * dependent on query and defined as follows:
  *  - (const char *)href -- for POLICY_ENGINE_QUERY_NAMES_FOR_HREF
+ *  - (const char *)href -- for POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF
  *
  * Expected return type depends also on query as follows:
- *  - (struct oscap_stringlists *) -- for POLICY_ENGINE_QUERY_NAMES_FOR_HREF
+ *  - (struct oscap_stringlist *) -- for POLICY_ENGINE_QUERY_NAMES_FOR_HREF
+ *  - (struct oscap_list *) -- for POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF
  *  - NULL shall be returned if the function doesn't understand the query.
  */
 typedef void *(*xccdf_policy_engine_query_fn) (void *, xccdf_policy_engine_query_t, void *);
@@ -289,6 +293,19 @@ typedef int (*policy_reporter_start)(struct xccdf_rule *, void *);
  */
 bool xccdf_policy_model_register_start_callback(struct xccdf_policy_model * model, policy_reporter_start func, void * usr);
 
+typedef int (*policy_reporter_multicheck)(struct oval_definition*, void *);
+/**
+ * Function to register callback for checking system that will be called
+ * DURING each rule evaluation if rule sets multi-check="true".
+ * @param model XCCDF Policy Model
+ * @param func Callback - pointer to function called by XCCDF Policy system when rule parsed
+ * @param usr optional parameter for passing user data to callback
+ * @memberof xccdf_policy_model
+ * @return true if callback registered successfully, false otherwise
+ */
+bool xccdf_policy_model_register_multicheck_callback(struct xccdf_policy_model *model, policy_reporter_multicheck func, void *usr);
+
+
 /************************************************************/
 /**
  * @name Getters
diff --git a/src/XCCDF_POLICY/xccdf_policy.c b/src/XCCDF_POLICY/xccdf_policy.c
index b406d7f59..079395c85 100644
--- a/src/XCCDF_POLICY/xccdf_policy.c
+++ b/src/XCCDF_POLICY/xccdf_policy.c
@@ -378,20 +378,19 @@ xccdf_policy_evaluate_cb(struct xccdf_policy * policy, const char * sysname, con
 }
 
 /**
- * Find all posible names for given check-content-ref/@href, considering also the check/@system.
- * This is usefull for multi-check="true" feature.
- * @return list of names (even empty) if the given href found, NULL otherwise.
+ * Find all possible names for given check-content-ref/@href, considering also the check/@system.
+ * This is useful for multi-check="true" feature.
+ * @return list of OVAL definitions if the given href found, NULL otherwise.
  */
-static struct oscap_stringlist *
-_xccdf_policy_get_namesfor_href(struct xccdf_policy *policy, const char *sysname, const char *href)
+static struct oscap_list *_xccdf_policy_get_oval_definitions_for_href(struct xccdf_policy *policy, const char *sysname, const char *href)
 {
 	struct oscap_iterator *cb_it = _xccdf_policy_get_engines_by_sysname(policy, sysname);
-	struct oscap_stringlist *result = NULL;
+	struct oscap_list *result = NULL;
 	while (oscap_iterator_has_more(cb_it) && result == NULL) {
 		struct xccdf_policy_engine *engine = (struct xccdf_policy_engine *) oscap_iterator_next(cb_it);
 		if (engine == NULL)
 			break;
-		result = xccdf_policy_engine_query(engine, POLICY_ENGINE_QUERY_NAMES_FOR_HREF, (void *) href);
+		result = xccdf_policy_engine_query(engine, POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF, (void *) href);
 	}
 	oscap_iterator_free(cb_it);
 	return result;
@@ -1047,24 +1046,27 @@ _xccdf_policy_rule_evaluate(struct xccdf_policy * policy, const struct xccdf_rul
 
 		if (content_name == NULL && xccdf_check_get_multicheck(check)) {
 			// parent element is Rule, @multi-check is required
-			struct oscap_stringlist *names = _xccdf_policy_get_namesfor_href(policy, system_name, href);
-			if (names != NULL) {
+			struct oscap_list *oval_definition_list = _xccdf_policy_get_oval_definitions_for_href(policy, system_name, href);
+			if (oval_definition_list != NULL) {
 				// multi-check is supported by checking-engine
-				struct oscap_string_iterator *name_it = oscap_stringlist_get_strings(names);
-				if (!oscap_string_iterator_has_more(name_it)) {
+				struct oscap_iterator *oval_definition_iterator = oscap_iterator_new(oval_definition_list);
+				if (!oscap_iterator_has_more(oval_definition_iterator)) {
 					// Super special case when oval file contains no definitions
 					// thus multi-check shall yield zero rule-results.
 					report = _xccdf_policy_report_rule_result(policy, result, rule, check, XCCDF_RESULT_UNKNOWN, "No definitions found for @multi-check.");
-					oscap_string_iterator_free(name_it);
-					oscap_stringlist_free(names);
+					oscap_iterator_free(oval_definition_iterator);
+					oscap_list_free(oval_definition_list, NULL);
 					xccdf_check_content_ref_iterator_free(content_it);
 					oscap_list_free(bindings, (oscap_destruct_func) xccdf_value_binding_free);
 					return report;
 				}
-				while (oscap_string_iterator_has_more(name_it)) {
-					const char *name = oscap_string_iterator_next(name_it);
+				while (oscap_iterator_has_more(oval_definition_iterator)) {
+					struct oval_definition *oval_definition = oscap_iterator_next(oval_definition_iterator);
+					if ((report = xccdf_policy_report_cb(policy, XCCDF_POLICY_OUTCB_MULTICHECK, (void *) oval_definition)) != 0) {
+						break;
+					}
 					struct xccdf_check *cloned_check = xccdf_check_clone(check);
-					xccdf_check_inject_content_ref(cloned_check, content, name);
+					xccdf_check_inject_content_ref(cloned_check, content, oval_definition_get_id(oval_definition));
 					int inner_ret = xccdf_policy_check_evaluate(policy, cloned_check);
 					if (inner_ret == -1) {
 						xccdf_check_free(cloned_check);
@@ -1073,12 +1075,13 @@ _xccdf_policy_rule_evaluate(struct xccdf_policy * policy, const struct xccdf_rul
 					}
 					if ((report = _xccdf_policy_report_rule_result(policy, result, rule, cloned_check, inner_ret, NULL)) != 0)
 						break;
-					if (oscap_string_iterator_has_more(name_it))
+					if (oscap_iterator_has_more(oval_definition_iterator)) {
 						if ((report = xccdf_policy_report_cb(policy, XCCDF_POLICY_OUTCB_START, (void *) rule)) != 0)
 							break;
+					}
 				}
-				oscap_string_iterator_free(name_it);
-				oscap_stringlist_free(names);
+				oscap_iterator_free(oval_definition_iterator);
+				oscap_list_free(oval_definition_list, NULL);
 				xccdf_check_content_ref_iterator_free(content_it);
 				oscap_list_free(bindings, (oscap_destruct_func) xccdf_value_binding_free);
 				xccdf_check_free(check);
@@ -1629,6 +1632,13 @@ bool xccdf_policy_model_register_output_callback(struct xccdf_policy_model * mod
         return oscap_list_add(model->callbacks, reporter);
 }
 
+bool xccdf_policy_model_register_multicheck_callback(struct xccdf_policy_model *model, policy_reporter_multicheck func, void *usr)
+{
+	__attribute__nonnull__(model);
+	struct reporter *reporter = reporter_new(XCCDF_POLICY_OUTCB_MULTICHECK, func, usr);
+	return oscap_list_add(model->callbacks, reporter);
+}
+
 struct xccdf_result * xccdf_policy_get_result_by_id(struct xccdf_policy * policy, const char * id) {
 
     struct xccdf_result_iterator    * result_it;
diff --git a/src/XCCDF_POLICY/xccdf_policy_engine.c b/src/XCCDF_POLICY/xccdf_policy_engine.c
index 5e0a2b6b0..3529dd1b6 100644
--- a/src/XCCDF_POLICY/xccdf_policy_engine.c
+++ b/src/XCCDF_POLICY/xccdf_policy_engine.c
@@ -69,9 +69,9 @@ xccdf_test_result_type_t xccdf_policy_engine_eval(struct xccdf_policy_engine *en
 	return ret;
 }
 
-struct oscap_stringlist *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data)
+struct oscap_list *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data)
 {
 	if (engine->query_fn == NULL)
 		return NULL;
-	return (struct oscap_stringlist *) engine->query_fn(engine->usr, query_type, query_data);
+	return (struct oscap_list *) engine->query_fn(engine->usr, query_type, query_data);
 }
diff --git a/src/XCCDF_POLICY/xccdf_policy_engine_priv.h b/src/XCCDF_POLICY/xccdf_policy_engine_priv.h
index cdcb49613..bcf758b2e 100644
--- a/src/XCCDF_POLICY/xccdf_policy_engine_priv.h
+++ b/src/XCCDF_POLICY/xccdf_policy_engine_priv.h
@@ -75,7 +75,7 @@ xccdf_test_result_type_t xccdf_policy_engine_eval(struct xccdf_policy_engine *en
  * @param query_data Additional data for the checking engine query.
  * @returns list of query results
  */
-struct oscap_stringlist *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data);
+struct oscap_list *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data);
 
 OSCAP_HIDDEN_END;
 
diff --git a/src/XCCDF_POLICY/xccdf_policy_model_priv.h b/src/XCCDF_POLICY/xccdf_policy_model_priv.h
index c5223a7b8..8f503ef46 100644
--- a/src/XCCDF_POLICY/xccdf_policy_model_priv.h
+++ b/src/XCCDF_POLICY/xccdf_policy_model_priv.h
@@ -33,6 +33,7 @@ OSCAP_HIDDEN_START;
 
 #define XCCDF_POLICY_OUTCB_START "urn:xccdf:system:callback:start"
 #define XCCDF_POLICY_OUTCB_END "urn:xccdf:system:callback:output"
+#define XCCDF_POLICY_OUTCB_MULTICHECK "urn:xccdf:system:callback:multicheck"
 
 /**
  * Remove checking engines with given system from xccdf_policy_model
diff --git a/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml b/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml
index 6d4868686..6efdb2f1c 100644
--- a/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml
+++ b/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml
@@ -17,11 +17,11 @@
 	</generator>
 	<definitions>
 		<definition class="compliance" id="oval:moc.elpmaxe.www:def:1" version="1">
-			<metadata><title>PASS</title><description>Bla.</description></metadata>
+			<metadata><title>DEFINITION_1_TITLE_EXPECTED_PASS</title><description>Bla.</description></metadata>
 			<criteria><criterion test_ref="oval:moc.elpmaxe.www:tst:1" comment="Is executable"/></criteria>
 		</definition>
 		<definition class="compliance" id="oval:moc.elpmaxe.www:def:2" version="1">
-			<metadata><title>FAIL</title><description>Bla.</description></metadata>
+			<metadata><title>DEFINITION_2_TITLE_EXPECTED_FAIL</title><description>Bla.</description></metadata>
 			<criteria><criterion test_ref="oval:moc.elpmaxe.www:tst:2" comment="Is not executable"/></criteria>
 		</definition>
 	</definitions>
diff --git a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh
index d5991aa0f..2c45ad0a3 100755
--- a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh
+++ b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh
@@ -17,6 +17,12 @@ echo "Result file = $result"
 [ -f $stderr ]; [ ! -s $stderr ]; rm $stderr
 grep '^Result.*pass$' $stdout
 grep '^Result.*fail$' $stdout
+[ $(grep -c '^Rule.*xccdf_moc.elpmaxe.www_rule_1' $stdout) == 2 ]
+[ $(grep -c '^Title.*The only rule in this benchmark' $stdout) == 2 ]
+grep '^OVAL Definition ID.*oval:moc.elpmaxe.www:def:1$' $stdout
+grep '^OVAL Definition Title.*DEFINITION_1_TITLE_EXPECTED_PASS$' $stdout
+grep '^OVAL Definition ID.*oval:moc.elpmaxe.www:def:2$' $stdout
+grep '^OVAL Definition Title.*DEFINITION_2_TITLE_EXPECTED_FAIL$' $stdout
 rm $stdout
 
 $OSCAP xccdf validate-xml $result
diff --git a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml
index 44dc4de49..1c87b9d51 100644
--- a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml
+++ b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml
@@ -3,6 +3,7 @@
   <status>incomplete</status>
   <version>1.0</version>
   <Rule selected="true" id="xccdf_moc.elpmaxe.www_rule_1">
+    <title>The only rule in this benchmark</title>
     <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5" multi-check="true">
       <check-content-ref href="test_xccdf_check_content_ref_without_name_attr.oval.xml"/>
     </check>
diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c
index 19ffc0859..59cd7bcf2 100644
--- a/utils/oscap-xccdf.c
+++ b/utils/oscap-xccdf.c
@@ -392,6 +392,13 @@ static int callback_scr_result_progress(struct xccdf_rule_result *rule_result, v
 	return 0;
 }
 
+static int callback_scr_multicheck(struct oval_definition *definition, void *arg)
+{
+	printf("OVAL Definition ID\t%s\n", oval_definition_get_id(definition));
+	printf("OVAL Definition Title\t%s\n", oval_definition_get_title(definition));
+	return 0;
+}
+
 /*
  * Send XCCDF Rule Results info message to syslog
  *
@@ -434,6 +441,7 @@ static void _register_progress_callback(struct xccdf_session *session, bool prog
 		xccdf_policy_model_register_start_callback(policy_model, callback_scr_rule,
 				(void *) xccdf_session_get_xccdf_policy(session));
 		xccdf_policy_model_register_output_callback(policy_model, callback_scr_result, NULL);
+		xccdf_policy_model_register_multicheck_callback(policy_model, callback_scr_multicheck, NULL);
 	}
 	/* xccdf_policy_model_register_output_callback(policy_model, callback_syslog_result, NULL); */
 }
diff --git a/xsl/xccdf-report-impl.xsl b/xsl/xccdf-report-impl.xsl
index cc76a56cc..ba07b0a90 100644
--- a/xsl/xccdf-report-impl.xsl
+++ b/xsl/xccdf-report-impl.xsl
@@ -346,19 +346,14 @@ Authors:
     <xsl:text>}</xsl:text>
 </xsl:template>
 
-<xsl:key name="testresult_ruleresults" match="//cdf:rule-result" use="concat(ancestor::cdf:TestResult/@id, '|', @idref)"/>
-
-<xsl:template name="rule-overview-leaf">
-    <xsl:param name="testresult"/>
-    <xsl:param name="item"/>
-    <xsl:param name="profile"/>
-    <xsl:param name="indent"/>
-
-    <xsl:variable name="ruleresult" select="key('testresult_ruleresults', concat($testresult/@id, '|', $item/@id))"/>
-    <xsl:variable name="result" select="$ruleresult/cdf:result/text()"/>
-
-    <xsl:if test="$result != 'notselected'">
-    <tr data-tt-id="{$item/@id}" class="rule-overview-leaf rule-overview-leaf-{$result} rule-overview-leaf-id-{$item/@id}" id="rule-overview-leaf-{generate-id($ruleresult)}">
+<xsl:template name="rule-overview-leaf-table-row">
+    <xsl:param name="result" />
+    <xsl:param name="item" />
+    <xsl:param name="indent" />
+    <xsl:param name="testresult" />
+    <xsl:param name="profile" />
+
+    <tr data-tt-id="{$item/@id}" class="rule-overview-leaf rule-overview-leaf-{$result} rule-overview-leaf-id-{$item/@id}" id="rule-overview-leaf-{generate-id(.)}">
         <xsl:attribute name="data-tt-parent-id">
             <xsl:value-of select="$item/parent::cdf:*/@id"/>
         </xsl:attribute>
@@ -371,18 +366,26 @@ Authors:
             <xsl:attribute name="class">rule-overview-leaf rule-overview-leaf-<xsl:value-of select="$result"/> rule-overview-needs-attention</xsl:attribute>
         </xsl:if>
 
-        <td style="padding-left: {$indent * 19}px"><a href="#rule-detail-{generate-id($ruleresult)}" onclick="return openRuleDetailsDialog('{generate-id($ruleresult)}')">
-            <xsl:call-template name="item-title">
-                <xsl:with-param name="item" select="$item"/>
-                <xsl:with-param name="testresult" select="$testresult"/>
-                <xsl:with-param name="profile" select="$profile"/>
-            </xsl:call-template>
+        <td style="padding-left: {$indent * 19}px">
+            <a href="#rule-detail-{generate-id(.)}" onclick="return openRuleDetailsDialog('{generate-id(.)}')">
+                <xsl:call-template name="item-title">
+                    <xsl:with-param name="item" select="$item"/>
+                    <xsl:with-param name="testresult" select="$testresult"/>
+                    <xsl:with-param name="profile" select="$profile"/>
+                </xsl:call-template>
             </a>
-            <xsl:if test="$ruleresult/cdf:override">
+            <xsl:if test="cdf:check[@multi-check='true']">
+                (<xsl:value-of select="cdf:check/cdf:check-content-ref/@name" />)
+            </xsl:if>
+            <xsl:if test="cdf:override">
                 &#160;<span class="label label-warning">waived</span>
             </xsl:if>
         </td>
-        <td class="rule-severity" style="text-align: center"><xsl:call-template name="item-severity"><xsl:with-param name="item" select="$ruleresult" /></xsl:call-template></td>
+        <td class="rule-severity" style="text-align: center">
+            <xsl:call-template name="item-severity">
+                <xsl:with-param name="item" select="." />
+            </xsl:call-template>
+        </td>
         <td class="rule-result rule-result-{$result}">
             <xsl:variable name="result_tooltip">
                 <xsl:call-template name="rule-result-tooltip">
@@ -394,7 +397,31 @@ Authors:
             </div>
         </td>
     </tr>
-    </xsl:if>
+</xsl:template>
+
+<xsl:key name="testresult_ruleresults" match="//cdf:rule-result" use="concat(ancestor::cdf:TestResult/@id, '|', @idref)"/>
+
+<xsl:template name="rule-overview-leaf">
+    <xsl:param name="testresult"/>
+    <xsl:param name="item"/>
+    <xsl:param name="profile"/>
+    <xsl:param name="indent"/>
+
+    <xsl:variable name="ruleresult" select="key('testresult_ruleresults', concat($testresult/@id, '|', $item/@id))"/>
+
+    <!-- There can be multiple results for 1 XCCDF rule if multi-check is set -->
+    <xsl:for-each select="$ruleresult">
+        <xsl:variable name="result" select="cdf:result/text()"/>
+        <xsl:if test="$result != 'notselected'">
+            <xsl:call-template name="rule-overview-leaf-table-row">
+                <xsl:with-param name="item" select="$item"/>
+                <xsl:with-param name="result" select="$result"/>
+                <xsl:with-param name="indent" select="$indent"/>
+                <xsl:with-param name="testresult" select="$testresult"/>
+                <xsl:with-param name="profile" select="$profile"/>
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:for-each>
 </xsl:template>
 
 <xsl:template name="rule-overview-inner-node">
@@ -718,179 +745,212 @@ Authors:
 
 </xsl:template>
 
-<xsl:template name="result-details-leaf">
-    <xsl:param name="testresult"/>
+
+<xsl:template name="result-details-leaf-table">
     <xsl:param name="item"/>
+    <xsl:param name="testresult"/>
     <xsl:param name="profile"/>
+    <xsl:param name="result"/>
 
-    <xsl:variable name="ruleresult" select="key('testresult_ruleresults', concat($testresult/@id, '|', $item/@id))"/>
-    <xsl:variable name="result" select="$ruleresult/cdf:result/text()"/>
-
-    <xsl:if test="$result != 'notselected'">
-    <div class="panel panel-default rule-detail rule-detail-{$result} rule-detail-id-{$item/@id}" id="rule-detail-{generate-id($ruleresult)}">
-        <div class="keywords sr-only">
-            <xsl:comment>This allows OpenSCAP JS to search the report rules</xsl:comment>
-            <xsl:call-template name="item-title">
-                <xsl:with-param name="item" select="$item"/>
-                <xsl:with-param name="testresult" select="$testresult"/>
-                <xsl:with-param name="profile" select="$profile"/>
-            </xsl:call-template>
-            <xsl:value-of select="concat($item/@id, ' ')"/>
-            <xsl:value-of select="$ruleresult/@severity"/>
-            <xsl:for-each select="$ruleresult/cdf:ident">
-                <xsl:value-of select="concat(text(), ' ')"/>
-            </xsl:for-each>
-            <xsl:for-each select="$ruleresult/cdf:reference">
-                <xsl:value-of select="concat(text(), ' ')"/>
-            </xsl:for-each>
-        </div>
-        <div class="panel-heading">
-            <h3 class="panel-title">
-                <xsl:call-template name="item-title">
+    <table class="table table-striped table-bordered">
+        <tbody>
+            <tr><td class="col-md-3">Rule ID</td><td class="rule-id col-md-9"><xsl:value-of select="$item/@id"/></td></tr>
+            <tr><td>Result</td>
+            <td class="rule-result rule-result-{$result}">
+                <xsl:variable name="result_tooltip">
+                    <xsl:call-template name="rule-result-tooltip">
+                        <xsl:with-param name="ruleresult" select="$result"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <div>
+                    <abbr title="{$result_tooltip}"><xsl:value-of select="$result"/></abbr>
+                </div>
+            </td></tr>
+            <tr>
+                <td>Multi-check rule</td>
+                <td>
+                    <xsl:choose>
+                        <xsl:when test="cdf:check[@multi-check='true']">yes</xsl:when>
+                        <xsl:otherwise>no</xsl:otherwise>
+                    </xsl:choose>
+                </td>
+            </tr>
+            <xsl:if test="cdf:check[@system='http://oval.mitre.org/XMLSchema/oval-definitions-5']">
+                <tr>
+                    <td>OVAL Definition ID</td>
+                    <td>
+                        <xsl:value-of select="cdf:check/cdf:check-content-ref/@name" />
+                    </td>
+                </tr>
+            </xsl:if>
+            <tr><td>Time</td><td><xsl:value-of select="@time"/></td></tr>
+            <tr><td>Severity</td><td><xsl:call-template name="item-severity"><xsl:with-param name="item" select="." /></xsl:call-template></td></tr>
+            <tr><td>Identifiers and References</td><td class="identifiers">
+                <!-- XCCDF 1.2 spec says that idents in rule-result should be copied from
+                    the Rule itself. That means that we can just use the same code as guide
+                    and just use idents from Rule. -->
+                <xsl:call-template name="item-idents-refs">
                     <xsl:with-param name="item" select="$item"/>
-                    <xsl:with-param name="testresult" select="$testresult"/>
-                    <xsl:with-param name="profile" select="$profile"/>
                 </xsl:call-template>
-            </h3>
-        </div>
-        <div class="panel-body">
-            <table class="table table-striped table-bordered">
-                <tbody>
-                    <tr><td class="col-md-3">Rule ID</td><td class="rule-id col-md-9"><xsl:value-of select="$item/@id"/></td></tr>
-                    <tr><td>Result</td>
-                    <td class="rule-result rule-result-{$result}">
-                        <xsl:variable name="result_tooltip">
-                            <xsl:call-template name="rule-result-tooltip">
-                                <xsl:with-param name="ruleresult" select="$result"/>
-                            </xsl:call-template>
-                        </xsl:variable>
-                        <div>
-                            <abbr title="{$result_tooltip}"><xsl:value-of select="$result"/></abbr>
+            </td></tr>
+            <xsl:if test="cdf:override">
+                <tr><td colspan="2">
+                    <xsl:for-each select="cdf:override">
+                        <xsl:variable name="old-result" select="cdf:old-result/text()"/>
+
+                        <div class="alert alert-warning waiver">
+                            This rule has been waived by <strong><xsl:value-of select="@authority"/></strong> at <strong><xsl:value-of select="@date"/></strong>.
+                            <blockquote>
+                                <xsl:value-of select="cdf:remark/text()"/>
+                            </blockquote>
+                            <small>
+                                The previous result was <span class="rule-result rule-result-{$old-result}">&#160;<xsl:value-of select="$old-result"/>&#160;</span>.
+                            </small>
                         </div>
-                    </td></tr>
-                    <tr><td>Time</td><td><xsl:value-of select="$ruleresult/@time"/></td></tr>
-                    <tr><td>Severity</td><td><xsl:call-template name="item-severity"><xsl:with-param name="item" select="$ruleresult" /></xsl:call-template></td></tr>
-                    <tr><td>Identifiers and References</td><td class="identifiers">
-                        <!-- XCCDF 1.2 spec says that idents in rule-result should be copied from
-                             the Rule itself. That means that we can just use the same code as guide
-                             and just use idents from Rule. -->
-                        <xsl:call-template name="item-idents-refs">
-                            <xsl:with-param name="item" select="$item"/>
-                        </xsl:call-template>
-                    </td></tr>
-                    <xsl:if test="$ruleresult/cdf:override">
-                        <tr><td colspan="2">
-                            <xsl:for-each select="$ruleresult/cdf:override">
-                                <xsl:variable name="old-result" select="cdf:old-result/text()"/>
-
-                                <div class="alert alert-warning waiver">
-                                    This rule has been waived by <strong><xsl:value-of select="@authority"/></strong> at <strong><xsl:value-of select="@date"/></strong>.
-                                    <blockquote>
-                                        <xsl:value-of select="cdf:remark/text()"/>
-                                    </blockquote>
-                                    <small>
-                                        The previous result was <span class="rule-result rule-result-{$old-result}">&#160;<xsl:value-of select="$old-result"/>&#160;</span>.
-                                    </small>
-                                </div>
-                            </xsl:for-each>
-                        </td></tr>
-                    </xsl:if>
-                    <xsl:if test="$item/cdf:description">
-                        <tr><td>Description</td><td><div class="description">
-                            <p>
-                                <xsl:apply-templates mode="sub-testresult" select="$item/cdf:description">
-                                    <xsl:with-param name="testresult" select="$testresult"/>
+                    </xsl:for-each>
+                </td></tr>
+            </xsl:if>
+            <xsl:if test="$item/cdf:description">
+                <tr><td>Description</td><td><div class="description">
+                    <p>
+                        <xsl:apply-templates mode="sub-testresult" select="$item/cdf:description">
+                            <xsl:with-param name="testresult" select="$testresult"/>
+                            <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
+                            <xsl:with-param name="profile" select="$profile"/>
+                        </xsl:apply-templates>
+                    </p>
+                </div></td></tr>
+            </xsl:if>
+            <xsl:if test="$item/cdf:rationale">
+                <tr><td>Rationale</td><td><div class="rationale">
+                    <p>
+                        <xsl:apply-templates mode="sub-testresult" select="$item/cdf:rationale">
+                            <xsl:with-param name="testresult" select="$testresult"/>
+                            <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
+                            <xsl:with-param name="profile" select="$profile"/>
+                        </xsl:apply-templates>
+                    </p>
+                </div></td></tr>
+            </xsl:if>
+            <xsl:if test="$item/cdf:warning">
+                <tr><td>Warnings</td><td>
+                    <xsl:for-each select="$item/cdf:warning">
+                        <div class="panel panel-warning">
+                            <div class="panel-heading">
+                                <span class="label label-warning">warning</span>&#160;
+                                <xsl:apply-templates mode="sub-testresult" select=".">
                                     <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
                                     <xsl:with-param name="profile" select="$profile"/>
                                 </xsl:apply-templates>
-                            </p>
-                        </div></td></tr>
-                    </xsl:if>
-                    <xsl:if test="$item/cdf:rationale">
-                        <tr><td>Rationale</td><td><div class="rationale">
-                            <p>
-                                <xsl:apply-templates mode="sub-testresult" select="$item/cdf:rationale">
-                                    <xsl:with-param name="testresult" select="$testresult"/>
+                            </div>
+                        </div>
+                    </xsl:for-each>
+                </td></tr>
+            </xsl:if>
+            <xsl:variable name="check_system_details_ret">
+                <xsl:call-template name="check-system-details">
+                    <xsl:with-param name="check" select="cdf:check"/>
+                    <xsl:with-param name="oval-tmpl" select="$oval-tmpl"/>
+                    <xsl:with-param name="sce-tmpl" select="$sce-tmpl"/>
+                    <xsl:with-param name="result" select="$result"/>
+                </xsl:call-template>
+            </xsl:variable>
+
+            <xsl:if test="normalize-space($check_system_details_ret)">
+                <tr><td colspan="2"><div class="check-system-details">
+                    <xsl:copy-of select="$check_system_details_ret"/>
+                </div></td></tr>
+            </xsl:if>
+            <xsl:if test="cdf:message">
+                <tr><td colspan="2"><div class="evaluation-messages">
+                    <span class="label label-default"><abbr title="Messages taken from rule-result">Evaluation messages</abbr></span>
+                    <div class="panel panel-default">
+                        <div class="panel-body">
+                            <xsl:for-each select="cdf:message">
+                                <xsl:if test="./@severity">
+                                    <span class="label label-primary"><xsl:value-of select="./@severity"/></span>&#160;
+                                </xsl:if>
+                                <pre><xsl:apply-templates mode="sub-testresult" select=".">
                                     <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
                                     <xsl:with-param name="profile" select="$profile"/>
-                                </xsl:apply-templates>
-                            </p>
-                        </div></td></tr>
-                    </xsl:if>
-                    <xsl:if test="$item/cdf:warning">
-                        <tr><td>Warnings</td><td>
-                            <xsl:for-each select="$item/cdf:warning">
-                                <div class="panel panel-warning">
-                                    <div class="panel-heading">
-                                        <span class="label label-warning">warning</span>&#160;
-                                        <xsl:apply-templates mode="sub-testresult" select=".">
-                                            <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
-                                            <xsl:with-param name="profile" select="$profile"/>
-                                        </xsl:apply-templates>
-                                    </div>
-                                </div>
+                                </xsl:apply-templates></pre>
                             </xsl:for-each>
-                        </td></tr>
-                    </xsl:if>
-                    <xsl:variable name="check_system_details_ret">
-                        <xsl:call-template name="check-system-details">
-                            <xsl:with-param name="check" select="$ruleresult/cdf:check"/>
-                            <xsl:with-param name="oval-tmpl" select="$oval-tmpl"/>
-                            <xsl:with-param name="sce-tmpl" select="$sce-tmpl"/>
-                            <xsl:with-param name="result" select="$result"/>
+                        </div>
+                    </div>
+                </div></td></tr>
+            </xsl:if>
+            <xsl:if test="$result = 'fail' or $result = 'error' or $result = 'unknown'">
+                <xsl:for-each select="$item/cdf:fixtext">
+                    <tr><td colspan="2"><div class="remediation-description">
+                        <xsl:call-template name="show-fixtext">
+                            <xsl:with-param name="fixtext" select="."/>
+                            <xsl:with-param name="testresult" select="$testresult"/>
+                            <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
+                            <xsl:with-param name="profile" select="$profile"/>
+                        </xsl:call-template>
+                    </div></td></tr>
+                </xsl:for-each>
+                <xsl:for-each select="$item/cdf:fix">
+                    <tr><td colspan="2"><div class="remediation">
+                        <xsl:call-template name="show-fix">
+                            <xsl:with-param name="fix" select="."/>
+                            <xsl:with-param name="testresult" select="$testresult"/>
+                            <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
+                            <xsl:with-param name="profile" select="$profile"/>
                         </xsl:call-template>
-                    </xsl:variable>
+                    </div></td></tr>
+                </xsl:for-each>
+            </xsl:if>
+        </tbody>
+    </table>
+</xsl:template>
 
-                    <xsl:if test="normalize-space($check_system_details_ret)">
-                        <tr><td colspan="2"><div class="check-system-details">
-                            <xsl:copy-of select="$check_system_details_ret"/>
-                        </div></td></tr>
-                    </xsl:if>
-                    <xsl:if test="$ruleresult/cdf:message">
-                        <tr><td colspan="2"><div class="evaluation-messages">
-                            <span class="label label-default"><abbr title="Messages taken from rule-result">Evaluation messages</abbr></span>
-                            <div class="panel panel-default">
-                                <div class="panel-body">
-                                    <xsl:for-each select="$ruleresult/cdf:message">
-                                        <xsl:if test="./@severity">
-                                            <span class="label label-primary"><xsl:value-of select="./@severity"/></span>&#160;
-                                        </xsl:if>
-                                        <pre><xsl:apply-templates mode="sub-testresult" select=".">
-                                            <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
-                                            <xsl:with-param name="profile" select="$profile"/>
-                                        </xsl:apply-templates></pre>
-                                    </xsl:for-each>
-                                </div>
-                            </div>
-                        </div></td></tr>
-                    </xsl:if>
-                    <xsl:if test="$result = 'fail' or $result = 'error' or $result = 'unknown'">
-                        <xsl:for-each select="$item/cdf:fixtext">
-                            <tr><td colspan="2"><div class="remediation-description">
-                                <xsl:call-template name="show-fixtext">
-                                    <xsl:with-param name="fixtext" select="."/>
-                                    <xsl:with-param name="testresult" select="$testresult"/>
-                                    <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
-                                    <xsl:with-param name="profile" select="$profile"/>
-                                </xsl:call-template>
-                            </div></td></tr>
-                        </xsl:for-each>
-                        <xsl:for-each select="$item/cdf:fix">
-                            <tr><td colspan="2"><div class="remediation">
-                                <xsl:call-template name="show-fix">
-                                    <xsl:with-param name="fix" select="."/>
-                                    <xsl:with-param name="testresult" select="$testresult"/>
-                                    <xsl:with-param name="benchmark" select="$item/ancestor::cdf:Benchmark"/>
-                                    <xsl:with-param name="profile" select="$profile"/>
-                                </xsl:call-template>
-                            </div></td></tr>
-                        </xsl:for-each>
-                    </xsl:if>
-                </tbody>
-            </table>
-        </div>
-    </div>
-    </xsl:if>
+<xsl:template name="result-details-leaf">
+    <xsl:param name="testresult"/>
+    <xsl:param name="item"/>
+    <xsl:param name="profile"/>
+
+    <xsl:variable name="ruleresult" select="key('testresult_ruleresults', concat($testresult/@id, '|', $item/@id))"/>
+    <xsl:for-each select="$ruleresult">
+        <xsl:variable name="result" select="cdf:result/text()"/>
+        <xsl:if test="$result != 'notselected'">
+            <div class="panel panel-default rule-detail rule-detail-{$result} rule-detail-id-{$item/@id}" id="rule-detail-{generate-id(.)}">
+                <div class="keywords sr-only">
+                    <xsl:comment>This allows OpenSCAP JS to search the report rules</xsl:comment>
+                    <xsl:call-template name="item-title">
+                        <xsl:with-param name="item" select="$item"/>
+                        <xsl:with-param name="testresult" select="$testresult"/>
+                        <xsl:with-param name="profile" select="$profile"/>
+                    </xsl:call-template>
+                    <xsl:value-of select="concat($item/@id, ' ')"/>
+                    <xsl:value-of select="@severity"/>
+                    <xsl:for-each select="cdf:ident">
+                        <xsl:value-of select="concat(text(), ' ')"/>
+                    </xsl:for-each>
+                    <xsl:for-each select="cdf:reference">
+                        <xsl:value-of select="concat(text(), ' ')"/>
+                    </xsl:for-each>
+                </div>
+                <div class="panel-heading">
+                    <h3 class="panel-title">
+                        <xsl:call-template name="item-title">
+                            <xsl:with-param name="item" select="$item"/>
+                            <xsl:with-param name="testresult" select="$testresult"/>
+                            <xsl:with-param name="profile" select="$profile"/>
+                        </xsl:call-template>
+                    </h3>
+                </div>
+                <div class="panel-body">
+                    <xsl:call-template name="result-details-leaf-table">
+                            <xsl:with-param name="item" select="$item"/>
+                            <xsl:with-param name="testresult" select="$testresult"/>
+                            <xsl:with-param name="profile" select="$profile"/>
+                            <xsl:with-param name="result" select="$result"/>
+                    </xsl:call-template>
+                </div>
+            </div>
+        </xsl:if>
+    </xsl:for-each>
 </xsl:template>
 
 <xsl:template name="result-details-inner-node">