From 97ea3d5dd72e11996fac63bfdcf9691e1515f09a Mon Sep 17 00:00:00 2001
From: Malte Kraus <malte.kraus@suse.com>
Date: Fri, 29 Mar 2019 16:12:22 +0100
Subject: [PATCH] improve --stig-viewer output when there is no 1:1 connection
between rules
There can both be rules covering multiple STIG IDs (easy to handle: just
output the same result multiple times), and STIG IDs covered by more
than one rule. The DISA Stig Viewer seems to only consider the last
result for a given rule when it imports results, so when that last rule
happens to pass, that means the whole STIG will be marked as 'not a
finding'. To counteract that, this commit attempts to merge the results
in a sensible way - only if all tests passed, the STIG should be
marked as 'not a finding' in the way.
---
src/XCCDF/result.c | 92 +++++++++++++++++++++++++++++++++---------
src/XCCDF/xccdf_impl.h | 3 +-
2 files changed, 76 insertions(+), 19 deletions(-)
diff --git a/src/XCCDF/result.c b/src/XCCDF/result.c
index f389813d5..f0fa647b2 100644
--- a/src/XCCDF/result.c
+++ b/src/XCCDF/result.c
@@ -924,13 +924,68 @@ void xccdf_result_to_dom(struct xccdf_result *result, xmlNode *result_node, xmlD
}
xccdf_setvalue_iterator_free(setvalues);
+ struct oscap_htable *nodes_by_rule_id = oscap_htable_new();
+
struct xccdf_rule_result_iterator *rule_results = xccdf_result_get_rule_results(result);
+ if (use_stig_rule_id) {
+ while (xccdf_rule_result_iterator_has_more(rule_results)) {
+ struct xccdf_rule_result *rule_result = xccdf_rule_result_iterator_next(rule_results);
+
+ const char *idref = xccdf_rule_result_get_idref(rule_result);
+ if (!idref)
+ continue;
+
+ xccdf_test_result_type_t test_res = xccdf_rule_result_get_result(rule_result);
+
+ const struct xccdf_item *item = xccdf_benchmark_get_member(associated_benchmark, XCCDF_RULE, idref);
+ if (!item)
+ continue;
+
+ struct oscap_reference_iterator *references = xccdf_item_get_references(item);
+ while (oscap_reference_iterator_has_more(references)) {
+ struct oscap_reference *ref = oscap_reference_iterator_next(references);
+ if (strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF) == 0) {
+ const char *stig_rule_id = oscap_reference_get_title(ref);
+
+ xccdf_test_result_type_t other_res = (xccdf_test_result_type_t)oscap_htable_detach(nodes_by_rule_id, stig_rule_id);
+ xccdf_test_result_type_t wanted_res;
+ if (other_res == 0) {
+ wanted_res = test_res;
+ } else {
+ // if one test passed, and the other didn't, the other one should win
+ if (test_res == XCCDF_RESULT_PASS) {
+ wanted_res = other_res;
+ } else if (other_res == XCCDF_RESULT_PASS) {
+ wanted_res = test_res;
+ // if one had an error, that should win
+ } else if (test_res == XCCDF_RESULT_ERROR || other_res == XCCDF_RESULT_ERROR) {
+ wanted_res = XCCDF_RESULT_ERROR;
+ // next prio: failures
+ } else if (test_res == XCCDF_RESULT_FAIL || other_res == XCCDF_RESULT_FAIL) {
+ wanted_res = XCCDF_RESULT_FAIL;
+ // next prio: unknown
+ } else if (test_res == XCCDF_RESULT_UNKNOWN || other_res == XCCDF_RESULT_UNKNOWN) {
+ wanted_res = XCCDF_RESULT_UNKNOWN;
+ // otherwise, just pick the lower one (more or less arbitrarily)
+ } else {
+ wanted_res = (test_res < other_res) ? test_res : other_res;
+ }
+ }
+ oscap_htable_add(nodes_by_rule_id, stig_rule_id, (void*)wanted_res);
+ }
+ }
+ oscap_reference_iterator_free(references);
+ }
+ xccdf_rule_result_iterator_reset(rule_results);
+ }
while (xccdf_rule_result_iterator_has_more(rule_results)) {
struct xccdf_rule_result *rule_result = xccdf_rule_result_iterator_next(rule_results);
- xccdf_rule_result_to_dom(rule_result, doc, result_node, version_info, associated_benchmark, use_stig_rule_id);
+ xccdf_rule_result_to_dom(rule_result, doc, result_node, version_info, associated_benchmark, use_stig_rule_id, nodes_by_rule_id);
}
xccdf_rule_result_iterator_free(rule_results);
+ oscap_htable_free0(nodes_by_rule_id);
+
struct xccdf_score_iterator *scores = xccdf_result_get_scores(result);
while (xccdf_score_iterator_has_more(scores)) {
struct xccdf_score *score = xccdf_score_iterator_next(scores);
@@ -1081,36 +1136,40 @@ xmlNode *xccdf_target_identifier_to_dom(const struct xccdf_target_identifier *ti
}
}
-xmlNode *xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id)
+static void _xccdf_rule_result_to_dom_idref(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, const char *idref);
+void xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id, struct oscap_htable *nodes_by_rule_id)
{
const char *idref = xccdf_rule_result_get_idref(result);
if (use_stig_rule_id) {
// Don't output rules with no stig ids
if (!idref || !benchmark)
- return NULL;
+ return;
- struct xccdf_item *item = xccdf_benchmark_get_member(benchmark, XCCDF_RULE, idref);
+ const struct xccdf_item *item = xccdf_benchmark_get_member(benchmark, XCCDF_RULE, idref);
if (!item)
- return NULL;
+ return;
- const char *stig_rule_id = NULL;
- struct oscap_reference_iterator *references = xccdf_item_get_references(XRULE(item));
+ struct oscap_reference_iterator *references = xccdf_item_get_references(item);
while (oscap_reference_iterator_has_more(references)) {
struct oscap_reference *ref = oscap_reference_iterator_next(references);
if (strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF[0]) == 0 ||
strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF[1]) == 0) {
- stig_rule_id = oscap_reference_get_title(ref);
- break;
+ const char *stig_rule_id = oscap_reference_get_title(ref);
+
+ xccdf_test_result_type_t expected_res = (xccdf_test_result_type_t)oscap_htable_get(nodes_by_rule_id, stig_rule_id);
+ xccdf_test_result_type_t test_res = xccdf_rule_result_get_result(result);
+ if (expected_res == test_res) {
+ oscap_htable_detach(nodes_by_rule_id, stig_rule_id);
+ _xccdf_rule_result_to_dom_idref(result, doc, parent, version_info, benchmark, stig_rule_id);
+ }
}
}
oscap_reference_iterator_free(references);
-
- if (!stig_rule_id)
- return NULL;
-
- idref = stig_rule_id;
+ } else {
+ _xccdf_rule_result_to_dom_idref(result, doc, parent, version_info, benchmark, idref);
}
-
+}
+static void _xccdf_rule_result_to_dom_idref(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, const char *idref) {
xmlNs *ns_xccdf = lookup_xccdf_ns(doc, parent, version_info);
xmlNode *result_node = xmlNewTextChild(parent, ns_xccdf, BAD_CAST "rule-result", NULL);
@@ -1203,8 +1262,6 @@ xmlNode *xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc,
xccdf_check_to_dom(check, doc, result_node, version_info);
}
xccdf_check_iterator_free(checks);
-
- return result_node;
}
bool xccdf_rule_result_override(struct xccdf_rule_result *rule_result, xccdf_test_result_type_t new_result, const char *waiver_time, const char *authority, struct oscap_text *remark)
diff --git a/src/XCCDF/xccdf_impl.h b/src/XCCDF/xccdf_impl.h
index b87e7f170..05e0edd85 100644
--- a/src/XCCDF/xccdf_impl.h
+++ b/src/XCCDF/xccdf_impl.h
@@ -27,6 +27,7 @@
#include <public/xccdf_benchmark.h>
#include <common/util.h>
#include <libxml/xmlreader.h>
+#include <common/list.h>
OSCAP_HIDDEN_START;
@@ -49,7 +50,7 @@ void xccdf_group_to_dom(struct xccdf_group *group, xmlNode *group_node, xmlDoc *
void xccdf_profile_to_dom(struct xccdf_profile *profile, xmlNode *profile_node, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info *version_info);
void xccdf_result_to_dom(struct xccdf_result *result, xmlNode *result_node, xmlDoc *doc, xmlNode *parent, bool use_stig_rule_id);
xmlNode *xccdf_target_identifier_to_dom(const struct xccdf_target_identifier *ti, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info);
-xmlNode *xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id);
+void xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id, struct oscap_htable *nodes_by_rule_id);
xmlNode *xccdf_ident_to_dom(struct xccdf_ident *ident, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info);
xmlNode *xccdf_setvalue_to_dom(struct xccdf_setvalue *setvalue, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info);
xmlNode *xccdf_override_to_dom(struct xccdf_override *override, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info);