Blob Blame History Raw
From 35ae94cd84b7b99845f0d2306924946e66da6d50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Tue, 15 Jun 2021 10:09:44 +0200
Subject: [PATCH 01/10] Allow providing pre-downloaded components

OpenSCAP can download remote SCAP source data stream components from the
internet if the --fetch-remote-resources option is set. When this
command line option isn't set, it is possible to download the remote
component separately, save it as file to the filesystem which name is
equal to the name of the remote component and OpenSCAP would use this
local file instead of just skipping the checks. It currently works by
accident, it wasn't intended to work this way or at least I haven't
heard it before, but somobody found out that it works and documented it
as a KSC: https://access.redhat.com/solutions/5185891. However, it
produces warning messages if the approach used in the KSC is used.

This patch promotes the workaround to a feature. And it changes the
logic so that it won't print the warnings.

The main difference is that it won't work automatically, but only
on user's demand by providing --use-local-file which is the new
command line option introduced by this commit.

Fixes: rhbz#1970527
---
 src/DS/ds_sds_session.c                       | 14 ++-
 src/DS/ds_sds_session_priv.h                  |  1 +
 src/DS/public/ds_sds_session.h                | 15 ++-
 src/DS/sds.c                                  | 22 +++++
 src/OVAL/oval_session.c                       | 11 ++-
 src/OVAL/public/oval_session.h                | 15 ++-
 src/XCCDF/public/xccdf_session.h              | 15 ++-
 src/XCCDF/xccdf_session.c                     | 36 ++++---
 tests/DS/CMakeLists.txt                       |  1 +
 .../remote.oval.xml                           | 58 +++++++++++
 .../remote_content_1.3.ds.xml                 | 96 +++++++++++++++++++
 .../DS/test_ds_use_local_remote_resources.sh  | 57 +++++++++++
 utils/oscap-ds.c                              |  6 +-
 utils/oscap-info.c                            |  4 +-
 utils/oscap-oval.c                            |  6 +-
 utils/oscap-tool.h                            |  1 +
 utils/oscap-xccdf.c                           | 14 ++-
 utils/oscap.8                                 | 26 +++++
 18 files changed, 369 insertions(+), 29 deletions(-)
 create mode 100644 tests/DS/ds_use_local_remote_resources/remote.oval.xml
 create mode 100644 tests/DS/ds_use_local_remote_resources/remote_content_1.3.ds.xml
 create mode 100755 tests/DS/test_ds_use_local_remote_resources.sh

diff --git a/src/DS/ds_sds_session.c b/src/DS/ds_sds_session.c
index 8c0072bae0..9d9a9c8b52 100644
--- a/src/DS/ds_sds_session.c
+++ b/src/DS/ds_sds_session.c
@@ -55,6 +55,7 @@ struct ds_sds_session {
 	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.
+	bool use_local_file;                    ///< Use a locally downloaded copy of a remote resource if it exists
 };
 
 /**
@@ -337,12 +338,23 @@ int ds_sds_session_register_component_with_dependencies(struct ds_sds_session *s
 	return res;
 }
 
-void ds_sds_session_set_remote_resources(struct ds_sds_session *session, bool allowed, download_progress_calllback_t callback)
+void ds_sds_session_configure_remote_resources(struct ds_sds_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback)
 {
 	session->fetch_remote_resources = allowed;
+	session->use_local_file = use_local_file;
 	session->progress = (callback != NULL) ? callback : download_progress_empty_calllback;
 }
 
+void ds_sds_session_set_remote_resources(struct ds_sds_session *session, bool allowed, download_progress_calllback_t callback)
+{
+	ds_sds_session_configure_remote_resources(session, allowed, false, callback);
+}
+
+bool ds_sds_session_can_use_local_file(struct ds_sds_session *session)
+{
+	return session->use_local_file;
+}
+
 int ds_sds_session_dump_component_files(struct ds_sds_session *session)
 {
 	return ds_dump_component_sources(session->component_sources, ds_sds_session_get_target_dir(session));
diff --git a/src/DS/ds_sds_session_priv.h b/src/DS/ds_sds_session_priv.h
index f58231dc48..018cd053ec 100644
--- a/src/DS/ds_sds_session_priv.h
+++ b/src/DS/ds_sds_session_priv.h
@@ -40,6 +40,7 @@ struct oscap_htable *ds_sds_session_get_component_sources(struct ds_sds_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);
+bool ds_sds_session_can_use_local_file(struct ds_sds_session *session);
 download_progress_calllback_t ds_sds_session_remote_resources_progress(struct ds_sds_session *session);
 
 void download_progress_empty_calllback(bool warning, const char * format, ...);
diff --git a/src/DS/public/ds_sds_session.h b/src/DS/public/ds_sds_session.h
index 20a85146cc..695a0df215 100644
--- a/src/DS/public/ds_sds_session.h
+++ b/src/DS/public/ds_sds_session.h
@@ -200,7 +200,20 @@ OSCAP_API void ds_sds_session_reset(struct ds_sds_session *session);
  * @param callback used to notify user about download proceeds. This might be safely set
  * to NULL -- ignoring user notification.
  */
-OSCAP_API void ds_sds_session_set_remote_resources(struct ds_sds_session *session, bool allowed, download_progress_calllback_t callback);
+OSCAP_API OSCAP_DEPRECATED(void ds_sds_session_set_remote_resources(struct ds_sds_session *session, bool allowed, download_progress_calllback_t callback));
+
+/**
+ * Set property of remote content.
+ * @memberof ds_sds_session
+ * @param session The source data stream session
+ * @param allowed Whether is download of remote resources allowed in this
+ * session (defaults to false)
+ * @param use_local_file Allows to use a locally downloaded copy of the remote
+ * resource if it exists (defaults to false)
+ * @param callback used to notify user about download proceeds. This might be
+ * safely set to NULL -- ignoring user notification.
+ */
+OSCAP_API void ds_sds_session_configure_remote_resources(struct ds_sds_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback);
 
 /**
  * Returns HTML representation of selected checklist in form of OpenSCAP guide.
diff --git a/src/DS/sds.c b/src/DS/sds.c
index 0f19a81982..b1737aa57f 100644
--- a/src/DS/sds.c
+++ b/src/DS/sds.c
@@ -413,6 +413,28 @@ static int ds_sds_dump_component_by_href(struct ds_sds_session *session, char* x
 		}
 
 		if (!ds_sds_session_fetch_remote_resources(session)) {
+			/*
+			 * If fetching remote resources isn't allowed by the user let's take
+			 * a look whether there exists a file whose file name is equal to
+			 * @name attribute of the uri element within the catalog of the
+			 * previously processed component-ref which pointed us to the
+			 * currently processed component-ref. Note that the @name attribute
+			 * value has been passed as relative_filepath in the recursive call
+			 * of ds_sds_dump_component_ref_as. If such file exists, we will
+			 * assume that it's a local copy of the remote component located at
+			 * the URL defined in @xlink:href. This way people can provide the
+			 * previously downloaded component which might be useful on systems
+			 * with limited internet access. This behavior is allowed only when
+			 * --use-local-file is used on the command line.
+			 * See: https://bugzilla.redhat.com/show_bug.cgi?id=1970527
+			 * See: https://access.redhat.com/solutions/5185891
+			 */
+			struct stat sb;
+			if (ds_sds_session_can_use_local_file(session) && stat(relative_filepath, &sb) == 0) {
+				dI("Using local file '%s' instead of '%s'", relative_filepath, xlink_href);
+				return ds_sds_dump_file_component(relative_filepath, *component_id, session, target_filename_dirname, relative_filepath);
+			}
+
 			static bool fetch_remote_resources_suggested = false;
 
 			if (!fetch_remote_resources_suggested) {
diff --git a/src/OVAL/oval_session.c b/src/OVAL/oval_session.c
index ebc7e55f71..5adda43f66 100644
--- a/src/OVAL/oval_session.c
+++ b/src/OVAL/oval_session.c
@@ -84,6 +84,7 @@ struct oval_session {
 	bool full_validation;
 	bool fetch_remote_resources;
 	download_progress_calllback_t progress;
+	bool use_local_file;
 };
 
 struct oval_session *oval_session_new(const char *filename)
@@ -223,7 +224,7 @@ static int oval_session_load_definitions(struct oval_session *session)
 		if ((session->sds_session = ds_sds_session_new_from_source(session->source)) == NULL) {
 			return 1;
 		}
-		ds_sds_session_set_remote_resources(session->sds_session,session->fetch_remote_resources ,session->progress);
+		ds_sds_session_configure_remote_resources(session->sds_session, session->fetch_remote_resources, session->use_local_file, session->progress);
 		ds_sds_session_set_datastream_id(session->sds_session, session->datastream_id);
 		if (ds_sds_session_register_component_with_dependencies(session->sds_session,
 					"checks", session->component_id, "oval.xml") != 0) {
@@ -451,12 +452,18 @@ void oval_session_set_export_system_characteristics(struct oval_session *session
 	session->export_sys_chars = export;
 }
 
-void oval_session_set_remote_resources(struct oval_session *session, bool allowed, download_progress_calllback_t callback)
+void oval_session_configure_remote_resources(struct oval_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback)
 {
 	session->fetch_remote_resources = allowed;
+	session->use_local_file = use_local_file;
 	session->progress = callback;
 }
 
+void oval_session_set_remote_resources(struct oval_session *session, bool allowed, download_progress_calllback_t callback)
+{
+	oval_session_configure_remote_resources(session, allowed, false, callback);
+}
+
 void oval_session_free(struct oval_session *session)
 {
 	if (session == NULL)
diff --git a/src/OVAL/public/oval_session.h b/src/OVAL/public/oval_session.h
index ed97cb7768..d485676747 100644
--- a/src/OVAL/public/oval_session.h
+++ b/src/OVAL/public/oval_session.h
@@ -239,7 +239,20 @@ OSCAP_API void oval_session_set_export_system_characteristics(struct oval_sessio
  * @param callback used to notify user about download proceeds. This might be safely set
  * to NULL -- ignoring user notification.
  */
-OSCAP_API void oval_session_set_remote_resources(struct oval_session *session, bool allowed, download_progress_calllback_t callback);
+OSCAP_API OSCAP_DEPRECATED(void oval_session_set_remote_resources(struct oval_session *session, bool allowed, download_progress_calllback_t callback));
+
+/**
+ * Set property of remote content.
+ * @memberof oval_session
+ * @param session an \ref oval_session
+ * @param allowed Whether is download of remote resources allowed in this
+ * session (defaults to false)
+ * @param use_local_file Allows to use a locally downloaded copy of the remote
+ * resource if it exists (defaults to false)
+ * @param callback used to notify user about download proceeds. This might be
+ * safely set to NULL -- ignoring user notification.
+ */
+OSCAP_API void oval_session_configure_remote_resources(struct oval_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback);
 
 /**
  * Destructor of an \ref oval_session.
diff --git a/src/XCCDF/public/xccdf_session.h b/src/XCCDF/public/xccdf_session.h
index 70cfc677f3..2eb7128019 100644
--- a/src/XCCDF/public/xccdf_session.h
+++ b/src/XCCDF/public/xccdf_session.h
@@ -232,7 +232,20 @@ OSCAP_API void xccdf_session_set_user_tailoring_cid(struct xccdf_session *sessio
  * @param callback used to notify user about download proceeds. This might be safely set
  * to NULL -- ignoring user notification.
  */
-OSCAP_API void xccdf_session_set_remote_resources(struct xccdf_session *session, bool allowed, download_progress_calllback_t callback);
+OSCAP_API OSCAP_DEPRECATED(void xccdf_session_set_remote_resources(struct xccdf_session *session, bool allowed, download_progress_calllback_t callback));
+
+/**
+ * Set properties of remote content.
+ * @memberof xccdf_session
+ * @param session XCCDF Session
+ * @param allowed Whether is download od remote resources allowed in this
+ * session (defaults to false)
+ * @param use_local_file Allows to use a locally downloaded copy of the remote
+ * resource if it exists (defaults to false)
+ * @param callback used to notify user about download proceeds. This might be
+ * safely set to NULL -- ignoring user notification.
+ */
+OSCAP_API void xccdf_session_configure_remote_resources(struct xccdf_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback);
 
 /**
  * Disable or allow loading of depending content (OVAL, SCE, CPE)
diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c
index 9d8f42c445..85fcc90d23 100644
--- a/src/XCCDF/xccdf_session.c
+++ b/src/XCCDF/xccdf_session.c
@@ -87,6 +87,7 @@ struct xccdf_session {
 	} ds;
 	struct {
 		bool fetch_remote_resources;		///< Allows download of remote resources (not applicable when user sets custom oval files)
+		bool use_local_file; ///< Use a locally downloaded copy of a remote resource if it exists
 		download_progress_calllback_t progress;	///< Callback to report progress of download.
 		struct oval_content_resource **custom_resources;///< OVAL files required by user
 		struct oval_content_resource **resources;///< OVAL files referenced from XCCDF
@@ -626,7 +627,8 @@ static struct ds_sds_session *xccdf_session_get_ds_sds_session(struct xccdf_sess
 	return session->ds.session;
 }
 
-void xccdf_session_set_remote_resources(struct xccdf_session *session, bool allowed, download_progress_calllback_t callback)
+
+void xccdf_session_configure_remote_resources(struct xccdf_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback)
 {
 	if (callback == NULL) {
 		// With empty cb we don't have to check for NULL
@@ -635,15 +637,21 @@ void xccdf_session_set_remote_resources(struct xccdf_session *session, bool allo
 	}
 
 	session->oval.fetch_remote_resources = allowed;
+	session->oval.use_local_file = use_local_file;
 	session->oval.progress = callback;
 
 	if (xccdf_session_is_sds(session)) {
 		// We have to propagate this option to allow loading
 		// of external datastream components
-		ds_sds_session_set_remote_resources(xccdf_session_get_ds_sds_session(session), allowed, callback);
+		ds_sds_session_configure_remote_resources(xccdf_session_get_ds_sds_session(session), allowed, use_local_file, callback);
 	}
 }
 
+void xccdf_session_set_remote_resources(struct xccdf_session *session, bool allowed, download_progress_calllback_t callback)
+{
+	xccdf_session_configure_remote_resources(session, allowed, false, callback);
+}
+
 void xccdf_session_set_loading_flags(struct xccdf_session *session, xccdf_session_loading_flags_t flags)
 {
 	session->loading_flags = flags;
@@ -993,6 +1001,7 @@ static int _xccdf_session_get_oval_from_model(struct xccdf_session *session)
 	while (oscap_file_entry_iterator_has_more(files_it)) {
 		struct oscap_file_entry *file_entry;
 		struct stat sb;
+		bool source_owned = false;
 
 		file_entry = (struct oscap_file_entry *) oscap_file_entry_iterator_next(files_it);
 
@@ -1002,9 +1011,6 @@ static int _xccdf_session_get_oval_from_model(struct xccdf_session *session)
 
 		const char *file_path = oscap_file_entry_get_file(file_entry);
 		struct oscap_source *source = NULL;
-		if (xccdf_session_get_ds_sds_session(session) != NULL) {
-			source = ds_sds_session_get_component_by_href(xccdf_session_get_ds_sds_session(session), file_path);
-		}
 
 		tmp_path = malloc(PATH_MAX * sizeof(char));
 		if (file_path[0] == '/') { // it's a simple absolute path
@@ -1017,16 +1023,20 @@ static int _xccdf_session_get_oval_from_model(struct xccdf_session *session)
 			snprintf(tmp_path, PATH_MAX, "%s/%s", dir_path, file_path);
 		}
 
-		if (source != NULL || stat(tmp_path, &sb) == 0) {
-			resources[idx] = malloc(sizeof(struct oval_content_resource));
-			resources[idx]->href = oscap_strdup(oscap_file_entry_get_file(file_entry));
-			if (source == NULL) {
+		if (xccdf_session_get_ds_sds_session(session) != NULL) {
+			source = ds_sds_session_get_component_by_href(xccdf_session_get_ds_sds_session(session), file_path);
+			source_owned = false;
+		} else {
+			if (stat(tmp_path, &sb) == 0) {
 				source = oscap_source_new_from_file(tmp_path);
-				resources[idx]->source_owned = true;
-			}
-			else {
-				resources[idx]->source_owned = false;
+				source_owned = true;
 			}
+		}
+
+		if (source != NULL) {
+			resources[idx] = malloc(sizeof(struct oval_content_resource));
+			resources[idx]->href = oscap_strdup(oscap_file_entry_get_file(file_entry));
+			resources[idx]->source_owned = source_owned;
 			resources[idx]->source = source;
 			idx++;
 			void *new_resources = realloc(resources, (idx + 1) * sizeof(struct oval_content_resource *));
diff --git a/tests/DS/CMakeLists.txt b/tests/DS/CMakeLists.txt
index f239586f8b..aac089cab5 100644
--- a/tests/DS/CMakeLists.txt
+++ b/tests/DS/CMakeLists.txt
@@ -4,6 +4,7 @@ add_oscap_test("test_sds_compose_split.sh")
 add_oscap_test("test_sds_eval.sh")
 add_oscap_test("test_sds_fix_from_results.sh")
 add_oscap_test("test_sds_fix_from_source.sh")
+add_oscap_test("test_ds_use_local_remote_resources.sh")
 
 add_subdirectory("ds_sds_index")
 add_subdirectory("schematron")
diff --git a/tests/DS/ds_use_local_remote_resources/remote.oval.xml b/tests/DS/ds_use_local_remote_resources/remote.oval.xml
new file mode 100644
index 0000000000..ed864a462d
--- /dev/null
+++ b/tests/DS/ds_use_local_remote_resources/remote.oval.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<oval_definitions xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ind-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" xmlns:unix-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix" xmlns:lin-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5" xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix unix-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#independent independent-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#linux linux-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-common-5 oval-common-schema.xsd">
+  <generator>
+    <oval:schema_version>5.10</oval:schema_version>
+    <oval:timestamp>0001-01-01T00:00:00+00:00</oval:timestamp>
+  </generator>
+
+  <definitions>
+    <definition class="compliance" version="1" id="oval:x:def:1">
+      <metadata>
+        <title>This definition will pass</title>
+        <description>x</description>
+        <affected family="unix">
+          <platform>x</platform>
+        </affected>
+      </metadata>
+      <criteria comment="x" operator="OR">
+        <criterion test_ref="oval:x:tst:1" comment="always pass"/>
+        <criterion test_ref="oval:x:tst:2" comment="always fail"/>
+      </criteria>
+    </definition>
+    <definition class="compliance" version="1" id="oval:x:def:2">
+      <metadata>
+        <title>This definition will fail</title>
+        <description>x</description>
+        <affected family="unix">
+          <platform>x</platform>
+        </affected>
+      </metadata>
+      <criteria comment="x" operator="AND">
+        <criterion test_ref="oval:x:tst:1" comment="always pass"/>
+        <criterion test_ref="oval:x:tst:2" comment="always fail"/>
+      </criteria>
+    </definition>
+  </definitions>
+
+  <tests>
+    <variable_test id="oval:x:tst:1" check="all" comment="always pass" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent">
+      <object object_ref="oval:x:obj:1"/>
+    </variable_test>
+
+    <variable_test id="oval:x:tst:2" check="all" check_existence="none_exist" comment="always fail" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent">
+      <object object_ref="oval:x:obj:1"/>
+    </variable_test>
+  </tests>
+
+  <objects>
+    <variable_object id="oval:x:obj:1" version="1" comment="x" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent">
+      <var_ref>oval:x:var:1</var_ref>
+    </variable_object>
+  </objects>
+
+  <variables>
+    <constant_variable id="oval:x:var:1" version="1" comment="x" datatype="string">
+      <value>x</value>
+    </constant_variable>
+  </variables>
+</oval_definitions>
diff --git a/tests/DS/ds_use_local_remote_resources/remote_content_1.3.ds.xml b/tests/DS/ds_use_local_remote_resources/remote_content_1.3.ds.xml
new file mode 100644
index 0000000000..ab1a0f1458
--- /dev/null
+++ b/tests/DS/ds_use_local_remote_resources/remote_content_1.3.ds.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ds:data-stream-collection xmlns:ds="http://scap.nist.gov/schema/scap/source/1.2" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:cat="urn:oasis:names:tc:entity:xmlns:xml:catalog" id="scap_org.open-scap_collection_from_xccdf_test_single_rule.xccdf.xml" schematron-version="1.3">
+<ds:data-stream id="scap_org.open-scap_datastream_from_xccdf_test_single_rule.xccdf.xml" scap-version="1.3" use-case="OTHER">
+  <ds:checklists>
+    <ds:component-ref id="scap_org.open-scap_cref_test_single_rule.xccdf.xml" xlink:href="#scap_org.open-scap_comp_test_single_rule.xccdf.xml">
+      <cat:catalog>
+        <cat:uri name="test_single_rule.oval.xml" uri="#scap_org.open-scap_cref_test_single_rule.oval.xml"/>
+        <cat:uri name="remote.oval.xml" uri="#scap_org.open-scap_cref_remote.oval.xml"/>
+      </cat:catalog>
+    </ds:component-ref>
+  </ds:checklists>
+  <ds:checks>
+    <ds:component-ref id="scap_org.open-scap_cref_test_single_rule.oval.xml" xlink:href="#scap_org.open-scap_comp_test_single_rule.oval.xml"/>
+    <ds:component-ref id="scap_org.open-scap_cref_remote.oval.xml" xlink:href="https://www.example.com/security/data/oval/remote.oval.xml"/>
+  </ds:checks>
+</ds:data-stream>
+
+<ds:component id="scap_org.open-scap_comp_test_single_rule.oval.xml" timestamp="2017-06-09T07:07:38">
+<oval_definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:ind-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5" xmlns:win-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#windows" xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd    http://oval.mitre.org/XMLSchema/oval-definitions-5#independent independent-definitions-schema.xsd   http://oval.mitre.org/XMLSchema/oval-definitions-5#windows windows-definitions-schema.xsd">
+  <generator>
+    <oval:schema_version>5.11</oval:schema_version>
+    <oval:timestamp>2009-01-12T10:41:00-05:00</oval:timestamp>
+  </generator>
+
+  <definitions>
+    <definition class="compliance" id="oval:test-pass:def:1" version="1">
+      <metadata>
+        <title>PASS</title>
+	<description>pass</description>
+      </metadata>
+      <criteria>
+        <criterion comment="PASS test" test_ref="oval:x:tst:1"/>
+      </criteria>
+    </definition>
+  </definitions>
+
+    <tests>
+    <variable_test xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" id="oval:x:tst:1" check="all" comment="always pass" version="1">
+      <object object_ref="oval:x:obj:1"/>
+    </variable_test>
+    </tests>
+
+    <objects>
+    <variable_object xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" id="oval:x:obj:1" version="1" comment="x">
+      <var_ref>oval:x:var:1</var_ref>
+    </variable_object>
+    </objects>
+
+    <variables>
+      <constant_variable id="oval:x:var:1" version="1" comment="x" datatype="int">
+        <value>100</value>
+      </constant_variable>
+    </variables>
+
+</oval_definitions>
+</ds:component>
+
+<ds:component id="scap_org.open-scap_comp_test_single_rule.xccdf.xml" timestamp="2017-06-09T09:15:45">
+<Benchmark xmlns="http://checklists.nist.gov/xccdf/1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="xccdf_com.example.www_benchmark_dummy" xml:lang="en-US">
+  <status>accepted</status>
+  <version>1.0</version>
+
+  <Profile id="xccdf_com.example.www_profile_test_remote_res">
+    <title>xccdf_test_profile</title>
+    <description>This profile is for testing.</description>
+    <select idref="xccdf_com.example.www_rule_test-pass" selected="true"/>
+    <select idref="xccdf_com.example.www_rule_test-remote_res" selected="true"/>
+  </Profile>
+
+  <Value id="xccdf_com.example.www_value_val1" type="number" operator="equals" interactive="0">
+    <title>test value</title>
+    <description>foo</description>
+    <value selector="bar_1">50</value>
+    <value selector="bar_2">100</value>
+  </Value>
+  <Rule selected="true" id="xccdf_com.example.www_rule_test-pass">
+    <title>This rule always pass</title>
+    <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
+      <check-content-ref href="test_single_rule.oval.xml" name="oval:test-pass:def:1"/>
+    </check>
+  </Rule>
+  <Rule selected="true" id="xccdf_com.example.www_rule_test-remote_res">
+    <title>This rule checks remote resource</title>
+    <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5" multi-check="true">
+      <check-content-ref href="remote.oval.xml"/>
+    </check>
+  </Rule>
+  <Rule selected="true" id="xccdf_com.example.www_rule_test-pass2">
+    <title>This rule always pass</title>
+    <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
+      <check-content-ref href="test_single_rule.oval.xml" name="oval:test-pass:def:1"/>
+    </check>
+  </Rule>
+</Benchmark>
+</ds:component>
+</ds:data-stream-collection>
diff --git a/tests/DS/test_ds_use_local_remote_resources.sh b/tests/DS/test_ds_use_local_remote_resources.sh
new file mode 100755
index 0000000000..706f38c2bc
--- /dev/null
+++ b/tests/DS/test_ds_use_local_remote_resources.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+# Author:
+#   Jan Černý <jcerny@redhat.com>
+
+set -e -o pipefail
+set -x
+
+. $builddir/tests/test_common.sh
+	
+PROFILE="xccdf_com.example.www_profile_test_remote_res"
+result=$(mktemp)
+stderr=$(mktemp)
+tmpdir=$(mktemp -d)
+cp "${srcdir}/ds_use_local_remote_resources/remote_content_1.3.ds.xml" "$tmpdir"
+cp "${srcdir}/ds_use_local_remote_resources/remote.oval.xml" "$tmpdir"
+pushd "$tmpdir"
+
+$OSCAP xccdf eval --use-local-file --profile "$PROFILE" --results "$result" "remote_content_1.3.ds.xml" 2>"$stderr" || ret=$?
+[ "$ret" = 2 ]
+
+grep -q "WARNING: Datastream component 'scap_org.open-scap_cref_remote.oval.xml' points out to the remote 'https://www.example.com/security/data/oval/remote.oval.xml'. Use '--fetch-remote-resources' option to download it." "$stderr" && false
+grep -q "WARNING: Skipping 'https://www.example.com/security/data/oval/remote.oval.xml' file which is referenced from datastream" "$stderr" && false
+
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass"]/result[text()="pass"]'
+# the remote_res rule is a multicheck with 2 oval definitions so it's twice here
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-remote_res"]/result[text()="pass"]'
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-remote_res"]/result[text()="fail"]'
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass2"]/result[text()="pass"]'
+
+popd
+rm -f "$result" "$stderr"
+rm -rf "$tmpdir"
+
+
+# test the same without --use-local-file to make sure the $tmpdir/remote.oval.xml isn't loaded by oscap
+
+result=$(mktemp)
+stderr=$(mktemp)
+tmpdir=$(mktemp -d)
+cp "${srcdir}/ds_use_local_remote_resources/remote_content_1.3.ds.xml" "$tmpdir"
+cp "${srcdir}/ds_use_local_remote_resources/remote.oval.xml" "$tmpdir"
+pushd "$tmpdir"
+
+$OSCAP xccdf eval --profile "$PROFILE" --results "$result" "remote_content_1.3.ds.xml" 2>"$stderr" || ret=$?
+[ "$ret" = 2 ]
+
+grep -q "WARNING: Datastream component 'scap_org.open-scap_cref_remote.oval.xml' points out to the remote 'https://www.example.com/security/data/oval/remote.oval.xml'. Use '--fetch-remote-resources' option to download it." "$stderr"
+grep -q "WARNING: Skipping 'https://www.example.com/security/data/oval/remote.oval.xml' file which is referenced from datastream" "$stderr"
+
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass"]/result[text()="pass"]'
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-remote_res"]/result[text()="notchecked"]'
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass2"]/result[text()="pass"]'
+
+popd
+rm -f "$result" "$stderr"
+rm -rf "$tmpdir"
\ No newline at end of file
diff --git a/utils/oscap-ds.c b/utils/oscap-ds.c
index 772c9c3283..6a42724597 100644
--- a/utils/oscap-ds.c
+++ b/utils/oscap-ds.c
@@ -82,7 +82,8 @@ static struct oscap_module DS_SDS_SPLIT_MODULE = {
 		"   --xccdf-id <id>               - ID of XCCDF in the data stream that should be evaluated.\n"
 		"   --skip-valid                  - Skips validating of given XCCDF.\n"
 		"   --skip-validation\n"
-		"   --fetch-remote-resources      - Download remote content referenced by data stream.\n",
+		"   --fetch-remote-resources      - Download remote content referenced by data stream.\n"
+		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n",
 	.opt_parser = getopt_ds,
 	.func = app_ds_sds_split
 };
@@ -186,6 +187,7 @@ bool getopt_ds(int argc, char **argv, struct oscap_action *action) {
 		{"xccdf-id",		required_argument, NULL, DS_OPT_XCCDF_ID},
 		{"report-id",		required_argument, NULL, DS_OPT_REPORT_ID},
 		{"fetch-remote-resources", no_argument, &action->remote_resources, 1},
+		{"use-local-file", no_argument, &action->use_local_file, 1},
 	// end
 		{0, 0, 0, 0}
 	};
@@ -308,7 +310,7 @@ int app_ds_sds_split(const struct oscap_action *action) {
 	}
 	ds_sds_session_set_datastream_id(session, f_datastream_id);
 
-	ds_sds_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	ds_sds_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 	ds_sds_session_set_target_dir(session, action->ds_action->target);
 	if (ds_sds_session_register_component_with_dependencies(session, "checklists", f_component_id, NULL) != 0) {
 		goto cleanup;
diff --git a/utils/oscap-info.c b/utils/oscap-info.c
index 46721eba05..01db5153b3 100644
--- a/utils/oscap-info.c
+++ b/utils/oscap-info.c
@@ -63,6 +63,7 @@ struct oscap_module OSCAP_INFO_MODULE = {
     .usage = "some-file.xml",
 	.help = "Options:\n"
 		"   --fetch-remote-resources      - Download remote content referenced by data stream.\n"
+		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
 		"   --profile <id>                - Show info of the profile with the given ID.\n"
 		"   --profiles                    - Show profiles from the input file in the <id>:<title> format, one line per profile.\n",
     .opt_parser = getopt_info,
@@ -531,7 +532,7 @@ static int app_info_sds(struct oscap_source *source, const struct oscap_action *
 		return OSCAP_ERROR;
 	}
 
-	ds_sds_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	ds_sds_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 
 	/* get collection */
 	struct ds_sds_index *sds = ds_sds_session_get_sds_idx(session);
@@ -762,6 +763,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action)
 	/* Command-options */
 	const struct option long_options[] = {
 		{"fetch-remote-resources", no_argument, &action->remote_resources, 1},
+		{"use-local-file", no_argument, &action->use_local_file, 1},
 		{"profile", required_argument, 0, 'p'},
 		{"profiles", no_argument, 0, 'n'},
 		// end
diff --git a/utils/oscap-oval.c b/utils/oscap-oval.c
index 39bcb92edb..582a5695e3 100644
--- a/utils/oscap-oval.c
+++ b/utils/oscap-oval.c
@@ -115,7 +115,8 @@ static struct oscap_module OVAL_EVAL = {
 	"   --oval-id <id>                - ID of the OVAL component ref in the data stream to use.\n"
 	"                                   (only applicable for source data streams)\n"
 	"   --fetch-remote-resources      - Download remote content referenced by OVAL Definitions.\n"
-	"                                   (only applicable for source data streams)\n",
+	"                                   (only applicable for source data streams)\n"
+	"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n",
     .opt_parser = getopt_oval_eval,
     .func = app_evaluate_oval
 };
@@ -344,7 +345,7 @@ int app_evaluate_oval(const struct oscap_action *action)
 	/* set OVAL Variables */
 	oval_session_set_variables(session, action->f_variables);
 
-	oval_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	oval_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 	/* load all necesary OVAL Definitions and bind OVAL Variables if provided */
 	if ((oval_session_load(session)) != 0)
 		goto cleanup;
@@ -520,6 +521,7 @@ bool getopt_oval_eval(int argc, char **argv, struct oscap_action *action)
 		{ "skip-valid",	no_argument, &action->validate, 0 },
 		{ "skip-validation",	no_argument, &action->validate, 0 },
 		{ "fetch-remote-resources", no_argument, &action->remote_resources, 1},
+		{ "use-local-file", no_argument, &action->use_local_file, 1},
 		{ 0, 0, 0, 0 }
 	};
 
diff --git a/utils/oscap-tool.h b/utils/oscap-tool.h
index 18cce0fbce..a3b8781b27 100644
--- a/utils/oscap-tool.h
+++ b/utils/oscap-tool.h
@@ -174,6 +174,7 @@ struct oscap_action {
         int list_dynamic;
 	char *verbosity_level;
 	char *fix_type;
+	int use_local_file;
 };
 
 int app_xslt(const char *infile, const char *xsltfile, const char *outfile, const char **params);
diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c
index 801e54fa35..ef2768bdef 100644
--- a/utils/oscap-xccdf.c
+++ b/utils/oscap-xccdf.c
@@ -129,6 +129,7 @@ static struct oscap_module XCCDF_EXPORT_OVAL_VARIABLES = {
 		"   --skip-valid                  - Skip validation.\n"
 		"   --skip-validation\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
+		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
 		"   --datastream-id <id>          - ID of the data stream in the collection to use.\n"
 		"                                   (only applicable for source data streams)\n"
 		"   --xccdf-id <id>               - ID of component-ref with XCCDF in the data stream that should be evaluated.\n"
@@ -171,6 +172,7 @@ static struct oscap_module XCCDF_EVAL = {
 		"                                   (only applicable for source data streams)\n"
 		"   --enforce-signature           - Process only signed data streams.\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
+		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
 		"   --progress                    - Switch to sparse output suitable for progress reporting.\n"
 		"                                   Format is \"$rule_id:$result\\n\".\n"
 		"   --datastream-id <id>          - ID of the data stream in the collection to use.\n"
@@ -199,6 +201,7 @@ static struct oscap_module XCCDF_REMEDIATE = {
 		"   --cpe <name>                  - Use given CPE dictionary or language (autodetected)\n"
 		"                                   for applicability checks.\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
+		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
 		"   --results <file>              - Write XCCDF Results into file.\n"
 		"   --results-arf <file>          - Write ARF (result data stream) into file.\n"
 		"   --stig-viewer <file>          - Writes XCCDF results into FILE in a format readable by DISA STIG Viewer\n"
@@ -573,7 +576,7 @@ int app_evaluate_xccdf(const struct oscap_action *action)
 	if (action->tailoring_file != NULL)
 		xccdf_session_set_user_tailoring_file(session, action->tailoring_file);
 	xccdf_session_set_user_tailoring_cid(session, action->tailoring_id);
-	xccdf_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 	xccdf_session_set_product_cpe(session, OSCAP_PRODUCTNAME);
 	xccdf_session_set_rule(session, action->rule);
@@ -678,7 +681,7 @@ static int app_xccdf_export_oval_variables(const struct oscap_action *action)
 		xccdf_session_set_benchmark_id(session, action->f_benchmark_id);
 	}
 	xccdf_session_set_user_cpe(session, action->cpe);
-	xccdf_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 	xccdf_session_set_custom_oval_eval_fn(session, resolve_variables_wrapper);
 
@@ -721,7 +724,7 @@ int app_xccdf_remediate(const struct oscap_action *action)
 		goto cleanup;
 	xccdf_session_set_validation(session, action->validate, getenv("OSCAP_FULL_VALIDATION") != NULL);
 	xccdf_session_set_user_cpe(session, action->cpe);
-	xccdf_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 
 	if (xccdf_session_load(session) != 0)
@@ -937,7 +940,7 @@ int app_generate_fix(const struct oscap_action *action)
 	xccdf_session_set_signature_validation(session, action->validate_signature);
 	xccdf_session_set_signature_enforcement(session, action->enforce_signature);
 	xccdf_session_set_user_cpe(session, action->cpe);
-	xccdf_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 	xccdf_session_set_user_tailoring_file(session, action->tailoring_file);
 	xccdf_session_set_user_tailoring_cid(session, action->tailoring_id);
@@ -1012,7 +1015,7 @@ int app_generate_guide(const struct oscap_action *action)
 	xccdf_session_set_validation(session, action->validate, getenv("OSCAP_FULL_VALIDATION") != NULL);
 	xccdf_session_set_signature_validation(session, action->validate_signature);
 	xccdf_session_set_signature_enforcement(session, action->enforce_signature);
-	xccdf_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
 	xccdf_session_set_user_tailoring_file(session, action->tailoring_file);
 	xccdf_session_set_user_tailoring_cid(session, action->tailoring_id);
 	if (xccdf_session_is_sds(session)) {
@@ -1166,6 +1169,7 @@ bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
 		{"skip-signature-validation", no_argument, &action->validate_signature, 0},
 		{"enforce-signature", no_argument, &action->enforce_signature, 1},
 		{"fetch-remote-resources", no_argument, &action->remote_resources, 1},
+		{"use-local-file", no_argument, &action->use_local_file, 1},
 		{"progress", no_argument, &action->progress, 1},
 		{"remediate", no_argument, &action->remediate, 1},
 		{"hide-profile-info",	no_argument, &action->hide_profile_info, 1},
diff --git a/utils/oscap.8 b/utils/oscap.8
index 6cae0ffe8a..8dcb9ca330 100644
--- a/utils/oscap.8
+++ b/utils/oscap.8
@@ -72,6 +72,11 @@ For XCCDF or SCAP source data stream files, the info module prints out IDs of in
 Allow download of remote components referenced from data stream.
 .RE
 .TP
+\fB\-\-use-local-file\fR
+.RS
+Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+.RE
+.TP
 \fB\-\-profile PROFILE\fR
 .RS
 Show info of the profile with the given ID.
@@ -201,6 +206,11 @@ Process only digitally signed SCAP source data streams. Data streams without a s
 Allow download of remote OVAL content referenced from XCCDF by check-content-ref/@href.
 .RE
 .TP
+\fB\-\-use-local-file\fR
+.RS
+Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+.RE
+.TP
 \fB\-\-remediate\fR
 .RS
 Execute XCCDF remediation in the process of XCCDF evaluation. This option automatically executes content of XCCDF fix elements for failed rules, and thus this shall be avoided unless for trusted content. Use of this option is always at your own risk.
@@ -226,6 +236,11 @@ Do not validate input/output files.
 Allow download of remote OVAL content referenced from XCCDF by check-content-ref/@href.
 .RE
 .TP
+\fB\-\-use-local-file\fR
+.RS
+Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+.RE
+.TP
 \fB\-\-cpe CPE_FILE\fR
 .RS
 Use given CPE dictionary or language (auto-detected) for applicability checks.
@@ -303,6 +318,11 @@ Select a particular profile from XCCDF document.
 Allow download of remote OVAL content referenced from XCCDF by check-content-ref/@href.
 .RE
 .TP
+\fB\-\-use-local-file\fR
+.RS
+Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+.RE
+.TP
 \fB\-\-skip-valid\fR, \fB\-\-skip-validation\fR
 .RS
 Do not validate input/output files.
@@ -477,6 +497,9 @@ Do not validate input/output files.
 .TP
 \fB\-\-fetch-remote-resources\fR
 Allow download of remote components referenced from data stream.
+.TP
+\fB\-\-use-local-file\fR
+Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 
 .TP
@@ -638,6 +661,9 @@ Do not validate input/output files.
 .TP
 \fB\-\-fetch-remote-resources\fR
 Allow download of remote components referenced from data stream.
+.TP
+\fB\-\-use-local-file\fR
+Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 .TP
 .B \fBsds-validate\fR SOURCE_DS

From 0a24a755b7102b716da7237717aa78290f8efe9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Tue, 13 Jul 2021 08:41:08 +0200
Subject: [PATCH 02/10] Describe using local files in user manual

To increase visiblity it's also added to the FAQs.
---
 docs/manual/manual.adoc | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc
index 409d502a3b..8655f518f0 100644
--- a/docs/manual/manual.adoc
+++ b/docs/manual/manual.adoc
@@ -1636,6 +1636,25 @@ Rule    xccdf_org.ssgproject.content_rule_partition_for_var_log
 ...
 ----
 
+On systems that don't have a direct internet access or if the user doesn't want OpenSCAP to connect to the network it's possible to download the remote content using other tools and then pass it to OpenSCAP as a file.
+To do that, use `--use-local-file` instead of `--fetch-remote-resources` as argument of the `oscap` command.
+
+In place of the remote data stream component OpenSCAP  will attempt to use a file whose file name is equal to `name` attribute of the `uri` element within the `catalog` element within the `component-ref` element representing a checklist in the data stream if such file exists.
+
+In the following example, the `ssg-rhel8-ds.xml` is an SCAP source datastream.
+It needs some checks from a remote component. The remote component's `component-ref` ID is `scap_org.open-scap_cref_security-data-oval-com.redhat.rhsa-RHEL8.xml`  and the `component-ref` is pointing to `https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml`.
+The checks from the remote component are used in the only checklist in the data stream.
+The `component-ref` of the checklist component contains a `catalog` where one of the `uri` elements maps the remote component's `component-ref` ID in the `uri` attribute to the actual name `security-data-oval-com.redhat.rhsa-RHEL8.xml` which is the value of the `name` attribute.
+Therefore, we can download the remote data from `https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml` and save it as `security-data-oval-com.redhat.rhsa-RHEL8.xml`.
+Then, we can optionally copy the file to the computer which we want to scan.
+Then, we execute `oscap` with `--use-local-file` in that directory.
+It will pick the file and use it instead of the remote data and it won't connect to the network.
+
+----
+$ wget -O security-data-oval-com.redhat.rhsa-RHEL8.xml https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml
+...
+$ oscap xccdf eval --use-local-file --profile ospp ssg-rhel8-ds.xml
+----
 
 == Practical Examples
 This section demonstrates practical usage of certain security content provided
@@ -2133,3 +2152,11 @@ The downloaded guidance contains rule descriptions, but it doesn't contain OVAL
 
 Make sure that you provide the ID of the customized profile in `--profile` option instead of the ID of the original profile.
 If you created the tailoring file using SCAP Workbench, you were prompted to choose the ID of the customized profile. You can display the ID of the customized profile by running `oscap info <your_tailoring_file>`. By default, the ID of the customized profile ends with `_customized` suffix.
+
+*My SCAP source data stream contains rule `security_patches_up_to_date` which needs to download some data from the internet to work.*
+*But I'm in an air gapped environment so it can't download it.*
+*Can I download it separately and pass it to oscap?*
+
+Yes, it's possible, you can download the file on other computer that is connected to the internet and then copy the file to the system where you run `oscap`.
+Instead of the `--fetch-remote-resources` option you will use the `--use-local-file` option.
+For more information, please refer to section <<_using_external_or_remote_resources,Using external or remote resources>>.

From 86ce263d26405d08abdf027e9b71bea69386e51f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Thu, 22 Jul 2021 16:19:36 +0200
Subject: [PATCH 03/10] Provide a directory path

This will allow users to pass a directory path where downloaded files
are stored.

Currently, when it attempts to open a file, the path is resolved
relatively to the data stream file. For example, that means if the data
stream file is located in /usr/share/xml/scap you need to copy the file
there and you can have permission problems. Instead, we will pass the
directory and store the downloaded file to the directory.

The interface and command line options are updated to accept the
directory path. Moreover, we have realized that there can be multiple
remote components in a single source data stream. Therefore, we have
found a more suitable name for the option which would more fit that the
option accepts an argument and there can be multiple files.
---
 docs/manual/manual.adoc                       | 16 +++++-----
 src/DS/ds_sds_session.c                       | 10 +++---
 src/DS/ds_sds_session_priv.h                  |  2 +-
 src/DS/public/ds_sds_session.h                |  7 +++--
 src/DS/sds.c                                  | 31 ++++++++++++++++---
 src/OVAL/oval_session.c                       | 10 +++---
 src/OVAL/public/oval_session.h                |  7 +++--
 src/XCCDF/public/xccdf_session.h              |  7 +++--
 src/XCCDF/xccdf_session.c                     | 12 +++----
 .../DS/test_ds_use_local_remote_resources.sh  | 19 ++++++------
 utils/oscap-ds.c                              | 10 ++++--
 utils/oscap-info.c                            |  9 ++++--
 utils/oscap-oval.c                            | 12 ++++---
 utils/oscap-tool.h                            |  2 +-
 utils/oscap-xccdf.c                           | 24 ++++++++------
 utils/oscap.8                                 | 18 +++++------
 16 files changed, 118 insertions(+), 78 deletions(-)

diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc
index 8655f518f0..9f0fc38b75 100644
--- a/docs/manual/manual.adoc
+++ b/docs/manual/manual.adoc
@@ -1636,8 +1636,8 @@ Rule    xccdf_org.ssgproject.content_rule_partition_for_var_log
 ...
 ----
 
-On systems that don't have a direct internet access or if the user doesn't want OpenSCAP to connect to the network it's possible to download the remote content using other tools and then pass it to OpenSCAP as a file.
-To do that, use `--use-local-file` instead of `--fetch-remote-resources` as argument of the `oscap` command.
+On systems that don't have a direct internet access or if the user doesn't want OpenSCAP to connect to the network it's possible to download the remote content using other tools, save it to a directory and then pass it to OpenSCAP as a file.
+To do that, use `--local-files` instead of `--fetch-remote-resources` as argument of the `oscap` command.
 
 In place of the remote data stream component OpenSCAP  will attempt to use a file whose file name is equal to `name` attribute of the `uri` element within the `catalog` element within the `component-ref` element representing a checklist in the data stream if such file exists.
 
@@ -1645,15 +1645,15 @@ In the following example, the `ssg-rhel8-ds.xml` is an SCAP source datastream.
 It needs some checks from a remote component. The remote component's `component-ref` ID is `scap_org.open-scap_cref_security-data-oval-com.redhat.rhsa-RHEL8.xml`  and the `component-ref` is pointing to `https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml`.
 The checks from the remote component are used in the only checklist in the data stream.
 The `component-ref` of the checklist component contains a `catalog` where one of the `uri` elements maps the remote component's `component-ref` ID in the `uri` attribute to the actual name `security-data-oval-com.redhat.rhsa-RHEL8.xml` which is the value of the `name` attribute.
-Therefore, we can download the remote data from `https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml` and save it as `security-data-oval-com.redhat.rhsa-RHEL8.xml`.
-Then, we can optionally copy the file to the computer which we want to scan.
-Then, we execute `oscap` with `--use-local-file` in that directory.
+Therefore, we can download the remote data from `https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml` and save it as `security-data-oval-com.redhat.rhsa-RHEL8.xml` to some directory.
+Then, we execute `oscap` with `--local-files` and provide a path to the directory where it's located.
 It will pick the file and use it instead of the remote data and it won't connect to the network.
 
 ----
-$ wget -O security-data-oval-com.redhat.rhsa-RHEL8.xml https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml
+$ mkdir ~/scap-files
+$ wget -O ~/scap-files/security-data-oval-com.redhat.rhsa-RHEL8.xml https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL8.xml
 ...
-$ oscap xccdf eval --use-local-file --profile ospp ssg-rhel8-ds.xml
+$ oscap xccdf eval --local-files ~/scap-files --profile ospp ssg-rhel8-ds.xml
 ----
 
 == Practical Examples
@@ -2158,5 +2158,5 @@ If you created the tailoring file using SCAP Workbench, you were prompted to cho
 *Can I download it separately and pass it to oscap?*
 
 Yes, it's possible, you can download the file on other computer that is connected to the internet and then copy the file to the system where you run `oscap`.
-Instead of the `--fetch-remote-resources` option you will use the `--use-local-file` option.
+Instead of the `--fetch-remote-resources` option you will use the `--local-files` option.
 For more information, please refer to section <<_using_external_or_remote_resources,Using external or remote resources>>.
diff --git a/src/DS/ds_sds_session.c b/src/DS/ds_sds_session.c
index 9d9a9c8b52..99f4bc20fd 100644
--- a/src/DS/ds_sds_session.c
+++ b/src/DS/ds_sds_session.c
@@ -55,7 +55,7 @@ struct ds_sds_session {
 	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.
-	bool use_local_file;                    ///< Use a locally downloaded copy of a remote resource if it exists
+	const char *local_files;            ///< Path to the directory where local copies of remote components are located
 };
 
 /**
@@ -338,10 +338,10 @@ int ds_sds_session_register_component_with_dependencies(struct ds_sds_session *s
 	return res;
 }
 
-void ds_sds_session_configure_remote_resources(struct ds_sds_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback)
+void ds_sds_session_configure_remote_resources(struct ds_sds_session *session, bool allowed, const char *local_files, download_progress_calllback_t callback)
 {
 	session->fetch_remote_resources = allowed;
-	session->use_local_file = use_local_file;
+	session->local_files = local_files;
 	session->progress = (callback != NULL) ? callback : download_progress_empty_calllback;
 }
 
@@ -350,9 +350,9 @@ void ds_sds_session_set_remote_resources(struct ds_sds_session *session, bool al
 	ds_sds_session_configure_remote_resources(session, allowed, false, callback);
 }
 
-bool ds_sds_session_can_use_local_file(struct ds_sds_session *session)
+const char *ds_sds_session_local_files(struct ds_sds_session *session)
 {
-	return session->use_local_file;
+	return session->local_files;
 }
 
 int ds_sds_session_dump_component_files(struct ds_sds_session *session)
diff --git a/src/DS/ds_sds_session_priv.h b/src/DS/ds_sds_session_priv.h
index 018cd053ec..5e6dfc7672 100644
--- a/src/DS/ds_sds_session_priv.h
+++ b/src/DS/ds_sds_session_priv.h
@@ -40,7 +40,7 @@ struct oscap_htable *ds_sds_session_get_component_sources(struct ds_sds_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);
-bool ds_sds_session_can_use_local_file(struct ds_sds_session *session);
+const char *ds_sds_session_local_files(struct ds_sds_session *session);
 download_progress_calllback_t ds_sds_session_remote_resources_progress(struct ds_sds_session *session);
 
 void download_progress_empty_calllback(bool warning, const char * format, ...);
diff --git a/src/DS/public/ds_sds_session.h b/src/DS/public/ds_sds_session.h
index 695a0df215..83e7feadba 100644
--- a/src/DS/public/ds_sds_session.h
+++ b/src/DS/public/ds_sds_session.h
@@ -208,12 +208,13 @@ OSCAP_API OSCAP_DEPRECATED(void ds_sds_session_set_remote_resources(struct ds_sd
  * @param session The source data stream session
  * @param allowed Whether is download of remote resources allowed in this
  * session (defaults to false)
- * @param use_local_file Allows to use a locally downloaded copy of the remote
- * resource if it exists (defaults to false)
+ * @param local_files Allows to use a locally downloaded copy of the remote
+ * resources. Contains a path to a directory where the files are stored
+ * (defaults to NULL).
  * @param callback used to notify user about download proceeds. This might be
  * safely set to NULL -- ignoring user notification.
  */
-OSCAP_API void ds_sds_session_configure_remote_resources(struct ds_sds_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback);
+OSCAP_API void ds_sds_session_configure_remote_resources(struct ds_sds_session *session, bool allowed, const char *local_files, download_progress_calllback_t callback);
 
 /**
  * Returns HTML representation of selected checklist in form of OpenSCAP guide.
diff --git a/src/DS/sds.c b/src/DS/sds.c
index b1737aa57f..365ae96987 100644
--- a/src/DS/sds.c
+++ b/src/DS/sds.c
@@ -425,14 +425,35 @@ static int ds_sds_dump_component_by_href(struct ds_sds_session *session, char* x
 			 * the URL defined in @xlink:href. This way people can provide the
 			 * previously downloaded component which might be useful on systems
 			 * with limited internet access. This behavior is allowed only when
-			 * --use-local-file is used on the command line.
+			 * --local-files is used on the command line.
 			 * See: https://bugzilla.redhat.com/show_bug.cgi?id=1970527
 			 * See: https://access.redhat.com/solutions/5185891
 			 */
-			struct stat sb;
-			if (ds_sds_session_can_use_local_file(session) && stat(relative_filepath, &sb) == 0) {
-				dI("Using local file '%s' instead of '%s'", relative_filepath, xlink_href);
-				return ds_sds_dump_file_component(relative_filepath, *component_id, session, target_filename_dirname, relative_filepath);
+			const char *local_files = ds_sds_session_local_files(session);
+			if (local_files != NULL) {
+				char *local_filepath = oscap_path_join(local_files, relative_filepath);
+				struct stat sb;
+				if (stat(local_filepath, &sb) == 0) {
+				//if (ds_sds_session_can_use_local_file(session)) {
+					dI("Using local file '%s' instead of '%s'", local_filepath, xlink_href);
+					struct oscap_source *source_file = oscap_source_new_from_file(local_filepath);
+					xmlDoc *doc = oscap_source_get_xmlDoc(source_file);
+					if (doc == NULL) {
+						free(local_filepath);
+						return -1;
+					}
+					xmlNodePtr inner_root = ds_sds_get_component_root_by_id(doc, *component_id);
+
+					if (ds_sds_register_component(session, doc, inner_root, *component_id, target_filename_dirname, relative_filepath) != 0) {
+						free(local_filepath);
+						return -1;
+					}
+					free(local_filepath);
+					return 0;
+				} else {
+					dW("Can't use local file '%s' instead of '%s'", local_filepath, xlink_href);
+				}
+				free(local_filepath);
 			}
 
 			static bool fetch_remote_resources_suggested = false;
diff --git a/src/OVAL/oval_session.c b/src/OVAL/oval_session.c
index 5adda43f66..bb73543ed8 100644
--- a/src/OVAL/oval_session.c
+++ b/src/OVAL/oval_session.c
@@ -84,7 +84,7 @@ struct oval_session {
 	bool full_validation;
 	bool fetch_remote_resources;
 	download_progress_calllback_t progress;
-	bool use_local_file;
+	const char *local_files;
 };
 
 struct oval_session *oval_session_new(const char *filename)
@@ -224,7 +224,7 @@ static int oval_session_load_definitions(struct oval_session *session)
 		if ((session->sds_session = ds_sds_session_new_from_source(session->source)) == NULL) {
 			return 1;
 		}
-		ds_sds_session_configure_remote_resources(session->sds_session, session->fetch_remote_resources, session->use_local_file, session->progress);
+		ds_sds_session_configure_remote_resources(session->sds_session, session->fetch_remote_resources, session->local_files, session->progress);
 		ds_sds_session_set_datastream_id(session->sds_session, session->datastream_id);
 		if (ds_sds_session_register_component_with_dependencies(session->sds_session,
 					"checks", session->component_id, "oval.xml") != 0) {
@@ -452,16 +452,16 @@ void oval_session_set_export_system_characteristics(struct oval_session *session
 	session->export_sys_chars = export;
 }
 
-void oval_session_configure_remote_resources(struct oval_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback)
+void oval_session_configure_remote_resources(struct oval_session *session, bool allowed, const char *local_files, download_progress_calllback_t callback)
 {
 	session->fetch_remote_resources = allowed;
-	session->use_local_file = use_local_file;
+	session->local_files = local_files;
 	session->progress = callback;
 }
 
 void oval_session_set_remote_resources(struct oval_session *session, bool allowed, download_progress_calllback_t callback)
 {
-	oval_session_configure_remote_resources(session, allowed, false, callback);
+	oval_session_configure_remote_resources(session, allowed, NULL, callback);
 }
 
 void oval_session_free(struct oval_session *session)
diff --git a/src/OVAL/public/oval_session.h b/src/OVAL/public/oval_session.h
index d485676747..378c77c490 100644
--- a/src/OVAL/public/oval_session.h
+++ b/src/OVAL/public/oval_session.h
@@ -247,12 +247,13 @@ OSCAP_API OSCAP_DEPRECATED(void oval_session_set_remote_resources(struct oval_se
  * @param session an \ref oval_session
  * @param allowed Whether is download of remote resources allowed in this
  * session (defaults to false)
- * @param use_local_file Allows to use a locally downloaded copy of the remote
- * resource if it exists (defaults to false)
+ * @param local_files Allows to use a locally downloaded copy of the remote
+ * resources. Contains a path to a directory where the files are stored
+ * (defaults to NULL).
  * @param callback used to notify user about download proceeds. This might be
  * safely set to NULL -- ignoring user notification.
  */
-OSCAP_API void oval_session_configure_remote_resources(struct oval_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback);
+OSCAP_API void oval_session_configure_remote_resources(struct oval_session *session, bool allowed, const char *local_files, download_progress_calllback_t callback);
 
 /**
  * Destructor of an \ref oval_session.
diff --git a/src/XCCDF/public/xccdf_session.h b/src/XCCDF/public/xccdf_session.h
index 2eb7128019..466ef2b9d4 100644
--- a/src/XCCDF/public/xccdf_session.h
+++ b/src/XCCDF/public/xccdf_session.h
@@ -240,12 +240,13 @@ OSCAP_API OSCAP_DEPRECATED(void xccdf_session_set_remote_resources(struct xccdf_
  * @param session XCCDF Session
  * @param allowed Whether is download od remote resources allowed in this
  * session (defaults to false)
- * @param use_local_file Allows to use a locally downloaded copy of the remote
- * resource if it exists (defaults to false)
+ * @param local_files Allows to use a locally downloaded copy of the remote
+ * resources. Contains a path to a directory where the files are stored
+ * (defaults to NULL).
  * @param callback used to notify user about download proceeds. This might be
  * safely set to NULL -- ignoring user notification.
  */
-OSCAP_API void xccdf_session_configure_remote_resources(struct xccdf_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback);
+OSCAP_API void xccdf_session_configure_remote_resources(struct xccdf_session *session, bool allowed, const char *local_files, download_progress_calllback_t callback);
 
 /**
  * Disable or allow loading of depending content (OVAL, SCE, CPE)
diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c
index 85fcc90d23..990e40702b 100644
--- a/src/XCCDF/xccdf_session.c
+++ b/src/XCCDF/xccdf_session.c
@@ -87,7 +87,7 @@ struct xccdf_session {
 	} ds;
 	struct {
 		bool fetch_remote_resources;		///< Allows download of remote resources (not applicable when user sets custom oval files)
-		bool use_local_file; ///< Use a locally downloaded copy of a remote resource if it exists
+		const char *local_files; ///< Path to the directory where local copies of remote components are located
 		download_progress_calllback_t progress;	///< Callback to report progress of download.
 		struct oval_content_resource **custom_resources;///< OVAL files required by user
 		struct oval_content_resource **resources;///< OVAL files referenced from XCCDF
@@ -103,7 +103,7 @@ struct xccdf_session {
 		char *arf_file;				///< Path to ARF file to export
 		char *xccdf_file;			///< Path to XCCDF file to export
 		char *xccdf_stig_viewer_file;		///< Path to STIG Viewer XCCDF file to export
-		char *report_file;			///< Path to HTML file to eport
+		char *report_file;			///< Path to HTML file to export
 		bool oval_results;			///< Shall be the OVAL results files exported?
 		bool oval_variables;			///< Shall be the OVAL variable files exported?
 		bool check_engine_plugins_results;	///< Shall the check engine plugins results be exported?
@@ -628,7 +628,7 @@ static struct ds_sds_session *xccdf_session_get_ds_sds_session(struct xccdf_sess
 }
 
 
-void xccdf_session_configure_remote_resources(struct xccdf_session *session, bool allowed, bool use_local_file, download_progress_calllback_t callback)
+void xccdf_session_configure_remote_resources(struct xccdf_session *session, bool allowed, const char *local_files, download_progress_calllback_t callback)
 {
 	if (callback == NULL) {
 		// With empty cb we don't have to check for NULL
@@ -637,19 +637,19 @@ void xccdf_session_configure_remote_resources(struct xccdf_session *session, boo
 	}
 
 	session->oval.fetch_remote_resources = allowed;
-	session->oval.use_local_file = use_local_file;
+	session->oval.local_files = local_files;
 	session->oval.progress = callback;
 
 	if (xccdf_session_is_sds(session)) {
 		// We have to propagate this option to allow loading
 		// of external datastream components
-		ds_sds_session_configure_remote_resources(xccdf_session_get_ds_sds_session(session), allowed, use_local_file, callback);
+		ds_sds_session_configure_remote_resources(xccdf_session_get_ds_sds_session(session), allowed, local_files, callback);
 	}
 }
 
 void xccdf_session_set_remote_resources(struct xccdf_session *session, bool allowed, download_progress_calllback_t callback)
 {
-	xccdf_session_configure_remote_resources(session, allowed, false, callback);
+	xccdf_session_configure_remote_resources(session, allowed, NULL, callback);
 }
 
 void xccdf_session_set_loading_flags(struct xccdf_session *session, xccdf_session_loading_flags_t flags)
diff --git a/tests/DS/test_ds_use_local_remote_resources.sh b/tests/DS/test_ds_use_local_remote_resources.sh
index 706f38c2bc..e4a1f0eedd 100755
--- a/tests/DS/test_ds_use_local_remote_resources.sh
+++ b/tests/DS/test_ds_use_local_remote_resources.sh
@@ -11,12 +11,14 @@ set -x
 PROFILE="xccdf_com.example.www_profile_test_remote_res"
 result=$(mktemp)
 stderr=$(mktemp)
-tmpdir=$(mktemp -d)
-cp "${srcdir}/ds_use_local_remote_resources/remote_content_1.3.ds.xml" "$tmpdir"
-cp "${srcdir}/ds_use_local_remote_resources/remote.oval.xml" "$tmpdir"
-pushd "$tmpdir"
-
-$OSCAP xccdf eval --use-local-file --profile "$PROFILE" --results "$result" "remote_content_1.3.ds.xml" 2>"$stderr" || ret=$?
+tmpdir1=$(mktemp -d)
+tmpdir2=$(mktemp -d)
+tmpdir3=$(mktemp -d)
+cp "${srcdir}/ds_use_local_remote_resources/remote_content_1.3.ds.xml" "$tmpdir2"
+cp "${srcdir}/ds_use_local_remote_resources/remote.oval.xml" "$tmpdir3"
+pushd "$tmpdir1"
+
+$OSCAP xccdf eval --local-files "$tmpdir3" --profile "$PROFILE" --results "$result" "$tmpdir2/remote_content_1.3.ds.xml" 2>"$stderr" || ret=$?
 [ "$ret" = 2 ]
 
 grep -q "WARNING: Datastream component 'scap_org.open-scap_cref_remote.oval.xml' points out to the remote 'https://www.example.com/security/data/oval/remote.oval.xml'. Use '--fetch-remote-resources' option to download it." "$stderr" && false
@@ -30,10 +32,9 @@ assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass2"]/r
 
 popd
 rm -f "$result" "$stderr"
-rm -rf "$tmpdir"
-
+rm -rf "$tmpdir1" "$tmpdir2" "$tmpdir3"
 
-# test the same without --use-local-file to make sure the $tmpdir/remote.oval.xml isn't loaded by oscap
+# test the same without --local-files to make sure the $tmpdir/remote.oval.xml isn't loaded by oscap
 
 result=$(mktemp)
 stderr=$(mktemp)
diff --git a/utils/oscap-ds.c b/utils/oscap-ds.c
index 6a42724597..8207f097ec 100644
--- a/utils/oscap-ds.c
+++ b/utils/oscap-ds.c
@@ -172,7 +172,8 @@ static struct oscap_module* DS_SUBMODULES[DS_SUBMODULES_NUM] = {
 enum ds_opt {
 	DS_OPT_DATASTREAM_ID = 1,
 	DS_OPT_XCCDF_ID,
-	DS_OPT_REPORT_ID
+	DS_OPT_REPORT_ID,
+	DS_OPT_LOCAL_FILES
 };
 
 bool getopt_ds(int argc, char **argv, struct oscap_action *action) {
@@ -187,7 +188,7 @@ bool getopt_ds(int argc, char **argv, struct oscap_action *action) {
 		{"xccdf-id",		required_argument, NULL, DS_OPT_XCCDF_ID},
 		{"report-id",		required_argument, NULL, DS_OPT_REPORT_ID},
 		{"fetch-remote-resources", no_argument, &action->remote_resources, 1},
-		{"use-local-file", no_argument, &action->use_local_file, 1},
+		{"local-files", required_argument, NULL, DS_OPT_LOCAL_FILES},
 	// end
 		{0, 0, 0, 0}
 	};
@@ -199,6 +200,9 @@ bool getopt_ds(int argc, char **argv, struct oscap_action *action) {
 		case DS_OPT_DATASTREAM_ID:	action->f_datastream_id = optarg;	break;
 		case DS_OPT_XCCDF_ID:	action->f_xccdf_id = optarg; break;
 		case DS_OPT_REPORT_ID:	action->f_report_id = optarg; break;
+		case DS_OPT_LOCAL_FILES:
+			action->local_files = optarg;
+			break;
 		case 0: break;
 		default: return oscap_module_usage(action->module, stderr, NULL);
 		}
@@ -310,7 +314,7 @@ int app_ds_sds_split(const struct oscap_action *action) {
 	}
 	ds_sds_session_set_datastream_id(session, f_datastream_id);
 
-	ds_sds_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	ds_sds_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 	ds_sds_session_set_target_dir(session, action->ds_action->target);
 	if (ds_sds_session_register_component_with_dependencies(session, "checklists", f_component_id, NULL) != 0) {
 		goto cleanup;
diff --git a/utils/oscap-info.c b/utils/oscap-info.c
index 01db5153b3..2e6f5003aa 100644
--- a/utils/oscap-info.c
+++ b/utils/oscap-info.c
@@ -63,7 +63,7 @@ struct oscap_module OSCAP_INFO_MODULE = {
     .usage = "some-file.xml",
 	.help = "Options:\n"
 		"   --fetch-remote-resources      - Download remote content referenced by data stream.\n"
-		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
+		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --profile <id>                - Show info of the profile with the given ID.\n"
 		"   --profiles                    - Show profiles from the input file in the <id>:<title> format, one line per profile.\n",
     .opt_parser = getopt_info,
@@ -532,7 +532,7 @@ static int app_info_sds(struct oscap_source *source, const struct oscap_action *
 		return OSCAP_ERROR;
 	}
 
-	ds_sds_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	ds_sds_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 
 	/* get collection */
 	struct ds_sds_index *sds = ds_sds_session_get_sds_idx(session);
@@ -763,7 +763,7 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action)
 	/* Command-options */
 	const struct option long_options[] = {
 		{"fetch-remote-resources", no_argument, &action->remote_resources, 1},
-		{"use-local-file", no_argument, &action->use_local_file, 1},
+		{"local-files", required_argument, NULL, 'l'},
 		{"profile", required_argument, 0, 'p'},
 		{"profiles", no_argument, 0, 'n'},
 		// end
@@ -781,6 +781,9 @@ bool getopt_info(int argc, char **argv, struct oscap_action *action)
 				action->show_profiles_only = 1;
 				action->provide_machine_readable_output = 1;
 				break;
+			case 'l':
+				action->local_files = optarg;
+				break;
 			default: return oscap_module_usage(action->module, stderr, NULL);
 		}
 	}
diff --git a/utils/oscap-oval.c b/utils/oscap-oval.c
index 582a5695e3..3a206bb3c4 100644
--- a/utils/oscap-oval.c
+++ b/utils/oscap-oval.c
@@ -116,7 +116,7 @@ static struct oscap_module OVAL_EVAL = {
 	"                                   (only applicable for source data streams)\n"
 	"   --fetch-remote-resources      - Download remote content referenced by OVAL Definitions.\n"
 	"                                   (only applicable for source data streams)\n"
-	"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n",
+	"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n",
     .opt_parser = getopt_oval_eval,
     .func = app_evaluate_oval
 };
@@ -345,7 +345,7 @@ int app_evaluate_oval(const struct oscap_action *action)
 	/* set OVAL Variables */
 	oval_session_set_variables(session, action->f_variables);
 
-	oval_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	oval_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 	/* load all necesary OVAL Definitions and bind OVAL Variables if provided */
 	if ((oval_session_load(session)) != 0)
 		goto cleanup;
@@ -500,7 +500,8 @@ enum oval_opt {
     OVAL_OPT_DIRECTIVES,
     OVAL_OPT_DATASTREAM_ID,
     OVAL_OPT_OVAL_ID,
-	OVAL_OPT_OUTPUT = 'o'
+	OVAL_OPT_OUTPUT = 'o',
+	OVAL_OPT_LOCAL_FILES
 };
 
 #if defined(OVAL_PROBES_ENABLED)
@@ -521,7 +522,7 @@ bool getopt_oval_eval(int argc, char **argv, struct oscap_action *action)
 		{ "skip-valid",	no_argument, &action->validate, 0 },
 		{ "skip-validation",	no_argument, &action->validate, 0 },
 		{ "fetch-remote-resources", no_argument, &action->remote_resources, 1},
-		{ "use-local-file", no_argument, &action->use_local_file, 1},
+		{ "local-files", required_argument, NULL, OVAL_OPT_LOCAL_FILES},
 		{ 0, 0, 0, 0 }
 	};
 
@@ -535,6 +536,9 @@ bool getopt_oval_eval(int argc, char **argv, struct oscap_action *action)
 		case OVAL_OPT_DIRECTIVES: action->f_directives = optarg; break;
 		case OVAL_OPT_DATASTREAM_ID: action->f_datastream_id = optarg;	break;
 		case OVAL_OPT_OVAL_ID: action->f_oval_id = optarg;	break;
+		case OVAL_OPT_LOCAL_FILES:
+			action->local_files = optarg;
+			break;
 		case 0: break;
 		default: return oscap_module_usage(action->module, stderr, NULL);
 		}
diff --git a/utils/oscap-tool.h b/utils/oscap-tool.h
index a3b8781b27..c0596f3e60 100644
--- a/utils/oscap-tool.h
+++ b/utils/oscap-tool.h
@@ -174,7 +174,7 @@ struct oscap_action {
         int list_dynamic;
 	char *verbosity_level;
 	char *fix_type;
-	int use_local_file;
+	char *local_files;
 };
 
 int app_xslt(const char *infile, const char *xsltfile, const char *outfile, const char **params);
diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c
index ef2768bdef..54b8df5467 100644
--- a/utils/oscap-xccdf.c
+++ b/utils/oscap-xccdf.c
@@ -129,7 +129,7 @@ static struct oscap_module XCCDF_EXPORT_OVAL_VARIABLES = {
 		"   --skip-valid                  - Skip validation.\n"
 		"   --skip-validation\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
-		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
+		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --datastream-id <id>          - ID of the data stream in the collection to use.\n"
 		"                                   (only applicable for source data streams)\n"
 		"   --xccdf-id <id>               - ID of component-ref with XCCDF in the data stream that should be evaluated.\n"
@@ -172,7 +172,7 @@ static struct oscap_module XCCDF_EVAL = {
 		"                                   (only applicable for source data streams)\n"
 		"   --enforce-signature           - Process only signed data streams.\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
-		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
+		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --progress                    - Switch to sparse output suitable for progress reporting.\n"
 		"                                   Format is \"$rule_id:$result\\n\".\n"
 		"   --datastream-id <id>          - ID of the data stream in the collection to use.\n"
@@ -201,7 +201,7 @@ static struct oscap_module XCCDF_REMEDIATE = {
 		"   --cpe <name>                  - Use given CPE dictionary or language (autodetected)\n"
 		"                                   for applicability checks.\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
-		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n"
+		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --results <file>              - Write XCCDF Results into file.\n"
 		"   --results-arf <file>          - Write ARF (result data stream) into file.\n"
 		"   --stig-viewer <file>          - Writes XCCDF results into FILE in a format readable by DISA STIG Viewer\n"
@@ -576,7 +576,7 @@ int app_evaluate_xccdf(const struct oscap_action *action)
 	if (action->tailoring_file != NULL)
 		xccdf_session_set_user_tailoring_file(session, action->tailoring_file);
 	xccdf_session_set_user_tailoring_cid(session, action->tailoring_id);
-	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 	xccdf_session_set_product_cpe(session, OSCAP_PRODUCTNAME);
 	xccdf_session_set_rule(session, action->rule);
@@ -681,7 +681,7 @@ static int app_xccdf_export_oval_variables(const struct oscap_action *action)
 		xccdf_session_set_benchmark_id(session, action->f_benchmark_id);
 	}
 	xccdf_session_set_user_cpe(session, action->cpe);
-	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 	xccdf_session_set_custom_oval_eval_fn(session, resolve_variables_wrapper);
 
@@ -724,7 +724,7 @@ int app_xccdf_remediate(const struct oscap_action *action)
 		goto cleanup;
 	xccdf_session_set_validation(session, action->validate, getenv("OSCAP_FULL_VALIDATION") != NULL);
 	xccdf_session_set_user_cpe(session, action->cpe);
-	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 
 	if (xccdf_session_load(session) != 0)
@@ -940,7 +940,7 @@ int app_generate_fix(const struct oscap_action *action)
 	xccdf_session_set_signature_validation(session, action->validate_signature);
 	xccdf_session_set_signature_enforcement(session, action->enforce_signature);
 	xccdf_session_set_user_cpe(session, action->cpe);
-	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 	xccdf_session_set_custom_oval_files(session, action->f_ovals);
 	xccdf_session_set_user_tailoring_file(session, action->tailoring_file);
 	xccdf_session_set_user_tailoring_cid(session, action->tailoring_id);
@@ -1015,7 +1015,7 @@ int app_generate_guide(const struct oscap_action *action)
 	xccdf_session_set_validation(session, action->validate, getenv("OSCAP_FULL_VALIDATION") != NULL);
 	xccdf_session_set_signature_validation(session, action->validate_signature);
 	xccdf_session_set_signature_enforcement(session, action->enforce_signature);
-	xccdf_session_configure_remote_resources(session, action->remote_resources, action->use_local_file, download_reporting_callback);
+	xccdf_session_configure_remote_resources(session, action->remote_resources, action->local_files, download_reporting_callback);
 	xccdf_session_set_user_tailoring_file(session, action->tailoring_file);
 	xccdf_session_set_user_tailoring_cid(session, action->tailoring_id);
 	if (xccdf_session_is_sds(session)) {
@@ -1128,7 +1128,8 @@ enum oval_opt {
     XCCDF_OPT_CPE_DICT,
     XCCDF_OPT_OUTPUT = 'o',
     XCCDF_OPT_RESULT_ID = 'i',
-	XCCDF_OPT_FIX_TYPE
+	XCCDF_OPT_FIX_TYPE,
+	XCCDF_OPT_LOCAL_FILES
 };
 
 bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
@@ -1160,6 +1161,7 @@ bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
 		{"cpe-dict",	required_argument, NULL, XCCDF_OPT_CPE_DICT}, // DEPRECATED!
 		{"sce-template", 	required_argument, NULL, XCCDF_OPT_SCE_TEMPLATE},
 		{"fix-type", required_argument, NULL, XCCDF_OPT_FIX_TYPE},
+		{"local-files", required_argument, NULL, XCCDF_OPT_LOCAL_FILES},
 	// flags
 		{"force",		no_argument, &action->force, 1},
 		{"oval-results",	no_argument, &action->oval_results, 1},
@@ -1169,7 +1171,6 @@ bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
 		{"skip-signature-validation", no_argument, &action->validate_signature, 0},
 		{"enforce-signature", no_argument, &action->enforce_signature, 1},
 		{"fetch-remote-resources", no_argument, &action->remote_resources, 1},
-		{"use-local-file", no_argument, &action->use_local_file, 1},
 		{"progress", no_argument, &action->progress, 1},
 		{"remediate", no_argument, &action->remediate, 1},
 		{"hide-profile-info",	no_argument, &action->hide_profile_info, 1},
@@ -1215,6 +1216,9 @@ bool getopt_xccdf(int argc, char **argv, struct oscap_action *action)
 		case XCCDF_OPT_FIX_TYPE:
 			action->fix_type = optarg;
 			break;
+		case XCCDF_OPT_LOCAL_FILES:
+			action->local_files = optarg;
+			break;
 		case 0: break;
 		default: return oscap_module_usage(action->module, stderr, NULL);
 		}
diff --git a/utils/oscap.8 b/utils/oscap.8
index 8dcb9ca330..4f5f6259e3 100644
--- a/utils/oscap.8
+++ b/utils/oscap.8
@@ -72,9 +72,9 @@ For XCCDF or SCAP source data stream files, the info module prints out IDs of in
 Allow download of remote components referenced from data stream.
 .RE
 .TP
-\fB\-\-use-local-file\fR
+\fB\-\-local-files DIRECTORY\fR
 .RS
-Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+Instead of downloading remote data stream components from the network, use data stream components stored locally as files in the given directory. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 .TP
 \fB\-\-profile PROFILE\fR
@@ -206,9 +206,9 @@ Process only digitally signed SCAP source data streams. Data streams without a s
 Allow download of remote OVAL content referenced from XCCDF by check-content-ref/@href.
 .RE
 .TP
-\fB\-\-use-local-file\fR
+\fB\-\-local-files DIRECTORY\fR
 .RS
-Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+Instead of downloading remote data stream components from the network, use data stream components stored locally as files in the given directory. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 .TP
 \fB\-\-remediate\fR
@@ -236,9 +236,9 @@ Do not validate input/output files.
 Allow download of remote OVAL content referenced from XCCDF by check-content-ref/@href.
 .RE
 .TP
-\fB\-\-use-local-file\fR
+\fB\-\-local-files DIRECTORY\fR
 .RS
-Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+Instead of downloading remote data stream components from the network, use data stream components stored locally as files in the given directory. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 .TP
 \fB\-\-cpe CPE_FILE\fR
@@ -318,7 +318,7 @@ Select a particular profile from XCCDF document.
 Allow download of remote OVAL content referenced from XCCDF by check-content-ref/@href.
 .RE
 .TP
-\fB\-\-use-local-file\fR
+\fB\-\-local-files DIRECTORY\fR
 .RS
 Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
@@ -498,7 +498,7 @@ Do not validate input/output files.
 \fB\-\-fetch-remote-resources\fR
 Allow download of remote components referenced from data stream.
 .TP
-\fB\-\-use-local-file\fR
+\fB\-\-local-files DIRECTORY\fR
 Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 
@@ -662,7 +662,7 @@ Do not validate input/output files.
 \fB\-\-fetch-remote-resources\fR
 Allow download of remote components referenced from data stream.
 .TP
-\fB\-\-use-local-file\fR
+\fB\-\-local-files DIRECTORY\fR
 Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 .TP

From 6732eb94fb6f4606be388e27c347a882917dfb3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Fri, 23 Jul 2021 12:58:33 +0200
Subject: [PATCH 04/10] Show a warning

When --local-files is used but the file which is expected to be
loaded instead of the remote data stream component doesn't exist
we will print a warning so that the user is informed and he would
know what file is expected.

Also added a simple test to test the aforementioned warning scenario.
---
 src/DS/sds.c                                  |  7 ++++-
 .../DS/test_ds_use_local_remote_resources.sh  | 26 ++++++++++++++++++-
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/src/DS/sds.c b/src/DS/sds.c
index 365ae96987..a6e7b27ac9 100644
--- a/src/DS/sds.c
+++ b/src/DS/sds.c
@@ -451,7 +451,12 @@ static int ds_sds_dump_component_by_href(struct ds_sds_session *session, char* x
 					free(local_filepath);
 					return 0;
 				} else {
-					dW("Can't use local file '%s' instead of '%s'", local_filepath, xlink_href);
+					ds_sds_session_remote_resources_progress(session)(true,
+						"WARNING: Data stream component '%s' points out to the remote '%s'. " \
+						"The option --local-files '%s' has been provided, but the file '%s' can't be used locally: %s.\n",
+						cref_id, url, local_files, local_filepath, strerror(errno));
+					free(local_filepath);
+					return -2;
 				}
 				free(local_filepath);
 			}
diff --git a/tests/DS/test_ds_use_local_remote_resources.sh b/tests/DS/test_ds_use_local_remote_resources.sh
index e4a1f0eedd..789dc8326c 100755
--- a/tests/DS/test_ds_use_local_remote_resources.sh
+++ b/tests/DS/test_ds_use_local_remote_resources.sh
@@ -9,6 +9,7 @@ set -x
 . $builddir/tests/test_common.sh
 	
 PROFILE="xccdf_com.example.www_profile_test_remote_res"
+
 result=$(mktemp)
 stderr=$(mktemp)
 tmpdir1=$(mktemp -d)
@@ -55,4 +56,27 @@ assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass2"]/r
 
 popd
 rm -f "$result" "$stderr"
-rm -rf "$tmpdir"
\ No newline at end of file
+rm -rf "$tmpdir"
+
+# test that a warning is shown when --local-files is provided but the file doesn't exist
+result=$(mktemp)
+stderr=$(mktemp)
+tmpdir1=$(mktemp -d)
+tmpdir2=$(mktemp -d)
+tmpdir3=$(mktemp -d)
+cp "${srcdir}/ds_use_local_remote_resources/remote_content_1.3.ds.xml" "$tmpdir2"
+pushd "$tmpdir1"
+
+# $tmpdir3 is empty, it doesn't contain any content
+$OSCAP xccdf eval --local-files "$tmpdir3" --profile "$PROFILE" --results "$result" "$tmpdir2/remote_content_1.3.ds.xml" 2>"$stderr" || ret=$?
+[ "$ret" = 2 ]
+
+grep -q "WARNING: Data stream component 'scap_org.open-scap_cref_remote.oval.xml' points out to the remote 'https://www.example.com/security/data/oval/remote.oval.xml'. The option --local-files '$tmpdir3' has been provided, but the file '$tmpdir3/remote.oval.xml' can't be used locally: No such file or directory." "$stderr"
+
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass"]/result[text()="pass"]'
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-remote_res"]/result[text()="notchecked"]'
+assert_exists 1 '//rule-result[@idref="xccdf_com.example.www_rule_test-pass2"]/result[text()="pass"]'
+
+popd
+rm -f "$result" "$stderr"
+rm -rf "$tmpdir1" "$tmpdir2" "$tmpdir3"

From 34ebf46a84a9bd4d9727739c514faaf0e41858ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Fri, 23 Jul 2021 17:07:44 +0200
Subject: [PATCH 05/10] Update bash completion

with --local-files
---
 dist/bash_completion.d/oscap | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/dist/bash_completion.d/oscap b/dist/bash_completion.d/oscap
index 030c250ebe..329f0736e3 100644
--- a/dist/bash_completion.d/oscap
+++ b/dist/bash_completion.d/oscap
@@ -27,14 +27,14 @@ function _oscap {
     local -A opts=()
 	opts[oscap]="--version --quiet --help -V -q -h"
     opts[oscap:oval:validate]="--version --definitions --variables --syschar --results --directives --skip-schematron"
-    opts[oscap:oval:eval]="--datastream-id --oval-id --id --variables --directives --without-syschar --results --report --skip-valid --skip-validation --fetch-remote-resources --verbose --verbose-log-file"
+    opts[oscap:oval:eval]="--datastream-id --oval-id --id --variables --directives --without-syschar --results --report --skip-valid --skip-validation --fetch-remote-resources --local-files --verbose --verbose-log-file"
     opts[oscap:oval:analyse]="--variables --directives --verbose --verbose-log-file --skip-valid --skip-validation"
     opts[oscap:oval:collect]="--id --syschar --skip-valid --skip-validation --variables --verbose --verbose-log-file"
     opts[oscap:oval:generate:report]="-o --output"
-    opts[oscap:xccdf:eval]="--benchmark-id --check-engine-results --cpe --datastream-id --enforce-signature --export-variables --fetch-remote-resources --oval-results --profile --progress --remediate --report --results --results-arf --rule --skip-valid --skip-validation --skip-signature-validation --stig-viewer --tailoring-file --tailoring-id --thin-results --verbose --verbose-log-file --without-syschar --xccdf-id"
+    opts[oscap:xccdf:eval]="--benchmark-id --check-engine-results --cpe --datastream-id --enforce-signature --export-variables --fetch-remote-resources --local-files --oval-results --profile --progress --remediate --report --results --results-arf --rule --skip-valid --skip-validation --skip-signature-validation --stig-viewer --tailoring-file --tailoring-id --thin-results --verbose --verbose-log-file --without-syschar --xccdf-id"
     opts[oscap:xccdf:validate]="--skip-schematron"
-    opts[oscap:xccdf:export-oval-variables]="--datastream-id --xccdf-id --profile --skip-valid --skip-validation --fetch-remote-resources --cpe"
-    opts[oscap:xccdf:remediate]="--result-id --skip-valid --skip-validation --fetch-remote-resources --results --results-arf --report --oval-results --export-variables --cpe --check-engine-results --progress"
+    opts[oscap:xccdf:export-oval-variables]="--datastream-id --xccdf-id --profile --skip-valid --skip-validation --fetch-remote-resources --local-files --cpe"
+    opts[oscap:xccdf:remediate]="--result-id --skip-valid --skip-validation --fetch-remote-resources --local-files --results --results-arf --report --oval-results --export-variables --cpe --check-engine-results --progress"
     opts[oscap:xccdf:resolve]="-o --output -f --force"
     opts[oscap:xccdf:generate]="--profile"
     opts[oscap:xccdf:generate:report]="-o --output --result-id --profile --oval-template --sce-template"
@@ -43,12 +43,12 @@ function _oscap {
     opts[oscap:xccdf:generate:custom]="-o --output --stylesheet"
     opts[oscap:ds:sds-add]="--datastream-id --skip-valid --skip-validation"
     opts[oscap:ds:sds-compose]="--skip-valid --skip-validation"
-    opts[oscap:ds:sds-split]="--datastream-id --xccdf-id --skip-valid --skip-validation --fetch-remote-resources"
+    opts[oscap:ds:sds-split]="--datastream-id --xccdf-id --skip-valid --skip-validation --fetch-remote-resources --local-files"
     opts[oscap:ds:rds-create]="--skip-valid --skip-validation"
     opts[oscap:ds:rds-split]="--report-id --skip-valid --skip-validation"
     opts[oscap:cvss:score]=""
     opts[oscap:cvss:describe]=""
-    opts[oscap:info]="--fetch-remote-resources --profile --profiles"
+    opts[oscap:info]="--fetch-remote-resources --local-files --profile --profiles"
 
     # local variables
 	local std cmd i prev

From 66ae271966405596b156bca13e6ca41dc118564b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Fri, 23 Jul 2021 17:08:17 +0200
Subject: [PATCH 06/10] Support local remote files in wrappers

This adds --local-files to oscap-chroot, oscap-ssh and oscap-vm.

In oscap-ssh we need to copy the files to the remote machine, which
is a similar thing that we do with tailoring and other files.
---
 utils/oscap-chroot   |  1 +
 utils/oscap-chroot.8 |  1 +
 utils/oscap-ssh      | 17 +++++++++++++++++
 utils/oscap-ssh.8    |  1 +
 utils/oscap-vm       |  1 +
 utils/oscap-vm.8     |  1 +
 6 files changed, 22 insertions(+)

diff --git a/utils/oscap-chroot b/utils/oscap-chroot
index c1e35aa652..57ec66c428 100755
--- a/utils/oscap-chroot
+++ b/utils/oscap-chroot
@@ -53,6 +53,7 @@ function usage()
     echo "  --skip-valid"
     echo "  --skip-validation"
     echo "  --fetch-remote-resources"
+    echo "  --local-files"
     echo "  --progress"
     echo "  --datastream-id"
     echo "  --xccdf-id"
diff --git a/utils/oscap-chroot.8 b/utils/oscap-chroot.8
index d0c75c68e3..e02dd8dbea 100644
--- a/utils/oscap-chroot.8
+++ b/utils/oscap-chroot.8
@@ -23,6 +23,7 @@ supported oscap xccdf eval options are:
   --skip-valid
   --skip-validation
   --fetch-remote-resources
+  --local-files
   --progress
   --datastream-id
   --xccdf-id
diff --git a/utils/oscap-ssh b/utils/oscap-ssh
index f428f99a8e..120d4ae8c6 100755
--- a/utils/oscap-ssh
+++ b/utils/oscap-ssh
@@ -54,6 +54,7 @@ function usage()
     echo "  --skip-valid"
     echo "  --skip-validation"
     echo "  --fetch-remote-resources"
+    echo "  --local-files"
     echo "  --progress"
     echo "  --datastream-id"
     echo "  --xccdf-id"
@@ -110,6 +111,12 @@ function scp_copy_to_temp_dir {
     scp -o ControlPath="$MASTER_SOCKET" -P "$SSH_PORT" $SSH_ADDITIONAL_OPTIONS "$1" "$SSH_HOST:$REMOTE_TEMP_DIR/$2"
 }
 
+# $1: Local directory name to copy
+# $2: Remote destination
+function scp_copy_dir_to_temp_dir {
+    scp -r -o ControlPath="$MASTER_SOCKET" -P "$SSH_PORT" $SSH_ADDITIONAL_OPTIONS "$1" "$SSH_HOST:$REMOTE_TEMP_DIR/$2"
+}
+
 # $1: Remote filename to get
 # $2: Local destination
 function scp_retreive_from_temp_dir {
@@ -196,6 +203,7 @@ oscap_args=("$@")
 
 LOCAL_CONTENT_PATH=""
 LOCAL_TAILORING_PATH=""
+LOCAL_LOCAL_FILES_PATH=""
 LOCAL_CPE_PATH=""
 LOCAL_VARIABLES_PATH=""
 LOCAL_DIRECTIVES_PATH=""
@@ -214,6 +222,10 @@ for i in $(seq 0 `expr $# - 1`); do
         LOCAL_TAILORING_PATH=${oscap_args[j]}
         oscap_args[j]="$REMOTE_TEMP_DIR/tailoring.xml"
       ;;
+    ("--local-files")
+        LOCAL_LOCAL_FILES_PATH=${oscap_args[j]}
+        oscap_args[j]="$REMOTE_TEMP_DIR/local_files"
+      ;;
     ("--cpe")
         LOCAL_CPE_PATH=${oscap_args[j]}
         oscap_args[j]="$REMOTE_TEMP_DIR/cpe.xml"
@@ -258,6 +270,7 @@ fi
 
 [ "$LOCAL_CONTENT_PATH" == "" ] || [ -f "$LOCAL_CONTENT_PATH" ] || die "Expected the last argument to be an input file, '$LOCAL_CONTENT_PATH' isn't a valid file path or the file doesn't exist!"
 [ "$LOCAL_TAILORING_PATH" == "" ] || [ -f "$LOCAL_TAILORING_PATH" ] || die "Tailoring file path '$LOCAL_TAILORING_PATH' isn't a valid file path or the file doesn't exist!"
+[ "$LOCAL_LOCAL_FILES_PATH" == "" ] || [ -d "$LOCAL_LOCAL_FILES_PATH" ] || die "Directory '$LOCAL_LOCAL_FILES_PATH' isn't a valid directory path or the directory doesn't exist!"
 [ "$LOCAL_CPE_PATH" == "" ] || [ -f "$LOCAL_CPE_PATH" ] || die "CPE file path '$LOCAL_CPE_PATH' isn't a valid file path or the file doesn't exist!"
 [ "$LOCAL_VARIABLES_PATH" == "" ] || [ -f "$LOCAL_VARIABLES_PATH" ] || die "OVAL variables file path '$LOCAL_VARIABLES_PATH' isn't a valid file path or the file doesn't exist!"
 [ "$LOCAL_DIRECTIVES_PATH" == "" ] || [ -f "$LOCAL_DIRECTIVES_PATH" ] || die "OVAL directives file path '$LOCAL_DIRECTIVES_PATH' isn't a valid file path or the file doesn't exist!"
@@ -270,6 +283,10 @@ if [ "$LOCAL_TAILORING_PATH" != "" ]; then
     echo "Copying tailoring file '$LOCAL_TAILORING_PATH' to remote working directory '$REMOTE_TEMP_DIR'..."
     scp_copy_to_temp_dir "$LOCAL_TAILORING_PATH" tailoring.xml || die "Failed to copy tailoring file to remote temporary directory!"
 fi
+if [ "$LOCAL_LOCAL_FILES_PATH" != "" ]; then
+    echo "Copying directory '$LOCAL_LOCAL_FILES_PATH' to remote working directory '$REMOTE_TEMP_DIR'..."
+    scp_copy_dir_to_temp_dir "$LOCAL_LOCAL_FILES_PATH" local_files || die "Failed to copy directory $LOCAL_LOCAL_FILES_PATH to remote temporary directory!"
+fi
 if [ "$LOCAL_CPE_PATH" != "" ]; then
     echo "Copying CPE file '$LOCAL_CPE_PATH' to remote working directory '$REMOTE_TEMP_DIR'..."
     scp_copy_to_temp_dir "$LOCAL_CPE_PATH" cpe.xml || die "Failed to copy CPE file to remote temporary directory!"
diff --git a/utils/oscap-ssh.8 b/utils/oscap-ssh.8
index f64855829a..416b1f3e58 100644
--- a/utils/oscap-ssh.8
+++ b/utils/oscap-ssh.8
@@ -29,6 +29,7 @@ Supported options are:
   --skip-valid
   --skip-validation
   --fetch-remote-resources
+  --local-files
   --progress
   --datastream-id
   --xccdf-id
diff --git a/utils/oscap-vm b/utils/oscap-vm
index 1a9d6b3bf6..e7ae6e2600 100755
--- a/utils/oscap-vm
+++ b/utils/oscap-vm
@@ -51,6 +51,7 @@ function usage()
     echo "  --skip-valid"
     echo "  --skip-validation"
     echo "  --fetch-remote-resources"
+    echo "  --local-files"
     echo "  --progress"
     echo "  --datastream-id"
     echo "  --xccdf-id"
diff --git a/utils/oscap-vm.8 b/utils/oscap-vm.8
index a335725e5e..b7276c1128 100644
--- a/utils/oscap-vm.8
+++ b/utils/oscap-vm.8
@@ -65,6 +65,7 @@ Supported oscap xccdf eval options are:
   \-\-skip-valid
   \-\-skip-validation
   \-\-fetch-remote-resources
+  \-\-local-files
   \-\-progress
   \-\-datastream-id <id>
   \-\-xccdf-id <id>

From cac289e1d15cd8f3bde64b94563ed1dcf34652bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Fri, 23 Jul 2021 17:18:55 +0200
Subject: [PATCH 07/10] Fix the value to use a correct type

---
 src/DS/ds_sds_session.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/DS/ds_sds_session.c b/src/DS/ds_sds_session.c
index 99f4bc20fd..3a720e8131 100644
--- a/src/DS/ds_sds_session.c
+++ b/src/DS/ds_sds_session.c
@@ -347,7 +347,7 @@ void ds_sds_session_configure_remote_resources(struct ds_sds_session *session, b
 
 void ds_sds_session_set_remote_resources(struct ds_sds_session *session, bool allowed, download_progress_calllback_t callback)
 {
-	ds_sds_session_configure_remote_resources(session, allowed, false, callback);
+	ds_sds_session_configure_remote_resources(session, allowed, NULL, callback);
 }
 
 const char *ds_sds_session_local_files(struct ds_sds_session *session)

From e4e6732ffd23b408c0bf08eea91c406434bd4ec6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Fri, 23 Jul 2021 17:36:36 +0200
Subject: [PATCH 08/10] Refactor: Extract function _handle_disabled_downloads

---
 src/DS/sds.c | 122 ++++++++++++++++++++++++++-------------------------
 1 file changed, 63 insertions(+), 59 deletions(-)

diff --git a/src/DS/sds.c b/src/DS/sds.c
index a6e7b27ac9..530e3ad9fa 100644
--- a/src/DS/sds.c
+++ b/src/DS/sds.c
@@ -371,6 +371,66 @@ static char *compose_target_filename_dirname(const char *relative_filepath, cons
 	return target_filename_dirname;
 }
 
+static int _handle_disabled_downloads(struct ds_sds_session *session, const char *relative_filepath, const char *xlink_href, const char *component_id, const char *target_filename_dirname, const char *cref_id, const char *url)
+{
+	/*
+	 * If fetching remote resources isn't allowed by the user let's take a look
+	 * whether there exists a file whose file name is equal to @name attribute
+	 * of the uri element within the catalog of the previously processed
+	 * component-ref which pointed us to the currently processed component-ref.
+	 * Note that the @name attribute value has been passed as relative_filepath
+	 * in the recursive call of ds_sds_dump_component_ref_as. If such file
+	 * exists, we will assume that it's a local copy of the remote component
+	 * located at the URL defined in @xlink:href. This way people can provide
+	 * the previously downloaded component which might be useful on systems with
+	 * limited internet access. This behavior is allowed only when --local-files
+	 * is used on the command line.
+	 * See: https://bugzilla.redhat.com/show_bug.cgi?id=1970527
+	 * See: https://access.redhat.com/solutions/5185891
+	 */
+	const char *local_files = ds_sds_session_local_files(session);
+	if (local_files == NULL) {
+		static bool fetch_remote_resources_suggested = false;
+		if (!fetch_remote_resources_suggested) {
+			fetch_remote_resources_suggested = true;
+			ds_sds_session_remote_resources_progress(session)(true,
+				"WARNING: Datastream component '%s' points out to the remote '%s'. Use '--fetch-remote-resources' option to download it.\n",
+				cref_id, url);
+		}
+
+		ds_sds_session_remote_resources_progress(session)(true,
+			"WARNING: Skipping '%s' file which is referenced from datastream\n",
+			url);
+		// -2 means that remote resources were not downloaded
+		return -2;
+	}
+	char *local_filepath = oscap_path_join(local_files, relative_filepath);
+	struct stat sb;
+	if (stat(local_filepath, &sb) == 0) {
+		dI("Using local file '%s' instead of '%s'", local_filepath, xlink_href);
+		struct oscap_source *source_file = oscap_source_new_from_file(local_filepath);
+		xmlDoc *doc = oscap_source_get_xmlDoc(source_file);
+		if (doc == NULL) {
+			free(local_filepath);
+			return -1;
+		}
+		xmlNodePtr inner_root = ds_sds_get_component_root_by_id(doc, component_id);
+
+		if (ds_sds_register_component(session, doc, inner_root, component_id, target_filename_dirname, relative_filepath) != 0) {
+			free(local_filepath);
+			return -1;
+		}
+		free(local_filepath);
+		return 0;
+	}
+	ds_sds_session_remote_resources_progress(session)(true,
+		"WARNING: Data stream component '%s' points out to the remote '%s'. " \
+		"The option --local-files '%s' has been provided, but the file '%s' can't be used locally: %s.\n",
+		cref_id, url, local_files, local_filepath, strerror(errno));
+	free(local_filepath);
+	return -2;
+}
+
 static int ds_sds_dump_component_by_href(struct ds_sds_session *session, char* xlink_href, char *target_filename_dirname, const char* relative_filepath, char* cref_id, char **component_id)
 {
 	if (!xlink_href || strlen(xlink_href) < 2)
@@ -413,65 +473,9 @@ static int ds_sds_dump_component_by_href(struct ds_sds_session *session, char* x
 		}
 
 		if (!ds_sds_session_fetch_remote_resources(session)) {
-			/*
-			 * If fetching remote resources isn't allowed by the user let's take
-			 * a look whether there exists a file whose file name is equal to
-			 * @name attribute of the uri element within the catalog of the
-			 * previously processed component-ref which pointed us to the
-			 * currently processed component-ref. Note that the @name attribute
-			 * value has been passed as relative_filepath in the recursive call
-			 * of ds_sds_dump_component_ref_as. If such file exists, we will
-			 * assume that it's a local copy of the remote component located at
-			 * the URL defined in @xlink:href. This way people can provide the
-			 * previously downloaded component which might be useful on systems
-			 * with limited internet access. This behavior is allowed only when
-			 * --local-files is used on the command line.
-			 * See: https://bugzilla.redhat.com/show_bug.cgi?id=1970527
-			 * See: https://access.redhat.com/solutions/5185891
-			 */
-			const char *local_files = ds_sds_session_local_files(session);
-			if (local_files != NULL) {
-				char *local_filepath = oscap_path_join(local_files, relative_filepath);
-				struct stat sb;
-				if (stat(local_filepath, &sb) == 0) {
-				//if (ds_sds_session_can_use_local_file(session)) {
-					dI("Using local file '%s' instead of '%s'", local_filepath, xlink_href);
-					struct oscap_source *source_file = oscap_source_new_from_file(local_filepath);
-					xmlDoc *doc = oscap_source_get_xmlDoc(source_file);
-					if (doc == NULL) {
-						free(local_filepath);
-						return -1;
-					}
-					xmlNodePtr inner_root = ds_sds_get_component_root_by_id(doc, *component_id);
-
-					if (ds_sds_register_component(session, doc, inner_root, *component_id, target_filename_dirname, relative_filepath) != 0) {
-						free(local_filepath);
-						return -1;
-					}
-					free(local_filepath);
-					return 0;
-				} else {
-					ds_sds_session_remote_resources_progress(session)(true,
-						"WARNING: Data stream component '%s' points out to the remote '%s'. " \
-						"The option --local-files '%s' has been provided, but the file '%s' can't be used locally: %s.\n",
-						cref_id, url, local_files, local_filepath, strerror(errno));
-					free(local_filepath);
-					return -2;
-				}
-				free(local_filepath);
-			}
-
-			static bool fetch_remote_resources_suggested = false;
-
-			if (!fetch_remote_resources_suggested) {
-				fetch_remote_resources_suggested = true;
-				ds_sds_session_remote_resources_progress(session)(true, "WARNING: Datastream component '%s' points out to the remote '%s'. "
-									"Use '--fetch-remote-resources' option to download it.\n", cref_id, url);
-			}
-
-			ds_sds_session_remote_resources_progress(session)(true, "WARNING: Skipping '%s' file which is referenced from datastream\n", url);
-			// -2 means that remote resources were not downloaded
-			return -2;
+			return _handle_disabled_downloads(
+				session, relative_filepath, xlink_href, *component_id,
+				target_filename_dirname, cref_id, url);
 		}
 
 		return ds_dsd_dump_remote_component(url, *component_id, session, target_filename_dirname, relative_filepath);

From 9cb39894484e6bdccc235e4138af6447ffc5ee4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Mon, 26 Jul 2021 16:03:18 +0200
Subject: [PATCH 09/10] Fix indentation and typos

---
 utils/oscap-ds.c    | 2 +-
 utils/oscap-info.c  | 2 +-
 utils/oscap-oval.c  | 2 +-
 utils/oscap-xccdf.c | 6 +++---
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/utils/oscap-ds.c b/utils/oscap-ds.c
index 8207f097ec..5f9863414b 100644
--- a/utils/oscap-ds.c
+++ b/utils/oscap-ds.c
@@ -83,7 +83,7 @@ static struct oscap_module DS_SDS_SPLIT_MODULE = {
 		"   --skip-valid                  - Skips validating of given XCCDF.\n"
 		"   --skip-validation\n"
 		"   --fetch-remote-resources      - Download remote content referenced by data stream.\n"
-		"   --use-local-file              - Use a locally downloaded copy of the remote resource if it exists.\n",
+		"   --local-files <dir>           - Use locally downloaded copies of remote resources stored in the given directory.\n",
 	.opt_parser = getopt_ds,
 	.func = app_ds_sds_split
 };
diff --git a/utils/oscap-info.c b/utils/oscap-info.c
index 2e6f5003aa..721596a528 100644
--- a/utils/oscap-info.c
+++ b/utils/oscap-info.c
@@ -63,7 +63,7 @@ struct oscap_module OSCAP_INFO_MODULE = {
     .usage = "some-file.xml",
 	.help = "Options:\n"
 		"   --fetch-remote-resources      - Download remote content referenced by data stream.\n"
-		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
+		"   --local-files <dir>           - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --profile <id>                - Show info of the profile with the given ID.\n"
 		"   --profiles                    - Show profiles from the input file in the <id>:<title> format, one line per profile.\n",
     .opt_parser = getopt_info,
diff --git a/utils/oscap-oval.c b/utils/oscap-oval.c
index 3a206bb3c4..da1b1aad86 100644
--- a/utils/oscap-oval.c
+++ b/utils/oscap-oval.c
@@ -116,7 +116,7 @@ static struct oscap_module OVAL_EVAL = {
 	"                                   (only applicable for source data streams)\n"
 	"   --fetch-remote-resources      - Download remote content referenced by OVAL Definitions.\n"
 	"                                   (only applicable for source data streams)\n"
-	"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n",
+	"   --local-files <dir>           - Use locally downloaded copies of remote resources stored in the given directory.\n",
     .opt_parser = getopt_oval_eval,
     .func = app_evaluate_oval
 };
diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c
index 54b8df5467..a7870c4281 100644
--- a/utils/oscap-xccdf.c
+++ b/utils/oscap-xccdf.c
@@ -129,7 +129,7 @@ static struct oscap_module XCCDF_EXPORT_OVAL_VARIABLES = {
 		"   --skip-valid                  - Skip validation.\n"
 		"   --skip-validation\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
-		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
+		"   --local-files <dir>           - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --datastream-id <id>          - ID of the data stream in the collection to use.\n"
 		"                                   (only applicable for source data streams)\n"
 		"   --xccdf-id <id>               - ID of component-ref with XCCDF in the data stream that should be evaluated.\n"
@@ -172,7 +172,7 @@ static struct oscap_module XCCDF_EVAL = {
 		"                                   (only applicable for source data streams)\n"
 		"   --enforce-signature           - Process only signed data streams.\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
-		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
+		"   --local-files <dir>           - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --progress                    - Switch to sparse output suitable for progress reporting.\n"
 		"                                   Format is \"$rule_id:$result\\n\".\n"
 		"   --datastream-id <id>          - ID of the data stream in the collection to use.\n"
@@ -201,7 +201,7 @@ static struct oscap_module XCCDF_REMEDIATE = {
 		"   --cpe <name>                  - Use given CPE dictionary or language (autodetected)\n"
 		"                                   for applicability checks.\n"
 		"   --fetch-remote-resources      - Download remote content referenced by XCCDF.\n"
-		"   --local-files <dir>       - Use locally downloaded copies of remote resources stored in the given directory.\n"
+		"   --local-files <dir>           - Use locally downloaded copies of remote resources stored in the given directory.\n"
 		"   --results <file>              - Write XCCDF Results into file.\n"
 		"   --results-arf <file>          - Write ARF (result data stream) into file.\n"
 		"   --stig-viewer <file>          - Writes XCCDF results into FILE in a format readable by DISA STIG Viewer\n"

From 7187e2a1e49927884d80702c2e3e6a796996277b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Mon, 26 Jul 2021 16:19:49 +0200
Subject: [PATCH 10/10] Update description in man page

---
 utils/oscap.8 | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/utils/oscap.8 b/utils/oscap.8
index 4f5f6259e3..dcfa3d0c2f 100644
--- a/utils/oscap.8
+++ b/utils/oscap.8
@@ -320,7 +320,7 @@ Allow download of remote OVAL content referenced from XCCDF by check-content-ref
 .TP
 \fB\-\-local-files DIRECTORY\fR
 .RS
-Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+Instead of downloading remote data stream components from the network, use data stream components stored locally as files in the given directory. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 .TP
 \fB\-\-skip-valid\fR, \fB\-\-skip-validation\fR
@@ -499,7 +499,7 @@ Do not validate input/output files.
 Allow download of remote components referenced from data stream.
 .TP
 \fB\-\-local-files DIRECTORY\fR
-Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+Instead of downloading remote data stream components from the network, use data stream components stored locally as files in the given directory. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 
 .TP
@@ -663,7 +663,7 @@ Do not validate input/output files.
 Allow download of remote components referenced from data stream.
 .TP
 \fB\-\-local-files DIRECTORY\fR
-Instead of downloading remote data stream components from the network, use a data stream component stored locally in a file. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
+Instead of downloading remote data stream components from the network, use data stream components stored locally as files in the given directory. In place of the remote data stream component OpenSCAP will attempt to use a file whose file name is equal to @name attribute of the uri element within the catalog element within the component-ref element in the data stream if such file exists.
 .RE
 .TP
 .B \fBsds-validate\fR SOURCE_DS