Compare commits

...

7 Commits

Author SHA1 Message Date
eabdullin 26b312aec5 Import from AlmaLinux stable repository 2024-05-31 17:59:29 +00:00
CentOS Sources 66ad735aa1 import oscap-anaconda-addon-1.2.1-12.el8 2023-05-16 07:17:27 +00:00
CentOS Sources 8ae345f04d import oscap-anaconda-addon-1.2.1-8.el8 2022-11-08 14:05:44 +00:00
CentOS Sources d41944b92a import oscap-anaconda-addon-1.2.1-5.el8 2022-05-10 07:52:06 +00:00
CentOS Sources ae8c79d536 import oscap-anaconda-addon-1.2.1-4.el8 2021-12-08 12:22:59 +00:00
CentOS Sources 13c62bbafe import oscap-anaconda-addon-1.1.1-7.el8 2021-09-09 23:24:06 +00:00
CentOS Sources ec32b32bd4 import oscap-anaconda-addon-1.1.1-5.el8 2021-09-09 23:24:03 +00:00
19 changed files with 1988 additions and 5907 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/oscap-anaconda-addon-1.0.tar.gz
SOURCES/oscap-anaconda-addon-1.2.1.tar.gz

View File

@ -1 +0,0 @@
6edf7e4859de8e66837404c084405ea4318a319d SOURCES/oscap-anaconda-addon-1.0.tar.gz

View File

@ -1,26 +0,0 @@
From 1e275a0da36595dd921732e0f60510171cdbe75c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Tue, 15 Jan 2019 19:16:44 +0100
Subject: [PATCH] Updated code to comply to the Bootloader proxy API.
---
org_fedora_oscap/rule_handling.py | 4 ++--
tests/test_rule_handling.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/org_fedora_oscap/rule_handling.py b/org_fedora_oscap/rule_handling.py
index 738465f..f3fd057 100644
--- a/org_fedora_oscap/rule_handling.py
+++ b/org_fedora_oscap/rule_handling.py
@@ -716,9 +716,9 @@ def eval_rules(self, ksdata, storage, report_only=False):
bootloader_proxy = STORAGE.get_proxy(BOOTLOADER)
- if self._require_password and not bootloader_proxy.password_is_set:
+ if self._require_password and not bootloader_proxy.IsPasswordSet:
# TODO: Anaconda provides a way to set bootloader password:
- # bootloader_proxy.set_password(...)
+ # bootloader_proxy.SetEncryptedPassword(...)
# We don't support setting the bootloader password yet,
# but we shouldn't stop the installation, just because of that.
return [RuleMessage(self.__class__, common.MESSAGE_TYPE_WARNING,

View File

@ -1,30 +0,0 @@
From fd1684358e212521abaf3ec7662aa97181868c0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Tue, 15 Jan 2019 18:19:28 +0100
Subject: [PATCH] Fixed the checksum function to use forward-compatible rb
mode.
On python3, there is a problem as contents of r-opened file is string,
but they are treated as bytes later. rb mode is fully python2-compatible.
---
org_fedora_oscap/utils.py | 4 ++--
tests/data/file | 1 +
tests/test_utils.py | 12 ++++++++++++
3 files changed, 15 insertions(+), 2 deletions(-)
create mode 100644 tests/data/file
diff --git a/org_fedora_oscap/utils.py b/org_fedora_oscap/utils.py
index 6d5c157..3be8325 100644
--- a/org_fedora_oscap/utils.py
+++ b/org_fedora_oscap/utils.py
@@ -175,8 +175,8 @@ def get_file_fingerprint(fpath, hash_obj):
"""
- with open(fpath, "r") as fobj:
- bsize = 4*1024
+ with open(fpath, "rb") as fobj:
+ bsize = 4 * 1024
# process file as 4 KB blocks
buf = fobj.read(bsize)
while buf:

View File

@ -1,25 +0,0 @@
From 2f97f43a4194263e47d4747e39c22b8287a659b3 Mon Sep 17 00:00:00 2001
From: Radek Vykydal <rvykydal@redhat.com>
Date: Wed, 21 Aug 2019 15:49:21 +0200
Subject: [PATCH] Do not use capitals for spoke title.
Resolves: rhbz#1744185
To be consistent with other spokes.
---
org_fedora_oscap/gui/spokes/oscap.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org_fedora_oscap/gui/spokes/oscap.py b/org_fedora_oscap/gui/spokes/oscap.py
index 96802cf..0c90fb7 100644
--- a/org_fedora_oscap/gui/spokes/oscap.py
+++ b/org_fedora_oscap/gui/spokes/oscap.py
@@ -195,7 +195,7 @@ class OSCAPSpoke(NormalSpoke):
icon = "changes-prevent-symbolic"
# title of the spoke (will be displayed on the hub)
- title = N_("_SECURITY POLICY")
+ title = N_("_Security Policy")
# methods defined by API and helper methods #
def __init__(self, data, storage, payload, instclass):

View File

@ -1,30 +0,0 @@
From ccd4e2f078d00fa4570d2bd56802c726286d1020 Mon Sep 17 00:00:00 2001
From: Martin Kolman <mkolman@redhat.com>
Date: Wed, 10 Oct 2018 17:12:01 +0200
Subject: [PATCH] Set help id for the OSCAP addon provided spoke (#1638068)
The new Anaconda help system now operates on help ids instead of pointing to
individual files.
So drop the old property and replace it with a proper help_id.
Resolves: rhbz#1638068
---
org_fedora_oscap/gui/spokes/oscap.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/org_fedora_oscap/gui/spokes/oscap.py b/org_fedora_oscap/gui/spokes/oscap.py
index d9fe548..36fd656 100644
--- a/org_fedora_oscap/gui/spokes/oscap.py
+++ b/org_fedora_oscap/gui/spokes/oscap.py
@@ -179,8 +179,8 @@ class OSCAPSpoke(NormalSpoke):
# name of the .glade file in the same directory as this source
uiFile = "oscap.glade"
- # name of the file providing help content for this spoke
- helpFile = "SecurityPolicySpoke.xml"
+ # id of the help content for this spoke
+ help_id = "SecurityPolicySpoke"
# domain of oscap-anaconda-addon translations
translationDomain = "oscap-anaconda-addon"

File diff suppressed because it is too large Load Diff

View File

@ -1,256 +0,0 @@
diff --git a/org_fedora_oscap/rule_handling.py b/org_fedora_oscap/rule_handling.py
index f712ac4..738465f 100644
--- a/org_fedora_oscap/rule_handling.py
+++ b/org_fedora_oscap/rule_handling.py
@@ -26,7 +26,13 @@
import optparse
import shlex
import logging
+
from pyanaconda.pwpolicy import F22_PwPolicyData
+from pyanaconda.core.constants import (
+ FIREWALL_ENABLED, FIREWALL_DISABLED, FIREWALL_USE_SYSTEM_DEFAULTS)
+from pyanaconda.modules.common.constants.objects import FIREWALL, BOOTLOADER
+from pyanaconda.modules.common.constants.services import NETWORK, STORAGE, USERS
+
from org_fedora_oscap import common
from org_fedora_oscap.common import OSCAPaddonError, RuleMessage
@@ -496,7 +502,10 @@ def eval_rules(self, ksdata, storage, report_only=False):
return []
ret = []
- if not ksdata.rootpw.password:
+
+ users_proxy = USERS.get_proxy()
+
+ if not users_proxy.IsRootPasswordSet:
# root password was not set
msg = _("make sure to create password with minimal length of %d "
@@ -505,12 +514,12 @@ def eval_rules(self, ksdata, storage, report_only=False):
common.MESSAGE_TYPE_WARNING, msg)]
else:
# root password set
- if ksdata.rootpw.isCrypted:
+ if users_proxy.IsRootPasswordCrypted:
msg = _("cannot check root password length (password is crypted)")
log.warning("cannot check root password length (password is crypted)")
return [RuleMessage(self.__class__,
common.MESSAGE_TYPE_WARNING, msg)]
- elif len(ksdata.rootpw.password) < self._minlen:
+ elif len(users_proxy.RootPassword) < self._minlen:
# too short
msg = _("root password is too short, a longer one with at "
"least %d characters is required") % self._minlen
@@ -705,10 +714,13 @@ def __str__(self):
def eval_rules(self, ksdata, storage, report_only=False):
""":see: RuleHandler.eval_rules"""
- if self._require_password and not storage.bootloader.password:
- # Anaconda doesn't provide a way to set bootloader password, so
- # users cannot do much about that --> we shouldn't stop the
- # installation, should we?
+ bootloader_proxy = STORAGE.get_proxy(BOOTLOADER)
+
+ if self._require_password and not bootloader_proxy.password_is_set:
+ # TODO: Anaconda provides a way to set bootloader password:
+ # bootloader_proxy.set_password(...)
+ # We don't support setting the bootloader password yet,
+ # but we shouldn't stop the installation, just because of that.
return [RuleMessage(self.__class__, common.MESSAGE_TYPE_WARNING,
"boot loader password not set up")]
else:
@@ -802,8 +814,13 @@ def __init__(self):
self._added_trusts = set()
self._removed_svcs = set()
+ self._new_services_to_add = set()
+ self._new_ports_to_add = set()
+ self._new_trusts_to_add = set()
+ self._new_services_to_remove = set()
+
self._firewall_enabled = None
- self._firewall_default_enabled = None
+ self._firewall_default_state = None
def add_services(self, services):
"""
@@ -895,25 +912,26 @@ def __str__(self):
def eval_rules(self, ksdata, storage, report_only=False):
""":see: RuleHandler.eval_rules"""
+ firewall_proxy = NETWORK.get_proxy(FIREWALL)
messages = []
- if self._firewall_default_enabled is None:
+ if self._firewall_default_state is None:
# firewall default startup setting
- self._firewall_default_enabled = ksdata.firewall.enabled
+ self._firewall_default_state = firewall_proxy.FirewallMode
if self._firewall_enabled is False:
msg = _("Firewall will be disabled on startup")
messages.append(RuleMessage(self.__class__,
common.MESSAGE_TYPE_INFO, msg))
if not report_only:
- ksdata.firewall.enabled = self._firewall_enabled
+ firewall_proxy.SetFirewallMode(FIREWALL_DISABLED)
elif self._firewall_enabled is True:
msg = _("Firewall will be enabled on startup")
messages.append(RuleMessage(self.__class__,
common.MESSAGE_TYPE_INFO, msg))
if not report_only:
- ksdata.firewall.enabled = self._firewall_enabled
+ firewall_proxy.SetFirewallMode(FIREWALL_ENABLED)
# add messages for the already added services
for svc in self._added_svcs:
@@ -937,49 +955,58 @@ def eval_rules(self, ksdata, storage, report_only=False):
common.MESSAGE_TYPE_INFO, msg))
# services, that should be added
- services_to_add = (svc for svc in self._add_svcs
- if svc not in ksdata.firewall.services)
+ self._new_services_to_add = {
+ svc for svc in self._add_svcs
+ if svc not in firewall_proxy.EnabledServices}
# ports, that should be added
- ports_to_add = (ports for ports in self._add_ports
- if ports not in ksdata.firewall.ports)
+ self._new_ports_to_add = {
+ ports for ports in self._add_ports
+ if ports not in firewall_proxy.EnabledPorts}
# trusts, that should be added
- trusts_to_add = (trust for trust in self._add_trusts
- if trust not in ksdata.firewall.trusts)
+ self._new_trusts_to_add = {
+ trust for trust in self._add_trusts
+ if trust not in firewall_proxy.Trusts}
- for svc in services_to_add:
+ for svc in self._new_services_to_add:
# add the service unless already added
if not report_only:
self._added_svcs.add(svc)
- ksdata.firewall.services.append(svc)
msg = _("service '%s' has been added to the list of services to be "
"added to the firewall" % svc)
messages.append(RuleMessage(self.__class__,
common.MESSAGE_TYPE_INFO, msg))
+ if not report_only:
+ all_services = list(self._add_svcs.union(set(firewall_proxy.EnabledServices)))
+ firewall_proxy.SetEnabledServices(all_services)
- for port in ports_to_add:
+ for port in self._new_ports_to_add:
# add the port unless already added
if not report_only:
self._added_ports.add(port)
- ksdata.firewall.ports.append(port)
msg = _("port '%s' has been added to the list of ports to be "
"added to the firewall" % port)
messages.append(RuleMessage(self.__class__,
common.MESSAGE_TYPE_INFO, msg))
+ if not report_only:
+ all_ports = list(self._add_ports.union(set(firewall_proxy.EnabledPorts)))
+ firewall_proxy.SetEnabledPorts(all_ports)
- for trust in trusts_to_add:
+ for trust in self._new_trusts_to_add:
# add the trust unless already added
if not report_only:
self._added_trusts.add(trust)
- ksdata.firewall.trusts.append(trust)
msg = _("trust '%s' has been added to the list of trusts to be "
"added to the firewall" % trust)
messages.append(RuleMessage(self.__class__,
common.MESSAGE_TYPE_INFO, msg))
+ if not report_only:
+ all_trusts = list(self._add_trusts.union(set(firewall_proxy.Trusts)))
+ firewall_proxy.SetTrusts(all_trusts)
# now do the same for the services that should be excluded
@@ -990,52 +1017,56 @@ def eval_rules(self, ksdata, storage, report_only=False):
messages.append(RuleMessage(self.__class__,
common.MESSAGE_TYPE_INFO, msg))
- # services, that should be added
- services_to_remove = (svc for svc in self._remove_svcs
- if svc not in ksdata.firewall.remove_services)
+ # services, that should be excluded
+ self._new_services_to_remove = {
+ svc for svc in self._remove_svcs
+ if svc not in firewall_proxy.DisabledServices}
- for svc in services_to_remove:
+ for svc in self._new_services_to_remove:
# exclude the service unless already excluded
if not report_only:
self._removed_svcs.add(svc)
- ksdata.firewall.remove_services.append(svc)
msg = _("service '%s' has been added to the list of services to be "
"removed from the firewall" % svc)
messages.append(RuleMessage(self.__class__,
common.MESSAGE_TYPE_INFO, msg))
+ if not report_only:
+ all_services = list(self._remove_svcs.union(set(firewall_proxy.DisabledServices)))
+ firewall_proxy.SetDisabledServices(all_services)
return messages
def revert_changes(self, ksdata, storage):
""":see: RuleHander.revert_changes"""
+ firewall_proxy = NETWORK.get_proxy(FIREWALL)
if self._firewall_enabled is not None:
- ksdata.firewall.enabled = self._firewall_default_enabled
+ firewall_proxy.SetFirewallMode(self._firewall_default_state)
# remove all services this handler added
- for svc in self._added_svcs:
- if svc in ksdata.firewall.services:
- ksdata.firewall.services.remove(svc)
+ all_services = firewall_proxy.EnabledServices
+ orig_services = set(all_services).difference(self._new_services_to_add)
+ firewall_proxy.SetEnabledServices(list(orig_services))
# remove all ports this handler added
- for port in self._added_ports:
- if port in ksdata.firewall.ports:
- ksdata.firewall.ports.remove(port)
+ all_ports = firewall_proxy.EnabledPorts
+ orig_ports = set(all_ports).difference(self._new_ports_to_add)
+ firewall_proxy.SetEnabledPorts(list(orig_ports))
# remove all trusts this handler added
- for trust in self._added_trusts:
- if trust in ksdata.firewall.trusts:
- ksdata.firewall.trusts.remove(trust)
+ all_trusts = firewall_proxy.Trusts
+ orig_trusts = set(all_trusts).difference(self._new_trusts_to_add)
+ firewall_proxy.SetTrusts(list(orig_trusts))
# remove all services this handler excluded
- for svc in self._removed_svcs:
- if svc in ksdata.firewall.remove_services:
- ksdata.firewall.remove_services.remove(svc)
+ all_services = firewall_proxy.DisabledServices
+ orig_services = set(all_services).difference(self._new_services_to_remove)
+ firewall_proxy.SetDisabledServices(list(orig_services))
self._added_svcs = set()
self._added_ports = set()
self._added_trusts = set()
self._removed_svcs = set()
self._firewall_enabled = None
- self._firewall_default_enabled = None
+ self._firewall_default_state = None

View File

@ -0,0 +1,206 @@
From 8eacfad08b3c27aa9510f2c3337356581bd9bebd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Mon, 3 Jan 2022 17:31:49 +0100
Subject: [PATCH 1/3] Add oscap sanity check before attempting remediation
If something is obviously wrong with the scanner, then don't attempt to remediate
and try to show relevant information in a dialog window.
---
org_fedora_oscap/common.py | 39 ++++++++++++++++++++++++++++--------
org_fedora_oscap/ks/oscap.py | 11 ++++++++++
tests/test_common.py | 8 ++++++++
3 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/org_fedora_oscap/common.py b/org_fedora_oscap/common.py
index 884bbc8..05829ce 100644
--- a/org_fedora_oscap/common.py
+++ b/org_fedora_oscap/common.py
@@ -139,7 +139,8 @@ def execute(self, ** kwargs):
proc = subprocess.Popen(self.args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, ** kwargs)
except OSError as oserr:
- msg = "Failed to run the oscap tool: %s" % oserr
+ msg = ("Failed to execute command '{command_string}': {oserr}"
+ .format(command_string=command_string, oserr=oserr))
raise OSCAPaddonError(msg)
(stdout, stderr) = proc.communicate()
@@ -215,6 +216,34 @@ def _run_oscap_gen_fix(profile, fpath, template, ds_id="", xccdf_id="",
return proc.stdout
+def do_chroot(chroot):
+ """Helper function doing the chroot if requested."""
+ if chroot and chroot != "/":
+ os.chroot(chroot)
+ os.chdir("/")
+
+
+def assert_scanner_works(chroot, executable="oscap"):
+ args = [executable, "--version"]
+ command = " ".join(args)
+
+ try:
+ proc = subprocess.Popen(
+ args, preexec_fn=lambda: do_chroot(chroot),
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ stderr = stderr.decode(errors="replace")
+ except OSError as exc:
+ msg = _(f"Basic invocation '{command}' fails: {str(exc)}")
+ raise OSCAPaddonError(msg)
+ if proc.returncode != 0:
+ msg = _(
+ f"Basic scanner invocation '{command}' exited "
+ "with non-zero error code {proc.returncode}: {stderr}")
+ raise OSCAPaddonError(msg)
+ return True
+
+
def run_oscap_remediate(profile, fpath, ds_id="", xccdf_id="", tailoring="",
chroot=""):
"""
@@ -244,12 +273,6 @@ def run_oscap_remediate(profile, fpath, ds_id="", xccdf_id="", tailoring="",
if not profile:
return ""
- def do_chroot():
- """Helper function doing the chroot if requested."""
- if chroot and chroot != "/":
- os.chroot(chroot)
- os.chdir("/")
-
# make sure the directory for the results exists
results_dir = os.path.dirname(RESULTS_PATH)
if chroot:
@@ -274,7 +297,7 @@ def do_chroot():
args.append(fpath)
proc = SubprocessLauncher(args)
- proc.execute(preexec_fn=do_chroot)
+ proc.execute(preexec_fn=lambda: do_chroot(chroot))
proc.log_messages()
if proc.returncode not in (0, 2):
diff --git a/org_fedora_oscap/ks/oscap.py b/org_fedora_oscap/ks/oscap.py
index 65d74cf..da1600f 100644
--- a/org_fedora_oscap/ks/oscap.py
+++ b/org_fedora_oscap/ks/oscap.py
@@ -488,6 +488,17 @@ def execute(self, storage, ksdata, users, payload):
# selected
return
+ try:
+ common.assert_scanner_works(
+ chroot=conf.target.system_root, executable="oscap")
+ except Exception as exc:
+ msg_lines = [_(
+ "The 'oscap' scanner doesn't work in the installed system: {error}"
+ .format(error=str(exc)))]
+ msg_lines.append(_("As a result, the installed system can't be hardened."))
+ self._terminate("\n".join(msg_lines))
+ return
+
target_content_dir = utils.join_paths(conf.target.system_root,
common.TARGET_CONTENT_DIR)
utils.ensure_dir_exists(target_content_dir)
diff --git a/tests/test_common.py b/tests/test_common.py
index 9f7a16a..4f25379 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -77,6 +77,14 @@ def _run_oscap(mock_subprocess, additional_args):
return expected_args, kwargs
+def test_oscap_works():
+ assert common.assert_scanner_works(chroot="/")
+ with pytest.raises(common.OSCAPaddonError, match="No such file"):
+ common.assert_scanner_works(chroot="/", executable="i_dont_exist")
+ with pytest.raises(common.OSCAPaddonError, match="non-zero"):
+ common.assert_scanner_works(chroot="/", executable="false")
+
+
def test_run_oscap_remediate_profile_only(mock_subprocess, monkeypatch):
return run_oscap_remediate_profile(
mock_subprocess, monkeypatch,
From b54cf2bddba56e5b776fb60514a5e29d47c74cac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Mon, 3 Jan 2022 17:42:31 +0100
Subject: [PATCH 2/3] Don't raise exceptions in execute()
Those result in tracebacks during the installation,
while a dialog window presents a more useful form of user interaction.
---
org_fedora_oscap/ks/oscap.py | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/org_fedora_oscap/ks/oscap.py b/org_fedora_oscap/ks/oscap.py
index da1600f..d3f0dbe 100644
--- a/org_fedora_oscap/ks/oscap.py
+++ b/org_fedora_oscap/ks/oscap.py
@@ -513,8 +513,9 @@ def execute(self, storage, ksdata, users, payload):
ret = util.execInSysroot("yum", ["-y", "--nogpg", "install",
self.raw_postinst_content_path])
if ret != 0:
- raise common.ExtractionError("Failed to install content "
- "RPM to the target system")
+ msg = _(f"Failed to install content RPM to the target system.")
+ self._terminate(msg)
+ return
elif self.content_type == "scap-security-guide":
# nothing needed
pass
@@ -525,10 +526,15 @@ def execute(self, storage, ksdata, users, payload):
if os.path.exists(self.preinst_tailoring_path):
shutil.copy2(self.preinst_tailoring_path, target_content_dir)
- common.run_oscap_remediate(self.profile_id, self.postinst_content_path,
- self.datastream_id, self.xccdf_id,
- self.postinst_tailoring_path,
- chroot=conf.target.system_root)
+ try:
+ common.run_oscap_remediate(self.profile_id, self.postinst_content_path,
+ self.datastream_id, self.xccdf_id,
+ self.postinst_tailoring_path,
+ chroot=conf.target.system_root)
+ except Exception as exc:
+ msg = _(f"Something went wrong during the final hardening: {str(exc)}.")
+ self._terminate(msg)
+ return
def clear_all(self):
"""Clear all the stored values."""
From 00d770d1b7f8e1f0734e93da227f1c3e445033c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Mon, 3 Jan 2022 17:44:12 +0100
Subject: [PATCH 3/3] Change the error feedback based on the installation mode
The original approach was confusing, because non-interactive installs run without any user input,
and the message assumed that the user is able to answer installer's questions.
---
org_fedora_oscap/ks/oscap.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/org_fedora_oscap/ks/oscap.py b/org_fedora_oscap/ks/oscap.py
index d3f0dbe..ef34448 100644
--- a/org_fedora_oscap/ks/oscap.py
+++ b/org_fedora_oscap/ks/oscap.py
@@ -372,13 +372,14 @@ def postinst_tailoring_path(self):
self.tailoring_path)
def _terminate(self, message):
- message += "\n" + _("The installation should be aborted.")
- message += " " + _("Do you wish to continue anyway?")
if flags.flags.automatedInstall and not flags.flags.ksprompt:
# cannot have ask in a non-interactive kickstart
# installation
+ message += "\n" + _("Aborting the installation.")
raise errors.CmdlineError(message)
+ message += "\n" + _("The installation should be aborted.")
+ message += " " + _("Do you wish to continue anyway?")
answ = errors.errorHandler.ui.showYesNoQuestion(message)
if answ == errors.ERROR_CONTINUE:
# prevent any futher actions here by switching to the dry

View File

@ -0,0 +1,39 @@
From 1abc4e96638e819d3fbee74396b36a6ccaf0ab29 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Tue, 3 Aug 2021 11:01:59 +0200
Subject: [PATCH] Refactor content identification
Don't use the multiprocessing pool - it sometimes creates probems during
its initialization:
https://bugzilla.redhat.com/show_bug.cgi?id=1989441
---
org_fedora_oscap/content_handling.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/org_fedora_oscap/content_handling.py b/org_fedora_oscap/content_handling.py
index f2af22f..65d5a28 100644
--- a/org_fedora_oscap/content_handling.py
+++ b/org_fedora_oscap/content_handling.py
@@ -111,9 +111,8 @@ def parse_HTML_from_content(content):
def identify_files(fpaths):
- with multiprocessing.Pool(os.cpu_count()) as p:
- labels = p.map(get_doc_type, fpaths)
- return {path: label for (path, label) in zip(fpaths, labels)}
+ result = {path: get_doc_type(path) for path in fpaths}
+ return result
def get_doc_type(file_path):
@@ -131,7 +130,9 @@ def get_doc_type(file_path):
except UnicodeDecodeError:
# 'oscap info' supplied weird output, which happens when it tries
# to explain why it can't examine e.g. a JPG.
- return None
+ pass
+ except Exception as e:
+ log.warning(f"OSCAP addon: Unexpected error when looking at {file_path}: {str(e)}")
log.info("OSCAP addon: Identified {file_path} as {content_type}"
.format(file_path=file_path, content_type=content_type))
return content_type

View File

@ -0,0 +1,51 @@
From 3377a914f4668af3d72216468ae192bc300890f9 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Mon, 9 Aug 2021 15:45:58 +0200
Subject: [PATCH 1/2] Fix archive handling in GUI installs
GUI downloads an archive, so the ensuing installation doesn't have to.
However, the installation has to be able to discover files recovered
from the archive.
The fix makes sure that files are discovered also in subdirectories.
---
org_fedora_oscap/content_discovery.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/org_fedora_oscap/content_discovery.py b/org_fedora_oscap/content_discovery.py
index f6b4d27..5fc7343 100644
--- a/org_fedora_oscap/content_discovery.py
+++ b/org_fedora_oscap/content_discovery.py
@@ -196,7 +196,8 @@ def _gather_available_files(self, actually_fetched_content, dest_filename):
if not dest_filename: # using scap-security-guide
fpaths = [self.DEFAULT_SSG_DATA_STREAM_PATH]
else: # Using downloaded XCCDF/OVAL/DS/tailoring
- fpaths = glob(str(self.CONTENT_DOWNLOAD_LOCATION / "*.xml"))
+ fpaths = pathlib.Path(self.CONTENT_DOWNLOAD_LOCATION).rglob("*")
+ fpaths = [str(p) for p in fpaths if p.is_file()]
else:
dest_filename = pathlib.Path(dest_filename)
# RPM is an archive at this phase
From 191df327e3e51f486fb655e97acac30222c264fa Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Mon, 9 Aug 2021 15:48:50 +0200
Subject: [PATCH 2/2] Improve logging
Logs written to log files can contain specific details.
---
org_fedora_oscap/ks/oscap.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org_fedora_oscap/ks/oscap.py b/org_fedora_oscap/ks/oscap.py
index d1b8c9e..65d74cf 100644
--- a/org_fedora_oscap/ks/oscap.py
+++ b/org_fedora_oscap/ks/oscap.py
@@ -393,7 +393,7 @@ def _terminate(self, message):
time.sleep(100000)
def _handle_error(self, exception):
- log.error("Failed to fetch and initialize SCAP content!")
+ log.error(f"Failed to fetch and initialize SCAP content: {str(exception)}")
if isinstance(exception, ContentCheckError):
msg = _("The integrity check of the security content failed.")

View File

@ -0,0 +1,32 @@
From 6ac75d5052fff5a7d4b7e249ef198ccecd1f86a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Mon, 17 Jul 2023 17:08:54 +0200
Subject: [PATCH] Make tar extraction safer
See also https://bugzilla.redhat.com/show_bug.cgi?id=2218875
---
org_fedora_oscap/common.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/org_fedora_oscap/common.py b/org_fedora_oscap/common.py
index 05829ce..b27276e 100644
--- a/org_fedora_oscap/common.py
+++ b/org_fedora_oscap/common.py
@@ -360,7 +360,7 @@ def extract_data(archive, out_dir, ensure_has_files=None):
raise ExtractionError(msg)
utils.ensure_dir_exists(out_dir)
- zfile.extractall(path=out_dir)
+ zfile.extractall(path=out_dir, filter="data")
result = [utils.join_paths(out_dir, info.filename) for info in zfile.filelist]
zfile.close()
elif archive.endswith(".tar"):
@@ -418,7 +418,7 @@ def _extract_tarball(archive, out_dir, ensure_has_files, alg):
raise ExtractionError(msg)
utils.ensure_dir_exists(out_dir)
- tfile.extractall(path=out_dir)
+ tfile.extractall(path=out_dir, filter="data")
result = [utils.join_paths(out_dir, member.path) for member in tfile.getmembers()]
tfile.close()

View File

@ -0,0 +1,372 @@
From e8e303aa3ca9db564ea52258de15a81851c3b265 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Wed, 12 Oct 2022 11:37:04 +0200
Subject: [PATCH 1/5] Add capability to preselect content from archives
Users can specify content path and tailoring path in kickstarts,
and the addon should be able to assure that those files are available,
and that they have precedence over other files.
---
org_fedora_oscap/content_discovery.py | 35 +++++++++++++++++++
tests/test_content_discovery.py | 48 +++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
create mode 100644 tests/test_content_discovery.py
diff --git a/org_fedora_oscap/content_discovery.py b/org_fedora_oscap/content_discovery.py
index 5fc7343..f654449 100644
--- a/org_fedora_oscap/content_discovery.py
+++ b/org_fedora_oscap/content_discovery.py
@@ -11,6 +11,7 @@
from org_fedora_oscap import data_fetch, utils
from org_fedora_oscap import common
from org_fedora_oscap import content_handling
+from org_fedora_oscap.content_handling import CONTENT_TYPES
from org_fedora_oscap.common import _
@@ -167,6 +168,38 @@ def _verify_fingerprint(self, dest_filename, fingerprint=""):
msg = _(f"Integrity check of the content failed - {hash_obj.name} hash didn't match")
raise content_handling.ContentCheckError(msg)
+ def filter_discovered_content(self, labelled_files):
+ expected_path = self._addon_data.content_path
+ categories = (CONTENT_TYPES["DATASTREAM"], CONTENT_TYPES["XCCDF_CHECKLIST"])
+ if expected_path:
+ labelled_files = self.reduce_files(labelled_files, expected_path, categories)
+
+ expected_path = self._addon_data.tailoring_path
+ categories = (CONTENT_TYPES["TAILORING"], )
+ if expected_path:
+ labelled_files = self.reduce_files(labelled_files, expected_path, categories)
+
+ expected_path = self._addon_data.cpe_path
+ categories = (CONTENT_TYPES["CPE_DICT"], )
+ if expected_path:
+ labelled_files = self.reduce_files(labelled_files, expected_path, categories)
+
+ return labelled_files
+
+ def reduce_files(self, labelled_files, expected_path, categories):
+ reduced_files = dict()
+ if expected_path not in labelled_files:
+ msg = (
+ f"Expected a file {expected_path} to be part of the supplied content, "
+ f"but it was not the case, got only {list(labelled_files.keys())}"
+ )
+ raise RuntimeError(msg)
+ for path, label in labelled_files.items():
+ if label in categories and path != expected_path:
+ continue
+ reduced_files[path] = label
+ return reduced_files
+
def _finish_actual_fetch(self, wait_for, fingerprint, report_callback, dest_filename):
threadMgr.wait(wait_for)
actually_fetched_content = wait_for is not None
@@ -182,6 +215,8 @@ def _finish_actual_fetch(self, wait_for, fingerprint, report_callback, dest_file
structured_content.add_content_archive(dest_filename)
labelled_files = content_handling.identify_files(fpaths)
+ labelled_files = self.filter_discovered_content(labelled_files)
+
for fname, label in labelled_files.items():
structured_content.add_file(fname, label)
diff --git a/tests/test_content_discovery.py b/tests/test_content_discovery.py
new file mode 100644
index 0000000..5463c9a
--- /dev/null
+++ b/tests/test_content_discovery.py
@@ -0,0 +1,48 @@
+import pytest
+
+import org_fedora_oscap.content_discovery as tested_module
+
+
+@pytest.fixture
+def labelled_files():
+ return {
+ "dir/datastream": "D",
+ "dir/datastream2": "D",
+ "dir/dir/datastream3": "D",
+ "dir/dir/datastream3": "D",
+ "dir/XCCDF": "X",
+ "XCCDF2": "X",
+ "cpe": "C",
+ "t1": "T",
+ "dir3/t2": "T",
+ }
+
+
+def test_reduce(labelled_files):
+ bringer = tested_module.ContentBringer(None)
+
+ d_count = 0
+ x_count = 0
+ for l in labelled_files.values():
+ if l == "D":
+ d_count += 1
+ elif l == "X":
+ x_count += 1
+
+ reduced = bringer.reduce_files(labelled_files, "dir/datastream", ["D"])
+ assert len(reduced) == len(labelled_files) - d_count + 1
+ assert "dir/datastream" in reduced
+
+ reduced = bringer.reduce_files(labelled_files, "dir/datastream", ["D", "X"])
+ assert len(reduced) == len(labelled_files) - d_count - x_count + 1
+ assert "dir/datastream" in reduced
+
+ reduced = bringer.reduce_files(labelled_files, "dir/XCCDF", ["D", "X"])
+ assert len(reduced) == len(labelled_files) - d_count - x_count + 1
+ assert "dir/XCCDF" in reduced
+
+ with pytest.raises(RuntimeError, match="dir/datastream4"):
+ bringer.reduce_files(labelled_files, "dir/datastream4", ["D"])
+
+ reduced = bringer.reduce_files(labelled_files, "cpe", ["C"])
+ assert reduced == labelled_files
From 82c1950903fcce079cd71f021c1fde25f75f9521 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Wed, 12 Oct 2022 11:40:11 +0200
Subject: [PATCH 2/5] Handle changes in content identification
The code is able to handle changes in the way how oscap identifies
content much more gracefully.
---
org_fedora_oscap/content_discovery.py | 13 +++++++++----
org_fedora_oscap/content_handling.py | 5 +++++
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/org_fedora_oscap/content_discovery.py b/org_fedora_oscap/content_discovery.py
index f654449..b20f3a6 100644
--- a/org_fedora_oscap/content_discovery.py
+++ b/org_fedora_oscap/content_discovery.py
@@ -2,6 +2,7 @@
import logging
import pathlib
import shutil
+import os
from glob import glob
from pyanaconda.core import constants
@@ -214,11 +215,15 @@ def _finish_actual_fetch(self, wait_for, fingerprint, report_callback, dest_file
if content_type in ("archive", "rpm"):
structured_content.add_content_archive(dest_filename)
- labelled_files = content_handling.identify_files(fpaths)
- labelled_files = self.filter_discovered_content(labelled_files)
+ labelled_filenames = content_handling.identify_files(fpaths)
+ labelled_relative_filenames = {
+ os.path.relpath(path, self.CONTENT_DOWNLOAD_LOCATION): label
+ for path, label in labelled_filenames.items()}
+ labelled_relative_filenames = self.filter_discovered_content(labelled_relative_filenames)
- for fname, label in labelled_files.items():
- structured_content.add_file(fname, label)
+ for rel_fname, label in labelled_relative_filenames.items():
+ fname = self.CONTENT_DOWNLOAD_LOCATION / rel_fname
+ structured_content.add_file(str(fname), label)
if fingerprint and dest_filename:
structured_content.record_verification(dest_filename)
diff --git a/org_fedora_oscap/content_handling.py b/org_fedora_oscap/content_handling.py
index 65d5a28..3e2ecae 100644
--- a/org_fedora_oscap/content_handling.py
+++ b/org_fedora_oscap/content_handling.py
@@ -122,6 +122,11 @@ def get_doc_type(file_path):
if line.startswith("Document type:"):
_prefix, _sep, type_info = line.partition(":")
content_type = type_info.strip()
+ if content_type not in CONTENT_TYPES.values():
+ log.info(
+ f"File {file_path} labelled by oscap as {content_type}, "
+ "which is an unexpected type.")
+ content_type = f"unknown - {content_type}"
break
except OSError:
# 'oscap info' exitted with a non-zero exit code -> unknown doc
From b6bf5a6c96f5dbbd78043455802ebc0033cf1a6a Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Wed, 12 Oct 2022 11:38:51 +0200
Subject: [PATCH 3/5] Remove unused code
The function is not referenced anywhere in the project
---
org_fedora_oscap/content_handling.py | 40 ----------------------------
1 file changed, 40 deletions(-)
diff --git a/org_fedora_oscap/content_handling.py b/org_fedora_oscap/content_handling.py
index 3e2ecae..5096bab 100644
--- a/org_fedora_oscap/content_handling.py
+++ b/org_fedora_oscap/content_handling.py
@@ -141,43 +141,3 @@ def get_doc_type(file_path):
log.info("OSCAP addon: Identified {file_path} as {content_type}"
.format(file_path=file_path, content_type=content_type))
return content_type
-
-
-def explore_content_files(fpaths):
- """
- Function for finding content files in a list of file paths. SIMPLY PICKS
- THE FIRST USABLE CONTENT FILE OF A PARTICULAR TYPE AND JUST PREFERS DATA
- STREAMS OVER STANDALONE BENCHMARKS.
-
- :param fpaths: a list of file paths to search for content files in
- :type fpaths: [str]
- :return: ContentFiles instance containing the file names of the XCCDF file,
- CPE dictionary and tailoring file or "" in place of those items
- if not found
- :rtype: ContentFiles
-
- """
- xccdf_file = ""
- cpe_file = ""
- tailoring_file = ""
- found_ds = False
-
- for fpath in fpaths:
- doc_type = get_doc_type(fpath)
- if not doc_type:
- continue
-
- # prefer DS over standalone XCCDF
- if doc_type == "Source Data Stream" and (not xccdf_file or not found_ds):
- xccdf_file = fpath
- found_ds = True
- elif doc_type == "XCCDF Checklist" and not xccdf_file:
- xccdf_file = fpath
- elif doc_type == "CPE Dictionary" and not cpe_file:
- cpe_file = fpath
- elif doc_type == "XCCDF Tailoring" and not tailoring_file:
- tailoring_file = fpath
-
- # TODO: raise exception if no xccdf_file is found?
- files = ContentFiles(xccdf_file, cpe_file, tailoring_file)
- return files
From a990568ccddb2864c8daeae91fdc1f6588b3c6f3 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Thu, 13 Oct 2022 14:11:25 +0200
Subject: [PATCH 4/5] Dont use tailoring if it is not expected
Take tailorings into account only if it is specified in the kickstart.
Compulsive usage of tailoring may be unwanted.
---
org_fedora_oscap/content_discovery.py | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/org_fedora_oscap/content_discovery.py b/org_fedora_oscap/content_discovery.py
index b20f3a6..e9cf34a 100644
--- a/org_fedora_oscap/content_discovery.py
+++ b/org_fedora_oscap/content_discovery.py
@@ -169,16 +169,25 @@ def _verify_fingerprint(self, dest_filename, fingerprint=""):
msg = _(f"Integrity check of the content failed - {hash_obj.name} hash didn't match")
raise content_handling.ContentCheckError(msg)
+ def allow_one_expected_tailoring_or_no_tailoring(self, labelled_files):
+ expected_tailoring = self._addon_data.tailoring_path
+ tailoring_label = CONTENT_TYPES["TAILORING"]
+ if expected_tailoring:
+ labelled_files = self.reduce_files(labelled_files, expected_tailoring, [tailoring_label])
+ else:
+ labelled_files = {
+ path: label for path, label in labelled_files.items()
+ if label != tailoring_label
+ }
+ return labelled_files
+
def filter_discovered_content(self, labelled_files):
expected_path = self._addon_data.content_path
categories = (CONTENT_TYPES["DATASTREAM"], CONTENT_TYPES["XCCDF_CHECKLIST"])
if expected_path:
labelled_files = self.reduce_files(labelled_files, expected_path, categories)
- expected_path = self._addon_data.tailoring_path
- categories = (CONTENT_TYPES["TAILORING"], )
- if expected_path:
- labelled_files = self.reduce_files(labelled_files, expected_path, categories)
+ labelled_files = self.allow_one_expected_tailoring_or_no_tailoring(labelled_files)
expected_path = self._addon_data.cpe_path
categories = (CONTENT_TYPES["CPE_DICT"], )
From c4cb296ca3838a0967c8258b9ed5221691884a36 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Tue, 8 Nov 2022 10:46:59 +0100
Subject: [PATCH 5/5] Make the content RPM installation robust
If a package manager fails to install the package,
use the rpm command directly and skip deps.
---
org_fedora_oscap/ks/oscap.py | 41 ++++++++++++++++++++++++++++--------
1 file changed, 32 insertions(+), 9 deletions(-)
diff --git a/org_fedora_oscap/ks/oscap.py b/org_fedora_oscap/ks/oscap.py
index e47d6ba..dac273d 100644
--- a/org_fedora_oscap/ks/oscap.py
+++ b/org_fedora_oscap/ks/oscap.py
@@ -23,6 +23,7 @@
import shutil
import re
import os
+import io
import time
import logging
import pathlib
@@ -473,6 +474,33 @@ def setup(self, storage, ksdata, payload):
if pkg not in ksdata.packages.packageList:
ksdata.packages.packageList.append(pkg)
+ def _attempt_rpm_installation(self):
+ log.info("OSCAP addon: Installing the security content RPM to the installed system.")
+ stdout = io.StringIO()
+ ret = util.execWithRedirect(
+ "yum", ["-y", "--nogpg", "install", self.raw_postinst_content_path],
+ stdout=stdout, root=conf.target.system_root)
+ stdout.seek(0)
+ if ret != 0:
+ log.error(
+ "OSCAP addon: Error installing security content RPM using yum: {0}",
+ stdout.read())
+
+ stdout = io.StringIO()
+ ret = util.execWithRedirect(
+ "rpm", ["--install", "--nodeps", self.raw_postinst_content_path],
+ stdout=stdout, root=conf.target.system_root)
+ if ret != 0:
+ log.error(
+ "OSCAP addon: Error installing security content RPM using rpm: {0}",
+ stdout.read())
+ msg = _(f"Failed to install content RPM to the target system.")
+ raise RuntimeError(msg)
+
+ def _copy_rpm_to_target_and_install(self, target_content_dir):
+ shutil.copy2(self.raw_preinst_content_path, target_content_dir)
+ self._attempt_rpm_installation()
+
def execute(self, storage, ksdata, users, payload):
"""
The execute method that should make changes to the installed system. It
@@ -507,15 +535,10 @@ def execute(self, storage, ksdata, users, payload):
if self.content_type == "datastream":
shutil.copy2(self.preinst_content_path, target_content_dir)
elif self.content_type == "rpm":
- # copy the RPM to the target system
- shutil.copy2(self.raw_preinst_content_path, target_content_dir)
-
- # and install it with yum
- ret = util.execInSysroot("yum", ["-y", "--nogpg", "install",
- self.raw_postinst_content_path])
- if ret != 0:
- msg = _(f"Failed to install content RPM to the target system.")
- self._terminate(msg)
+ try:
+ self._copy_rpm_to_target_and_install(target_content_dir)
+ except Exception as exc:
+ self._terminate(str(exc))
return
elif self.content_type == "scap-security-guide":
# nothing needed

View File

@ -0,0 +1,74 @@
From 55cc3b685dd5a9ca6059459f41876dd9f19f900d Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Tue, 11 Oct 2022 17:07:28 +0200
Subject: [PATCH 1/2] Remove redundant message
The send_ready already performs what the removed call
could aim to accomplish.
---
org_fedora_oscap/gui/spokes/oscap.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/org_fedora_oscap/gui/spokes/oscap.py b/org_fedora_oscap/gui/spokes/oscap.py
index c57b1cd..4f8702a 100644
--- a/org_fedora_oscap/gui/spokes/oscap.py
+++ b/org_fedora_oscap/gui/spokes/oscap.py
@@ -150,7 +150,6 @@ def decorated(self, *args, **kwargs):
self._ready = True
# pylint: disable-msg=E1101
hubQ.send_ready(self.__class__.__name__, True)
- hubQ.send_message(self.__class__.__name__, self.status)
return ret
From 3f7c560947a17d1696899857e70ebcc8cba44019 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Thu, 13 Oct 2022 17:19:17 +0200
Subject: [PATCH 2/2] Increase robustness of fetching state detection
It is not completely practical to rely on locks alone,
and we can elliminate some corner cases by looking
whether well-known UI threads exist.
---
org_fedora_oscap/gui/spokes/oscap.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/org_fedora_oscap/gui/spokes/oscap.py b/org_fedora_oscap/gui/spokes/oscap.py
index 4f8702a..d8e6ce2 100644
--- a/org_fedora_oscap/gui/spokes/oscap.py
+++ b/org_fedora_oscap/gui/spokes/oscap.py
@@ -363,11 +363,14 @@ def _render_selected(self, column, renderer, model, itr, user_data=None):
else:
renderer.set_property("stock-id", None)
+ def _still_fetching(self):
+ return self._fetching or threadMgr.get('OSCAPguiWaitForDataFetchThread')
+
def _fetch_data_and_initialize(self):
"""Fetch data from a specified URL and initialize everything."""
with self._fetch_flag_lock:
- if self._fetching:
+ if self._still_fetching():
# prevent multiple fetches running simultaneously
return
self._fetching = True
@@ -894,7 +897,7 @@ def refresh(self):
# hide the progress box, no progress now
with self._fetch_flag_lock:
- if not self._fetching:
+ if not self._still_fetching():
really_hide(self._progress_box)
self._content_url_entry.set_sensitive(True)
@@ -1117,7 +1120,7 @@ def on_fetch_button_clicked(self, *args):
"""Handler for the Fetch button"""
with self._fetch_flag_lock:
- if self._fetching:
+ if self._still_fetching():
# some other fetching/pre-processing running, give up
log.warn("Clicked the fetch button, although the GUI is in the fetching mode.")
return

View File

@ -0,0 +1,202 @@
From 08d3da5640e5c16cda4e79cc13ac7921f1ebd964 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Tue, 15 Nov 2022 15:37:28 +0100
Subject: [PATCH 1/2] Fix handling of content paths
Archives and ready-to-use content use paths differently.
Archives get unpacked into a directory, where they need to be unpacked,
analyzed, and cross-checked with e.g. the supplied content path,
whereas ready-to-use content can be used directly.
As the current codebase doesn't untangle all possible ways how to obtain
existing content in a way of decomposing those into layers, this change
just makes the current code working at the expense of making it worse to
maintain.
---
org_fedora_oscap/content_discovery.py | 34 ++++++++++++++++++---------
org_fedora_oscap/ks/oscap.py | 6 ++++-
tests/test_content_discovery.py | 21 +++++++++++++++++
3 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/org_fedora_oscap/content_discovery.py b/org_fedora_oscap/content_discovery.py
index e9cf34a..2b71b1f 100644
--- a/org_fedora_oscap/content_discovery.py
+++ b/org_fedora_oscap/content_discovery.py
@@ -25,6 +25,14 @@ def is_network(scheme):
for net_prefix in data_fetch.NET_URL_PREFIXES)
+def path_is_present_among_paths(path, paths):
+ absolute_path = os.path.abspath(path)
+ for second_path in paths:
+ if absolute_path == os.path.abspath(second_path):
+ return True
+ return False
+
+
class ContentBringer:
CONTENT_DOWNLOAD_LOCATION = pathlib.Path(common.INSTALLATION_CONTENT_DIR)
DEFAULT_SSG_DATA_STREAM_PATH = f"{common.SSG_DIR}/{common.SSG_CONTENT}"
@@ -170,7 +178,7 @@ def _verify_fingerprint(self, dest_filename, fingerprint=""):
raise content_handling.ContentCheckError(msg)
def allow_one_expected_tailoring_or_no_tailoring(self, labelled_files):
- expected_tailoring = self._addon_data.tailoring_path
+ expected_tailoring = self._addon_data.preinst_tailoring_path
tailoring_label = CONTENT_TYPES["TAILORING"]
if expected_tailoring:
labelled_files = self.reduce_files(labelled_files, expected_tailoring, [tailoring_label])
@@ -182,7 +190,7 @@ def allow_one_expected_tailoring_or_no_tailoring(self, labelled_files):
return labelled_files
def filter_discovered_content(self, labelled_files):
- expected_path = self._addon_data.content_path
+ expected_path = self._addon_data.preinst_content_path
categories = (CONTENT_TYPES["DATASTREAM"], CONTENT_TYPES["XCCDF_CHECKLIST"])
if expected_path:
labelled_files = self.reduce_files(labelled_files, expected_path, categories)
@@ -198,7 +206,7 @@ def filter_discovered_content(self, labelled_files):
def reduce_files(self, labelled_files, expected_path, categories):
reduced_files = dict()
- if expected_path not in labelled_files:
+ if not path_is_present_among_paths(expected_path, labelled_files.keys()):
msg = (
f"Expected a file {expected_path} to be part of the supplied content, "
f"but it was not the case, got only {list(labelled_files.keys())}"
@@ -225,13 +233,9 @@ def _finish_actual_fetch(self, wait_for, fingerprint, report_callback, dest_file
structured_content.add_content_archive(dest_filename)
labelled_filenames = content_handling.identify_files(fpaths)
- labelled_relative_filenames = {
- os.path.relpath(path, self.CONTENT_DOWNLOAD_LOCATION): label
- for path, label in labelled_filenames.items()}
- labelled_relative_filenames = self.filter_discovered_content(labelled_relative_filenames)
+ labelled_filenames = self.filter_discovered_content(labelled_filenames)
- for rel_fname, label in labelled_relative_filenames.items():
- fname = self.CONTENT_DOWNLOAD_LOCATION / rel_fname
+ for fname, label in labelled_filenames.items():
structured_content.add_file(str(fname), label)
if fingerprint and dest_filename:
@@ -274,11 +278,18 @@ def use_downloaded_content(self, content):
# We know that we have ended up with a datastream-like content,
# but if we can't convert an archive to a datastream.
# self._addon_data.content_type = "datastream"
- self._addon_data.content_path = str(preferred_content.relative_to(content.root))
+ content_type = self._addon_data.content_type
+ if content_type in ("archive", "rpm"):
+ self._addon_data.content_path = str(preferred_content.relative_to(content.root))
+ else:
+ self._addon_data.content_path = str(preferred_content)
preferred_tailoring = self.get_preferred_tailoring(content)
if content.tailoring:
- self._addon_data.tailoring_path = str(preferred_tailoring.relative_to(content.root))
+ if content_type in ("archive", "rpm"):
+ self._addon_data.tailoring_path = str(preferred_tailoring.relative_to(content.root))
+ else:
+ self._addon_data.tailoring_path = str(preferred_tailoring)
def use_system_content(self, content=None):
self._addon_data.clear_all()
@@ -372,6 +383,7 @@ def _xccdf_content(self):
def find_expected_usable_content(self, relative_expected_content_path):
content_path = self.root / relative_expected_content_path
+ content_path = content_path.resolve()
eligible_main_content = (self._datastream_content(), self._xccdf_content())
if content_path in eligible_main_content:
diff --git a/org_fedora_oscap/ks/oscap.py b/org_fedora_oscap/ks/oscap.py
index dac273d..7d4a131 100644
--- a/org_fedora_oscap/ks/oscap.py
+++ b/org_fedora_oscap/ks/oscap.py
@@ -179,7 +179,11 @@ def _parse_profile_id(self, value):
self.profile_id = value
def _parse_content_path(self, value):
- # need to be checked?
+ if self.content_type in ("archive", "rpm") and os.path.isabs(self.content_path):
+ msg = (
+ "When using archives-like content input, the corresponding content path "
+ "has to be relative, but got '{self.content_path}'.")
+ raise KickstartValueError(msg)
self.content_path = value
def _parse_cpe_path(self, value):
diff --git a/tests/test_content_discovery.py b/tests/test_content_discovery.py
index 5463c9a..d6e14d9 100644
--- a/tests/test_content_discovery.py
+++ b/tests/test_content_discovery.py
@@ -1,3 +1,5 @@
+import os
+
import pytest
import org_fedora_oscap.content_discovery as tested_module
@@ -46,3 +48,22 @@ def test_reduce(labelled_files):
reduced = bringer.reduce_files(labelled_files, "cpe", ["C"])
assert reduced == labelled_files
+
+
+def test_path_presence_detection():
+ list_of_paths = ["file1", os.path.abspath("file2"), os.path.abspath("dir///file3")]
+
+ list_of_paths_in_list = [
+ "file1", os.path.abspath("file1"), "./file1",
+ "file2", "dir/..//file2",
+ "dir/../dir/file3", "dir/file3",
+ ]
+ list_of_paths_not_in_list = [
+ "../file1", "file3"
+ ]
+
+ for path in list_of_paths_in_list:
+ assert tested_module.path_is_present_among_paths(path, list_of_paths)
+
+ for path in list_of_paths_not_in_list:
+ assert not tested_module.path_is_present_among_paths(path, list_of_paths)
From 786ec5d90d12a1321fbff86f5d8d4a534059ad22 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Wed, 16 Nov 2022 15:35:09 +0100
Subject: [PATCH 2/2] Compare paths according to their equivalence
not according their arbitrary string form
---
org_fedora_oscap/content_discovery.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/org_fedora_oscap/content_discovery.py b/org_fedora_oscap/content_discovery.py
index 2b71b1f..42c61e0 100644
--- a/org_fedora_oscap/content_discovery.py
+++ b/org_fedora_oscap/content_discovery.py
@@ -25,10 +25,14 @@ def is_network(scheme):
for net_prefix in data_fetch.NET_URL_PREFIXES)
+def paths_are_equivalent(p1, p2):
+ return os.path.abspath(p1) == os.path.abspath(p2)
+
+
def path_is_present_among_paths(path, paths):
absolute_path = os.path.abspath(path)
for second_path in paths:
- if absolute_path == os.path.abspath(second_path):
+ if paths_are_equivalent(path, second_path):
return True
return False
@@ -213,7 +217,7 @@ def reduce_files(self, labelled_files, expected_path, categories):
)
raise RuntimeError(msg)
for path, label in labelled_files.items():
- if label in categories and path != expected_path:
+ if label in categories and not paths_are_equivalent(path, expected_path):
continue
reduced_files[path] = label
return reduced_files

View File

@ -0,0 +1,66 @@
From 58d4847dc4b55b9d4982be9505127679beca87c6 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Wed, 18 Jan 2023 16:36:36 +0100
Subject: [PATCH 1/2] Handle the URL with missing ://
---
org_fedora_oscap/content_discovery.py | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/org_fedora_oscap/content_discovery.py b/org_fedora_oscap/content_discovery.py
index 42c61e0..23fdafd 100644
--- a/org_fedora_oscap/content_discovery.py
+++ b/org_fedora_oscap/content_discovery.py
@@ -67,9 +67,14 @@ def content_uri(self):
@content_uri.setter
def content_uri(self, uri):
- scheme, path = uri.split("://", 1)
- self.content_uri_path = path
- self.content_uri_scheme = scheme
+ scheme_and_maybe_path = uri.split("://")
+ if len(scheme_and_maybe_path) == 1:
+ msg = (
+ f"Invalid supplied content URL '{uri}', "
+ "use the 'scheme://path' form.")
+ raise KickstartValueError(msg)
+ self.content_uri_path = scheme_and_maybe_path[1]
+ self.content_uri_scheme = scheme_and_maybe_path[0]
def fetch_content(self, what_if_fail, ca_certs_path=""):
"""
@@ -80,7 +85,10 @@ def fetch_content(self, what_if_fail, ca_certs_path=""):
should handle them in the calling layer.
ca_certs_path: Path to the HTTPS certificate file
"""
- self.content_uri = self._addon_data.content_url
+ try:
+ self.content_uri = self._addon_data.content_url
+ except Exception as exc:
+ what_if_fail(exc)
shutil.rmtree(self.CONTENT_DOWNLOAD_LOCATION, ignore_errors=True)
self.CONTENT_DOWNLOAD_LOCATION.mkdir(parents=True, exist_ok=True)
fetching_thread_name = self._fetch_files(
From cbfdae4f43ade3ef982a967f3e2844e66db3f9a0 Mon Sep 17 00:00:00 2001
From: Matej Tyc <matyc@redhat.com>
Date: Wed, 18 Jan 2023 16:36:53 +0100
Subject: [PATCH 2/2] Stop fetching when there is an invalid profile
---
org_fedora_oscap/gui/spokes/oscap.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/org_fedora_oscap/gui/spokes/oscap.py b/org_fedora_oscap/gui/spokes/oscap.py
index d8e6ce2..54eae1e 100644
--- a/org_fedora_oscap/gui/spokes/oscap.py
+++ b/org_fedora_oscap/gui/spokes/oscap.py
@@ -469,6 +469,8 @@ def update_progress_label(msg):
if self._addon_data.profile_id and not selected:
# profile ID given, but it was impossible to select it -> invalid
# profile ID given
+ with self._fetch_flag_lock:
+ self._fetching = False
self._invalid_profile_id()
return

View File

@ -1,68 +0,0 @@
From 44a643f4c115d638d42f19f668cef1c220aab1b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Thu, 17 Jan 2019 18:06:02 +0100
Subject: [PATCH] Updated the code to use the up-to-date Anaconda API.
Fixes RHBZ#1665551
---
org_fedora_oscap/gui/spokes/oscap.py | 29 +++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/org_fedora_oscap/gui/spokes/oscap.py b/org_fedora_oscap/gui/spokes/oscap.py
index 36fd656..f16699b 100644
--- a/org_fedora_oscap/gui/spokes/oscap.py
+++ b/org_fedora_oscap/gui/spokes/oscap.py
@@ -38,6 +38,8 @@
from pyanaconda.ui.categories.system import SystemCategory
from pykickstart.errors import KickstartValueError
+from pyanaconda.modules.common.constants.services import USERS
+
# pylint: disable-msg=E0611
from gi.repository import Gdk
@@ -650,26 +652,35 @@ def _update_message_store(self, report_only=False):
def _resolve_rootpw_issues(self, messages, report_only):
"""Mitigate root password issues (which are not fatal in GUI)"""
- fatal_rootpw_msgs = [msg for msg in messages
- if msg.origin == rule_handling.PasswdRules and msg.type == common.MESSAGE_TYPE_FATAL]
+ fatal_rootpw_msgs = [
+ msg for msg in messages
+ if msg.origin == rule_handling.PasswdRules and msg.type == common.MESSAGE_TYPE_FATAL]
+
if fatal_rootpw_msgs:
for msg in fatal_rootpw_msgs:
# cannot just change the message type because it is a namedtuple
messages.remove(msg)
- messages.append(common.RuleMessage(self.__class__,
- common.MESSAGE_TYPE_WARNING,
- msg.text))
+
+ msg = common.RuleMessage(
+ self.__class__, common.MESSAGE_TYPE_WARNING, msg.text)
+ messages.append(msg)
+
if not report_only:
- self.__old_root_pw = self.data.rootpw.password
+ users_proxy = USERS.get_proxy()
+
+ self.__old_root_pw = users_proxy.RootPassword
self.data.rootpw.password = None
- self.__old_root_pw_seen = self.data.rootpw.seen
+ self.__old_root_pw_seen = users_proxy.IsRootpwKickstarted
self.data.rootpw.seen = False
def _revert_rootpw_changes(self):
if self.__old_root_pw is not None:
- self.data.rootpw.password = self.__old_root_pw
- self.data.rootpw.seen = self.__old_root_pw_seen
+ users_proxy = USERS.get_proxy()
+
+ users_proxy.SetRootPassword(self.__old_root_pw)
self.__old_root_pw = None
+
+ users_proxy.SetRootpwKickstarted(self.__old_root_pw_seen)
self.__old_root_pw_seen = None
@async_action_wait

View File

@ -1,22 +0,0 @@
From c88dba4b9deeb78158bf2e239e4b7118a9e8b39f Mon Sep 17 00:00:00 2001
From: Marek Haicman <mhaicman@redhat.com>
Date: Thu, 7 Feb 2019 19:24:08 +0100
Subject: [PATCH] Hack hub title to show translated.
---
org_fedora_oscap/gui/spokes/oscap.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/org_fedora_oscap/gui/spokes/oscap.py b/org_fedora_oscap/gui/spokes/oscap.py
index 72c1501..594969a 100644
--- a/org_fedora_oscap/gui/spokes/oscap.py
+++ b/org_fedora_oscap/gui/spokes/oscap.py
@@ -190,6 +190,8 @@ def __init__(self, data, storage, payload, instclass):
NormalSpoke.__init__(self, data, storage, payload, instclass)
self._addon_data = self.data.addons.org_fedora_oscap
+ # workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1673071
+ self.title = _(self.title)
self._storage = storage
self._ready = False

View File

@ -2,8 +2,8 @@
%global _default_patch_flags --no-backup-if-mismatch
Name: oscap-anaconda-addon
Version: 1.0
Release: 10%{?dist}
Version: 1.2.1
Release: 14%{?dist}
Summary: Anaconda addon integrating OpenSCAP to the installation process
License: GPLv2+
@ -16,25 +16,26 @@ URL: https://github.com/OpenSCAP/oscap-anaconda-addon
# or via direct git checkout:
# git clone https://github.com/OpenSCAP/oscap-anaconda-addon.git
Source0: %{name}-%{version}.tar.gz
# Let the Patch1 be reserved for translations patches
Patch1: lang.patch
Patch2: oaa-api-update.patch
Patch3: help_id.patch
Patch4: rootpw.patch
Patch5: bootloader.patch
Patch6: checksum.patch
Patch7: translate_spoke_title.patch
Patch8: do_not_use_capitals_for_the_spoke_title.patch
Patch2: oscap-anaconda-addon-1.2.2-content_ident-PR_167.patch
Patch3: oscap-anaconda-addon-1.2.2-deep_archives-PR_168.patch
Patch4: oscap-anaconda-addon-1.2.2-absent_appstream-PR_184.patch
Patch5: oscap-anaconda-addon-1.3.0-better_archive_handling-PR_220.patch
Patch6: oscap-anaconda-addon-1.3.0-clicking_nocrash-PR_221.patch
Patch7: oscap-anaconda-addon-1.3.0-fix_content_paths-PR_225.patch
Patch8: oscap-anaconda-addon-null-http_content_url-PR_232.patch
Patch9: oscap-anaconda-addon-1.2.2-tar-extraction-PR_249.patch
BuildArch: noarch
BuildRequires: make
BuildRequires: gettext
BuildRequires: python3-devel
BuildRequires: python3-devel
BuildRequires: python3-pycurl
#BuildRequires: python-mock
#BuildRequires: python-nose
#BuildRequires: python3-cpio
BuildRequires: openscap openscap-utils openscap-python3
BuildRequires: anaconda-core >= 28.22.10
Requires: anaconda-core >= 28.22.10
BuildRequires: anaconda-core >= 33
Requires: anaconda-core >= 33
Requires: python3-cpio
Requires: python3-pycurl
Requires: python3-kickstart
@ -49,6 +50,9 @@ content.
%prep
%setup -q -n %{name}-%{version}
# As patches may translates the strings that are updated by later patches,
# Patch1 needs to be aplied last.
%patch1 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
@ -56,9 +60,11 @@ content.
%patch6 -p1
%patch7 -p1
%patch8 -p1
# As Patch1 translates the upsated string "_Security Policy" added by Patch8,
# Patch1 needs to be aplied after Patch8
%patch1 -p1
%patch9 -p1
# NOTE CONCERNING TRANSLATION PATCHES
# When preparing translation patches, don't consider that some languages are unsupported -
# we aim to include all applicable translation texts to the appropriate patch.
# This has consulted with ljanda@redhat.com, and we basically follow the existing practice of the Anaconda project we integrate into.
%build
@ -76,6 +82,99 @@ make install DESTDIR=%{buildroot}
%doc COPYING ChangeLog README.md
%changelog
* Wed Aug 02 2023 Jan Černý <jcerny@redhat.com> - 1.2.1-14
- Rebuild after tests update
* Wed Jul 19 2023 Jan Černý <jcerny@redhat.com> - 1.2.1-13
- Fix tar file extraction (rhbz#2219408)
- Update translations (rhbz#2189572)
* Wed Feb 08 2023 Matej Tyc <matyc@redhat.com> - 1.2.1-12
- Update translations
Resolves: rhbz#2139743
* Mon Jan 23 2023 Matej Tyc <matyc@redhat.com> - 1.2.1-11
- Fix a reaction to invalid content URI
Resolves: rhbz#2148509
* Wed Nov 23 2022 Matej Tyc <matyc@redhat.com> - 1.2.1-10
- Fix regression introduced when fixing content archive input
Resolves: rhbz#2129008
* Thu Nov 10 2022 Matej Tyc <matyc@redhat.com> - 1.2.1-9
- Fix problems with handling multi-datastream archives
Resolves: rhbz#2129008
- Fix a crash when compulsively clicking in the GUI
Resolves: rhbz#2000998
* Wed Jul 20 2022 Matej Tyc <matyc@redhat.com> - 1.2.1-8
- Update translations
Resolves: rhbz#2062707
* Fri Jun 10 2022 Matej Tyc <matyc@redhat.com> - 1.2.1-7
- Remove the firstboot remediation feature completely.
We can't have it, while maintaining the standard UX.
Resolves: rhbz#2063179
* Mon Mar 21 2022 Matej Tyc <matyc@redhat.com> - 1.2.1-6
- Introduce the firstboot remediation
Resolves: rhbz#1834716
- Add better error handling of installation using unsupported installation sources
Resolves: rhbz#2007981
* Fri Jan 21 2022 Matej Tyc <matyc@redhat.com> - 1.2.1-5
- Updated translations
Resolves: rhbz#2017356
* Fri Aug 20 2021 Matej Tyc <matyc@redhat.com> - 1.2.1-4
- Updated translations
Resolves: rhbz#1962007
* Mon Aug 09 2021 Matej Tyc <matyc@redhat.com> - 1.2.1-3
- Fix handling of archives with directories in GUI installs
- Resolves: rhbz#1691305
* Tue Aug 03 2021 Matej Tyc <matyc@redhat.com> - 1.2.1-2
- Refactor content identification
- Resolves: rhbz#1989441
* Fri Jul 30 2021 Matej Tyc <matyc@redhat.com> - 1.2.1-1
- Rebase to the new upstream version.
- Resolves: rhbz#1691305
* Fri Jul 16 2021 Matej Tyc <matyc@redhat.com> - 1.2.0-2
- Updated translations
- Resolves: rhbz#1938623
* Fri Jun 25 2021 Matej Tyc <matyc@redhat.com> - 1.2.0-1
- Rebase to the new upstream version.
- Resolves: rhbz#1691305
* Mon Feb 15 2021 Matej Tyc <matyc@redhat.com> - 1.1.1-7
- Updated translations.
* Wed Nov 11 11:46:56 CET 2020 Matej Tyc <matyc@redhat.com> - 1.1.1-6
- Improved handling of conflicts between packages removed vs software wanted to be installed - rhbz#1892310
* Tue Aug 18 2020 Matěj Týč <matyc@redhat.com> - 1.1.1-5
- Fixed issues with encountering filenames with weird encoding during scans - rhbz#1867960
* Thu Jul 09 2020 Matěj Týč <matyc@redhat.com> - 1.1.1-4
- Fixed spoke window text: RHBZ#1855041
* Fri Jun 26 2020 Matěj Týč <matyc@redhat.com> - 1.1.1-3
- Updated translations: RHBZ#1820557
* Mon Jun 22 2020 Matěj Týč <matyc@redhat.com> - 1.1.1-2
- Fixed issues addressing combination of profiles and GUI-based software selections: RHBZ#1843932, RHBZ#1787156
- Improved handling of languages, capitalization: RHBZ#1696278
- Updated translations: RHBZ#1820557
* Tue Jun 02 2020 Matěj Týč <matyc@redhat.com> - 1.1.1-1
- Rebase to upstream 1.1.1
- This OAA is compatible with the RHEL 8.3 Anaconda: RHBZ#1696278
- The UX has been improved: RHBZ#1781790
* Mon Sep 02 2019 Watson Sato <wsato@redhat.com> - 1.0-10
- Do not use capital letters for spoke title: RHBZ#1744185
- Updated translations