Blob Blame History Raw
From 9b40767967e533bdb340ca4c91f2fd1192694820 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Wed, 2 Dec 2020 16:45:42 +0100
Subject: [PATCH] Fix TestResult/benchmark/@href attribute

Make the href attribute an URI according to SCAP specification Section
4.5. This should fix SCAPVAL error RES-253-2 which occured when
validating ARFs by SCAPVAL. See #1629 for the problem description.

Also, it fixes a problem that xccdf_benchmark_item_clone didn't create
the clusters_dict hash table.

The existing tests are extended to test this property, the
function test_sds_external_xccdf has been splitted into 2 functions.

Fixes: #1629
---
 src/DS/ds_sds_session.c        | 18 ++++++++++++++++++
 src/DS/ds_sds_session_priv.h   |  1 +
 src/DS/public/ds_sds_session.h |  8 ++++++++
 src/DS/sds.c                   |  3 +++
 src/XCCDF/item.c               |  1 +
 src/XCCDF/xccdf_session.c      | 25 ++++++++++++++++++++-----
 tests/DS/test_ds_misc.sh       | 30 +++++++++++++++++++++++-------
 7 files changed, 74 insertions(+), 12 deletions(-)

diff --git a/src/DS/ds_sds_session.c b/src/DS/ds_sds_session.c
index c03d3d593e..9120ee3243 100644
--- a/src/DS/ds_sds_session.c
+++ b/src/DS/ds_sds_session.c
@@ -50,7 +50,9 @@ struct ds_sds_session {
 	const char *target_dir;                 ///< Target directory for current split
 	const char *datastream_id;              ///< ID of selected datastream
 	const char *checklist_id;               ///< ID of selected checklist
+	const char *checklist_uri;              ///< URI of selected checklist
 	struct oscap_htable *component_sources;	///< oscap_source for parsed components
+	struct oscap_htable *component_uris;    ///< maps component refs to component URIs
 	bool fetch_remote_resources;            ///< Allows loading of external components;
 	download_progress_calllback_t progress;	///< Callback to report progress of download.
 };
@@ -72,6 +74,7 @@ struct ds_sds_session *ds_sds_session_new_from_source(struct oscap_source *sourc
 	struct ds_sds_session *sds_session = (struct ds_sds_session *) calloc(1, sizeof(struct ds_sds_session));
 	sds_session->source = source;
 	sds_session->component_sources = oscap_htable_new();
+	sds_session->component_uris = oscap_htable_new();
 	sds_session->progress = download_progress_empty_calllback;
 	return sds_session;
 }
@@ -84,6 +87,7 @@ void ds_sds_session_free(struct ds_sds_session *sds_session)
 			oscap_acquire_cleanup_dir(&(sds_session->temp_dir));
 		}
 		oscap_htable_free(sds_session->component_sources, (oscap_destruct_func) oscap_source_free);
+		oscap_htable_free(sds_session->component_uris, (oscap_destruct_func) free);
 		free(sds_session);
 	}
 }
@@ -91,10 +95,13 @@ void ds_sds_session_free(struct ds_sds_session *sds_session)
 void ds_sds_session_reset(struct ds_sds_session *session)
 {
 	session->checklist_id = NULL;
+	session->checklist_uri = NULL;
 	session->datastream_id = NULL;
 	session->target_dir = NULL;
 	oscap_htable_free(session->component_sources, (oscap_destruct_func) oscap_source_free);
 	session->component_sources = oscap_htable_new();
+	oscap_htable_free(session->component_uris, (oscap_destruct_func) free);
+	session->component_uris = oscap_htable_new();
 }
 
 struct ds_sds_index *ds_sds_session_get_sds_idx(struct ds_sds_session *session)
@@ -163,11 +170,21 @@ const char *ds_sds_session_get_checklist_id(const struct ds_sds_session *session
 	return session->checklist_id;
 }
 
+const char *ds_sds_session_get_checklist_uri(const struct ds_sds_session *session)
+{
+	return session->checklist_uri;
+}
+
 struct oscap_htable *ds_sds_session_get_component_sources(struct ds_sds_session *session)
 {
 	return session->component_sources;
 }
 
+struct oscap_htable *ds_sds_session_get_component_uris(struct ds_sds_session *session)
+{
+	return session->component_uris;
+}
+
 const char *ds_sds_session_get_readable_origin(const struct ds_sds_session *session)
 {
 	if (session->source == NULL)
@@ -210,6 +227,7 @@ struct oscap_source *ds_sds_session_select_checklist(struct ds_sds_session *sess
 		oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not extract %s with all dependencies from datastream.", session->checklist_id);
 		return NULL;
 	}
+	session->checklist_uri = oscap_htable_get(session->component_uris, session->checklist_id);
 	struct oscap_source *xccdf = oscap_htable_get(session->component_sources, session->checklist_id);
 	if (xccdf == NULL) {
 		oscap_seterr(OSCAP_EFAMILY_OSCAP, "Internal error: Could not acquire handle to '%s' source.", session->checklist_id);
diff --git a/src/DS/ds_sds_session_priv.h b/src/DS/ds_sds_session_priv.h
index 4aa559ebb4..f58231dc48 100644
--- a/src/DS/ds_sds_session_priv.h
+++ b/src/DS/ds_sds_session_priv.h
@@ -37,6 +37,7 @@ xmlDoc *ds_sds_session_get_xmlDoc(struct ds_sds_session *session);
 int ds_sds_session_register_component_source(struct ds_sds_session *session, const char *relative_filepath, struct oscap_source *component);
 const char *ds_sds_session_get_target_dir(struct ds_sds_session *session);
 struct oscap_htable *ds_sds_session_get_component_sources(struct ds_sds_session *session);
+struct oscap_htable *ds_sds_session_get_component_uris(struct ds_sds_session *session);
 const char *ds_sds_session_get_readable_origin(const struct ds_sds_session *session);
 bool ds_sds_session_fetch_remote_resources(struct ds_sds_session *session);
 download_progress_calllback_t ds_sds_session_remote_resources_progress(struct ds_sds_session *session);
diff --git a/src/DS/public/ds_sds_session.h b/src/DS/public/ds_sds_session.h
index 1ce692ce21..20a85146cc 100644
--- a/src/DS/public/ds_sds_session.h
+++ b/src/DS/public/ds_sds_session.h
@@ -119,6 +119,14 @@ const char *ds_sds_session_get_datastream_id(const struct ds_sds_sessi
  */
 const char *ds_sds_session_get_checklist_id(const struct ds_sds_session *session);
 
+/**
+ * Return URI of currently selected component representing XCCDF within the DataStream
+ * @memberof ds_sds_session
+ * @param session The Source DataStream session
+ * @returns URI of selected component or NULL
+ */
+const char *ds_sds_session_get_checklist_uri(const struct ds_sds_session *session);
+
 /**
  * Get component from Source DataStream by its href. This assumes that the component
  * has been already cached by the session. You can cache component or its dependencies
diff --git a/src/DS/sds.c b/src/DS/sds.c
index b546ed3e65..20c683a7aa 100644
--- a/src/DS/sds.c
+++ b/src/DS/sds.c
@@ -450,7 +450,10 @@ int ds_sds_dump_component_ref_as(const xmlNodePtr component_ref, struct ds_sds_s
 
 	char* component_id = NULL;
 
+	// make a copy of xlink_href because ds_sds_dump_component_by_href modifies its second argument
+	char *xlink_href_copy = oscap_strdup(xlink_href);
 	int ret = ds_sds_dump_component_by_href(session, xlink_href, target_filename_dirname, relative_filepath, cref_id, &component_id);
+	oscap_htable_add(ds_sds_session_get_component_uris(session), cref_id, xlink_href_copy);
 
 	xmlFree(xlink_href);
 	xmlFree(cref_id);
diff --git a/src/XCCDF/item.c b/src/XCCDF/item.c
index 3657fb461a..295e4a7f00 100644
--- a/src/XCCDF/item.c
+++ b/src/XCCDF/item.c
@@ -1155,6 +1155,7 @@ struct xccdf_benchmark_item * xccdf_benchmark_item_clone(struct xccdf_item *pare
 	clone->items_dict = oscap_htable_new();
 	clone->profiles_dict = oscap_htable_new();
 	clone->results_dict = oscap_htable_new();
+	clone->clusters_dict = oscap_htable_new();
 	clone->notices = oscap_list_clone(item->notices, (oscap_clone_func) xccdf_notice_clone);
 	clone->plain_texts = oscap_list_clone(item->plain_texts, (oscap_clone_func) xccdf_plain_text_clone);
 	
diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c
index f1b8379591..c88d90be05 100644
--- a/src/XCCDF/xccdf_session.c
+++ b/src/XCCDF/xccdf_session.c
@@ -1374,21 +1374,28 @@ static int _build_xccdf_result_source(struct xccdf_session *session)
 			oscap_seterr(OSCAP_EFAMILY_OSCAP, "No XCCDF results to export.");
 			return 1;
 		}
-		struct xccdf_result* cloned_result = xccdf_result_clone(session->xccdf.result);
-		xccdf_benchmark_add_result(benchmark, cloned_result);
-		session->xccdf.result_source = xccdf_benchmark_export_source(benchmark, session->export.xccdf_file);
 
 		if (session->export.xccdf_file != NULL) {
+			struct xccdf_benchmark *cloned_benchmark = xccdf_benchmark_clone(benchmark);
+			struct xccdf_result *cloned_result = xccdf_result_clone(session->xccdf.result);
+			xccdf_benchmark_add_result(cloned_benchmark, cloned_result);
+			struct oscap_source *xccdf_result_source = xccdf_benchmark_export_source(cloned_benchmark, session->export.xccdf_file);
+			// cloned_result is freed during xccdf_benchmark_free
+			xccdf_benchmark_free(cloned_benchmark);
 			// Export XCCDF result file only when explicitly requested
-			if (oscap_source_save_as(session->xccdf.result_source, NULL) != 0) {
+			if (oscap_source_save_as(xccdf_result_source, NULL) != 0) {
 				oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not save file: %s",
-						oscap_source_readable_origin(session->xccdf.result_source));
+						oscap_source_readable_origin(xccdf_result_source));
+				oscap_source_free(xccdf_result_source);
 				return -1;
 			}
+			oscap_source_free(xccdf_result_source);
 		}
 
 		if (session->export.xccdf_stig_viewer_file != NULL) {
+			struct xccdf_result *cloned_result = xccdf_result_clone(session->xccdf.result);
 			struct oscap_source * stig_result = xccdf_result_stig_viewer_export_source(cloned_result, session->export.xccdf_stig_viewer_file);
+			xccdf_result_free(cloned_result);
 			if (oscap_source_save_as(stig_result, NULL) != 0) {
 				oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not save file: %s",
 						oscap_source_readable_origin(stig_result));
@@ -1398,6 +1405,14 @@ static int _build_xccdf_result_source(struct xccdf_session *session)
 			oscap_source_free(stig_result);
 		}
 
+		struct xccdf_result *cloned_result = xccdf_result_clone(session->xccdf.result);
+		if (xccdf_session_is_sds(session)) {
+			struct ds_sds_session *sds_session = xccdf_session_get_ds_sds_session(session);
+			const char *benchmark_uri = ds_sds_session_get_checklist_uri(sds_session);
+			xccdf_result_set_benchmark_uri(cloned_result, benchmark_uri);
+		}
+		xccdf_benchmark_add_result(benchmark, cloned_result);
+		session->xccdf.result_source = xccdf_benchmark_export_source(benchmark, session->export.xccdf_file);
 		/* validate XCCDF Results */
 		if (session->validate && session->full_validation) {
 			if (oscap_source_validate(session->xccdf.result_source, _reporter, NULL)) {