From 869101fdea7f832dcb6cc382a1e82cf383aa84f4 Mon Sep 17 00:00:00 2001
From: Peter Macko <44851174+macko1@users.noreply.github.com>
Date: Fri, 1 May 2026 10:11:15 +0200
Subject: [PATCH 1/3] Revert changes and only refactor grub_bootloader_argument
OVAL template
---
.../grub2_bootloader_argument/oval.template | 705 +++++++++++++-----
1 file changed, 503 insertions(+), 202 deletions(-)
diff --git a/shared/templates/grub2_bootloader_argument/oval.template b/shared/templates/grub2_bootloader_argument/oval.template
index 480a4e6caee1..13b407ad4f4b 100644
--- a/shared/templates/grub2_bootloader_argument/oval.template
+++ b/shared/templates/grub2_bootloader_argument/oval.template
@@ -1,305 +1,583 @@
{{#-
- We set defaults to "off", and products should enable relevant ones depending on how the product configures grub.
- - /boot/loader/entries/* may not exist.
- - If they exist, they can reference variables defined in grubenv, or they can contain literal args
- - The grub cfg may either use those loader entries, or it can contain literal values as well
- - Kernel opts can be stored in /etc/default/grub so they are persistent between kernel upgrades
+ grub2_bootloader_argument OVAL template
+ ========================================
+
+ Checks that a kernel boot argument (e.g. audit=1) is set across all
+ relevant grub configuration files for the given product.
+
+ LOCATIONS WHERE KERNEL ARGS LIVE (per product):
+ ================================================
+
+ Product Boot entries Persistent config Other
+ --------------- ---------------------------------- ------------------------ ---------------------------
+ RHEL 9+, Fedora /boot/loader/entries/*.conf /etc/default/grub (bootc: kargs.d/*.toml)
+ (args inline on "options" line)
+ RHEL 8, OL8 /boot/loader/entries/*.conf /etc/default/grub
+ (args inline OR via $kernelopts)
+ + /boot/grub2/grubenv (kernelopts=)
+ OL7 /boot/grub2/grub.cfg (vmlinuz) /etc/default/grub
+ Ubuntu /boot/grub2/grub.cfg (vmlinuz) /etc/default/grub /etc/default/grub.d/*.cfg
+
+ WHAT THIS TEMPLATE DOES:
+ ========================
+
+ Kernel boot arguments (e.g. audit=1, pti=on, audit_backlog_limit=8192) are
+ settings passed to the Linux kernel at boot time via the bootloader (grub2).
+ Security policies require certain arguments to be set so the kernel enables
+ specific security features (auditing, memory protections, CPU mitigations, etc).
+
+ The problem is that grub2 stores these arguments in DIFFERENT files depending
+ on the OS version and platform. This template generates an OVAL check that
+ verifies the argument is present in ALL the right places for the given product.
+
+ KEY CONCEPTS:
+ =============
+
+ --- Boot entries (what the system actually boots) ---
+
+ /boot/loader/entries/*.conf (BLS = Boot Loader Specification entries)
+ One .conf file per installed kernel. Each has an "options" line with kernel args.
+ RHEL 9+/Fedora: args are always inline on the options line.
+ RHEL 8/OL8: args can be inline OR the entry can say "$kernelopts",
+ which grub expands from /boot/grub2/grubenv at boot time.
+
+ /boot/grub2/grubenv (RHEL 8 / OL8 only)
+ grub environment file. Contains "kernelopts=..." with the actual kernel args.
+ Only relevant when boot entries reference $kernelopts instead of listing args inline.
+
+ $kernelopts (RHEL 8 / OL8 only)
+ A grub environment variable. Boot entries can say "$kernelopts" on their options
+ line instead of listing args inline. grub expands it from grubenv at boot time.
+ On RHEL 9+ this indirection no longer exists -- args are always inline.
+
+ /boot/grub2/grub.cfg (OL7, Ubuntu only)
+ Generated grub config. Contains "linux /vmlinuz-... root=... arg=val ..." lines.
+ These platforms dont use BLS entries -- grub.cfg IS the boot config.
+
+ --- Persistent config (survives grub2-mkconfig regeneration) ---
+
+ /etc/default/grub (all products)
+ Persistent grub configuration. grub2-mkconfig reads this file and bakes the
+ values into grub.cfg or boot entries. Contains two relevant variables:
+ GRUB_CMDLINE_LINUX="..." -- args passed to ALL boot entries
+ GRUB_CMDLINE_LINUX_DEFAULT="..." -- args passed to non-recovery entries ONLY
+ If GRUB_DISABLE_RECOVERY=true is also set, there are no recovery entries,
+ so _DEFAULT effectively applies to all entries too.
+
+ /etc/default/grub.d/*.cfg (Ubuntu only)
+ Drop-in override files for /etc/default/grub. Same variables, same semantics.
+ Checked in addition to /etc/default/grub on Ubuntu.
+
+ --- bootc / RHEL Image Mode ---
+
+ bootc (RHEL Image Mode) is a way to run RHEL as an immutable container-based OS.
+ The root filesystem is a container image. There is no traditional grub config --
+ the bootloader is managed by bootc itself. Kernel args are defined in TOML files
+ shipped inside the image.
+
+ /usr/lib/bootc/kargs.d/*.toml
+ TOML files with kargs = ["arg=val", "arg2=val2"].
+ On bootc systems, grub files (/boot/loader/entries, grubenv, grub.cfg, etc.)
+ do NOT exist. Kernel args live here instead.
+ The OVAL definition has two mutually exclusive branches: one for bootc systems
+ (checks kargs.d) and one for normal grub systems (checks grub files).
+
+ CRITERIA TREE (what must pass for the definition to be compliant):
+ ==================================================================
+
+ definition (OR)
+ |
+ +-- [bootc] AND
+ | +-- extend_definition: IS bootc
+ | +-- test: kargs.d/*.toml has arg=value
+ |
+ +-- [normal grub] AND
+ +-- extend_definition: NOT bootc (if bootc supported)
+ |
+ +-- [RHEL 8 / OL8] entries check:
+ | +-- test: EACH /boot/loader/entries/*.conf has arg=value inline OR $kernelopts
+ | +-- (OR)
+ | +-- NO entry uses $kernelopts (skip grubenv)
+ | +-- grubenv has arg=value (BIOS or UEFI path)
+ |
+ +-- [RHEL 9+ / Fedora] entries check:
+ | +-- test: EACH /boot/loader/entries/*.conf has arg=value inline
+ |
+ +-- [OL7 / Ubuntu] grub.cfg check:
+ | +-- (OR) grub.cfg has arg=value (BIOS or UEFI path)
+ |
+ +-- [all products] /etc/default/grub check:
+ +-- (OR)
+ +-- GRUB_CMDLINE_LINUX has arg=value
+ | (+ grub.d drop-in on Ubuntu)
+ +-- AND
+ +-- GRUB_CMDLINE_LINUX_DEFAULT has arg=value
+ | (+ grub.d drop-in on Ubuntu)
+ +-- GRUB_DISABLE_RECOVERY=true
+
+ DATA FLOW (current -- will change in the rewrite):
+ ====================================================
+
+ Currently, objects extract the FULL options/cmdline line, and the shared
+ state uses a regex to check if arg=value appears as a word in that line.
+ This means all comparisons are "pattern match" on "string" datatype.
+
+ Object (extracts full line) State (regex checks arg=value in line)
+ ┌─────────────────────────┐ ┌────────────────────────────────────┐
+ │ /boot/loader/entries/ │ │ subexpression ~ "pattern match" │
+ │ ^options (.*)$ │────────>│ ^(?:.*\s)?arg=value(?:\s.*)?$ │
+ │ │ │ │
+ │ /boot/grub2/grubenv │ │ (same state, shared by all tests) │
+ │ ^kernelopts=(.*)$ │────────>│ │
+ │ │ └────────────────────────────────────┘
+ │ /etc/default/grub │ │
+ │ ^GRUB_CMDLINE_LINUX= │────────────────────────┘
+ │ "(.*)"$ │
+ └─────────────────────────┘
+
+ TARGET (after rewrite -- sysctl-style extract-then-compare):
+
+ Object (extracts JUST the value) State (native OVAL comparison)
+ ┌─────────────────────────┐ ┌────────────────────────────────────┐
+ │ /boot/loader/entries/ │ │ subexpression │
+ │ ^options.*arg=(\S+) │────────>│ operation="equals" │
+ │ │ │ datatype="int" │
+ │ /boot/grub2/grubenv │ │ value=8192 │
+ │ ^kernelopts.*arg= │────────>│ │
+ │ (\S+) │ │ (same state, shared by all tests) │
+ │ │ └────────────────────────────────────┘
+ │ /etc/default/grub │ │
+ │ ^GRUB_CMDLINE_LINUX= │────────────────────────┘
+ │ ".*arg=(\S+).*"$ │
+ └─────────────────────────┘
+
+ The check_* flags below control which locations are verified per product.
-#}}
-{{% set system_with_expanded_kernel_options_in_loader_entries = false -%}}
-{{% set system_with_kernel_options_in_grubenv = false -%}}
-{{% set system_with_expanded_kernel_options_in_loader_entries_or_with_options_in_grubenv = false -%}}
-{{% set system_with_kernel_options_in_etc_default_grub = true -%}}
-{{% set system_with_kernel_options_in_etc_default_grub_d = false -%}}
-{{% set system_with_expanded_kernel_options_in_grub_cfg = false -%}}
-{{% set system_with_bios_and_uefi_support = false -%}}
-
-{{% if product in ["fedora", "ol9", "rhel9", "rhel10"] -%}}
-{{% set system_with_expanded_kernel_options_in_loader_entries = true %}}
-{{%- endif -%}}
-
-{{% if product in ["ol8", "rhel8"] -%}}
-{{% set system_with_expanded_kernel_options_in_loader_entries_or_with_options_in_grubenv = true -%}}
-{{%- endif -%}}
-
-{{% if product in ["ol7"] or 'ubuntu' in product -%}}
-{{% set system_with_expanded_kernel_options_in_grub_cfg = true %}}
-{{%- endif -%}}
-
-{{% if 'ubuntu' in product -%}}
-{{% set system_with_kernel_options_in_etc_default_grub_d = true -%}}
-{{%- endif -%}}
-
-{{% if grub2_uefi_boot_path and grub2_uefi_boot_path != grub2_boot_path -%}}
-{{% set system_with_bios_and_uefi_support = true %}}
-{{%- endif -%}}
+{{% set check_boot_loader_entries = product in ["fedora", "ol9", "rhel9", "rhel10"] %}}
+{{% set check_boot_loader_entries_or_grubenv = product in ["ol8", "rhel8"] %}}
+{{% set check_etc_default_grub = true %}}
+{{% set check_etc_default_grub_d = 'ubuntu' in product %}}
+{{% set check_grub_cfg = product in ["ol7"] or 'ubuntu' in product %}}
+{{% set has_separate_bios_and_uefi = grub2_uefi_boot_path and grub2_uefi_boot_path != grub2_boot_path %}}
+
+{{# DEFINITION: {{{ _RULE_ID }}}
+ Class: compliance
+ Purpose: ensure the kernel boot argument {{{ ARG_NAME_VALUE }}} is set in all
+ relevant grub configuration files for this product.
+ Top-level OR: bootc branch vs normal grub branch.
+ Only one branch can be true at runtime (bootc XOR normal grub).
+ See the ASCII art criteria tree at the top of this file for the full picture. #}}
- {{{ oval_metadata("Ensure " + ARG_NAME_VALUE + " is configured in the kernel line in /etc/default/grub.", rule_title=rule_title) }}}
+ {{{ oval_metadata("Ensure " + ARG_NAME_VALUE + " is set as a kernel boot argument.", rule_title=rule_title) }}}
-
- {{% if bootable_containers_supported == "true" %}}
-
- {{% endif %}}
- {{% if system_with_expanded_kernel_options_in_loader_entries_or_with_options_in_grubenv %}}
-
-
-
- {{% if system_with_bios_and_uefi_support -%}}
-
- {{%- endif %}}
-
- {{% if system_with_bios_and_uefi_support -%}}
-
-
- {{%- endif %}}
-
- {{% elif system_with_kernel_options_in_grubenv -%}}
-
- {{% if system_with_bios_and_uefi_support -%}}
-
- {{%- endif %}}
-
- {{% if system_with_bios_and_uefi_support -%}}
-
+
+ {{%- if bootable_containers_supported == "true" %}}
+ {{# CRITERIA BRANCH: bootc / RHEL Image Mode
+ Platforms: RHEL 9+, RHEL 10 with bootc support (bootable_containers_supported)
+ The system must be bootc AND kargs.d/*.toml must contain {{{ ARG_NAME_VALUE }}}.
+ On non-bootc systems, the extend_definition fails and this whole branch is skipped. #}}
+
+
+
{{%- endif %}}
- {{% elif system_with_expanded_kernel_options_in_loader_entries -%}}
-
- {{%- endif %}}
- {{% if system_with_expanded_kernel_options_in_grub_cfg -%}}
- {{% if system_with_bios_and_uefi_support -%}}
+
+ {{# CRITERIA BRANCH: normal grub (non-bootc)
+ Platforms: all products
+ All sub-checks must pass: bootc guard, boot entries, and /etc/default/grub.
+ The specific sub-checks emitted depend on the check_* flags set per product. #}}
+
+
+ {{%- if bootable_containers_supported == "true" %}}
+ {{# EXTEND_DEFINITION: NOT bootc guard
+ Platforms: RHEL 9+, RHEL 10 with bootc support (bootable_containers_supported)
+ Fails this "normal grub" branch on bootc systems where grub files do not exist.
+ negate="true" -- passes only if the system is NOT bootc. #}}
+
+ {{%- endif %}}
+
+ {{%- if check_boot_loader_entries_or_grubenv %}}
+ {{# CRITERIA BRANCH: RHEL 8 / OL8 boot loader entries + $kernelopts + grubenv
+ Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
+ Two-part check:
+ 1. EACH /boot/loader/entries/*.conf (omit rescue) has {{{ ARG_NAME_VALUE }}}
+ inline on the options line OR contains $kernelopts.
+ 2. If any entry uses $kernelopts, then grubenv must also have {{{ ARG_NAME_VALUE }}}.
+ The $kernelopts indirection is an RHEL 8 mechanism: entries say "$kernelopts" and
+ grub expands it from /boot/grub2/grubenv at boot time. #}}
+
+
+ {{# CRITERIA: $kernelopts grubenv gate
+ Passes if no entry uses $kernelopts (all args on options line, grubenv irrelevant)
+ OR grubenv has {{{ ARG_NAME_VALUE }}} (BIOS or UEFI path). #}}
+
+
+ {{# CRITERION: no $kernelopts in any entry (negated)
+ If no entry uses $kernelopts, all args are on the options line and grubenv is irrelevant. #}}
+
+
+ {{# CRITERIA: grubenv has arg=value
+ BIOS: {grub2_boot_path}/grubenv (e.g. /boot/grub2/grubenv)
+ UEFI: {grub2_uefi_boot_path}/grubenv (e.g. /boot/efi/EFI/redhat/grubenv)
+ UEFI criterion only emitted when has_separate_bios_and_uefi is true. #}}
+
+ {{%- if has_separate_bios_and_uefi %}}
+
{{%- endif %}}
-
- {{% if system_with_bios_and_uefi_support -%}}
-
+
+ {{%- endif %}}
+
+ {{%- if check_boot_loader_entries %}}
+ {{# CRITERION: RHEL 9+ / Fedora / OL9 boot loader entries
+ Platforms: RHEL 9+, Fedora, OL9 (check_boot_loader_entries)
+ Checks: EACH /boot/loader/entries/*.conf (omit rescue) has {{{ ARG_NAME_VALUE }}}
+ directly on the options line. No $kernelopts indirection on these platforms. #}}
+
+ {{%- endif %}}
+
+ {{%- if check_grub_cfg %}}
+ {{# CRITERIA: OL7 / Ubuntu grub.cfg
+ Platforms: OL7, Ubuntu (check_grub_cfg)
+ BIOS: {grub2_boot_path}/grub.cfg (e.g. /boot/grub2/grub.cfg)
+ UEFI: {grub2_uefi_boot_path}/grub.cfg (e.g. /boot/efi/EFI/redhat/grub.cfg)
+ UEFI criterion only emitted when has_separate_bios_and_uefi is true. #}}
+
+
+ {{%- if has_separate_bios_and_uefi %}}
+
{{%- endif %}}
- {{%- endif %}}
- {{% if system_with_kernel_options_in_etc_default_grub -%}}
+
+ {{%- endif %}}
+
+ {{% if check_etc_default_grub %}}
+ {{# CRITERIA: /etc/default/grub persistent configuration
+ Platforms: all (check_etc_default_grub = true)
+ Passes if GRUB_CMDLINE_LINUX has {{{ ARG_NAME_VALUE }}} (applies to all entries),
+ OR GRUB_CMDLINE_LINUX_DEFAULT has it + GRUB_DISABLE_RECOVERY=true.
+ _DEFAULT only covers non-recovery boots, so disabling recovery ensures coverage. #}}
+
+ {{# CRITERIA: GRUB_CMDLINE_LINUX (applies to all entries)
+ Checks /etc/default/grub, plus grub.d drop-ins on Ubuntu. #}}
-
- {{% if system_with_kernel_options_in_etc_default_grub_d -%}}
-
- {{%- endif %}}
+
+ {{% if check_etc_default_grub_d %}}
+
+ {{% endif %}}
+
+ {{# CRITERIA: GRUB_CMDLINE_LINUX_DEFAULT + GRUB_DISABLE_RECOVERY=true
+ _DEFAULT only applies to non-recovery entries, so recovery must be disabled
+ to ensure the arg is on ALL boot entries. Checks /etc/default/grub,
+ plus grub.d drop-ins on Ubuntu. #}}
-
- {{% if system_with_kernel_options_in_etc_default_grub_d -%}}
-
- {{%- endif %}}
+
+ {{% if check_etc_default_grub_d %}}
+
+ {{% endif %}}
+ comment="Verify GRUB_DISABLE_RECOVERY=true in /etc/default/grub" />
- {{%- endif %}}
-
- {{% if bootable_containers_supported == "true" %}}
-
-
-
+ {{% endif %}}
+
- {{% endif %}}
+
-{{% if system_with_expanded_kernel_options_in_loader_entries_or_with_options_in_grubenv %}}
-
-
+
-
+ {{# Collect the "options" line from each /boot/loader/entries/*.conf.
+ Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
+ Each .conf file produces one collected item. Rescue entries (*rescue.conf) excluded.
+ Shared by two tests: the arg-or-$kernelopts check and the $kernelopts-presence check. #}}
+
/boot/loader/entries/
^.*\.conf$
+ {{# regex: ^options (.*)$ -- captures the full space-separated arg list after "options " #}}
^options (.*)$
1
- state_grub2_rescue_entry_for_{{{ _RULE_ID }}}
+ state_grub2_{{{ _RULE_ID }}}_is_rescue_entry
-
+ {{# Exclude filter: matches filenames ending in "rescue.conf".
+ Used by boot entry objects to skip rescue entries -- we only care about normal entries. #}}
+
+ {{# regex: .*rescue\.conf$ -- matches rescue entry filenames to exclude them #}}
.*rescue\.conf$
-
-
+
{{% endif %}}
-
-{{%- if system_with_kernel_options_in_etc_default_grub %}}
-
-
+
-
+ {{# Collect GRUB_CMDLINE_LINUX="..." from /etc/default/grub.
+ Platforms: all (check_etc_default_grub = true)
+ Extracts everything between the double quotes as subexpression. #}}
+
/etc/default/grub
+ {{# regex: ^\s*GRUB_CMDLINE_LINUX="(.*)"$ -- captures everything between the quotes #}}
^\s*GRUB_CMDLINE_LINUX="(.*)"$
1
-
-
+
-
+ {{# Collect GRUB_CMDLINE_LINUX_DEFAULT="..." from /etc/default/grub.
+ Platforms: all (check_etc_default_grub = true)
+ Extracts everything between the double quotes as subexpression.
+ Only passed to non-recovery boot entries by grub2-mkconfig. #}}
+
/etc/default/grub
+ {{# regex: ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ -- captures everything between the quotes #}}
^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$
1
{{%- endif %}}
-{{% if system_with_kernel_options_in_etc_default_grub_d -%}}
-
-
-
-
-
-
-
-
-
+{{%- if check_etc_default_grub_d %}}
+ {{# Check GRUB_CMDLINE_LINUX in /etc/default/grub.d/*.cfg drop-ins for {{{ ARG_NAME_VALUE }}}.
+ Platforms: Ubuntu (check_etc_default_grub_d)
+ Same as the /etc/default/grub check but from drop-in config files.
+ Passes if at least one drop-in has it (check="at least one"). #}}
+
+
+
+
-
- /etc/default/grub.d/[^/]+\.cfg
- ^\s*GRUB_CMDLINE_LINUX="(.*)"$
- 1
-
+ {{# Check GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub.d/*.cfg drop-ins for {{{ ARG_NAME_VALUE }}}.
+ Platforms: Ubuntu (check_etc_default_grub_d)
+ Same as the /etc/default/grub check but from drop-in config files.
+ Requires GRUB_DISABLE_RECOVERY=true (same as the non-drop-in variant). #}}
+
+
+
+
-
- /etc/default/grub.d/*.cfg
- ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$
- 1
-
+ {{# Collect GRUB_CMDLINE_LINUX="..." from /etc/default/grub.d/*.cfg drop-in files.
+ Platforms: Ubuntu (check_etc_default_grub_d)
+ Same regex as the /etc/default/grub object, but iterates over all .cfg drop-ins. #}}
+
+ {{# regex: /etc/default/grub.d/[^/]+\.cfg -- match .cfg drop-in files (Ubuntu) #}}
+ /etc/default/grub.d/[^/]+\.cfg
+ {{# regex: same as /etc/default/grub -- captures everything between the quotes #}}
+ ^\s*GRUB_CMDLINE_LINUX="(.*)"$
+ 1
+
+
+ {{# Collect GRUB_CMDLINE_LINUX_DEFAULT="..." from /etc/default/grub.d/*.cfg drop-in files.
+ Platforms: Ubuntu (check_etc_default_grub_d)
+ Same regex as the /etc/default/grub object, but iterates over all .cfg drop-ins.
+ Only applies to non-recovery entries (same as GRUB_CMDLINE_LINUX_DEFAULT). #}}
+
+ /etc/default/grub.d/*.cfg
+ {{# regex: same as /etc/default/grub -- captures everything between the quotes #}}
+ ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$
+ 1
+
{{%- endif %}}
-{{%- if system_with_kernel_options_in_grubenv or system_with_expanded_kernel_options_in_loader_entries_or_with_options_in_grubenv %}}
-{{%- macro test_and_object_for_kernel_options_grub_env(base_name, path) %}}
-
-
+
-
+ {{# Collect kernelopts=... from grubenv (everything after "kernelopts=").
+ This is the grub environment variable that boot entries expand via $kernelopts. #}}
+
{{{ path }}}
+ {{# regex: ^kernelopts=(.*)$ -- captures the full space-separated arg list from kernelopts #}}
^kernelopts=(.*)$
1
-{{%- endmacro %}}
+ {{%- endmacro %}}
-{{{ test_and_object_for_kernel_options_grub_env("grub2_" ~ SANITIZED_ARG_NAME ~ "_argument_grub_env", grub2_boot_path ~ "/grubenv") }}}
-{{% if system_with_bios_and_uefi_support -%}}
-{{{ test_and_object_for_kernel_options_grub_env("grub2_" ~ SANITIZED_ARG_NAME ~ "_argument_grub_env_uefi", grub2_uefi_boot_path ~ "/grubenv") }}}
-{{%- endif %}}
+ {{{- test_and_object_for_grubenv() }}}
+ {{%- if has_separate_bios_and_uefi -%}}
+ {{{- test_and_object_for_grubenv(variant="uefi") }}}
+ {{%- endif %}}
{{%- endif %}}
-{{%- if system_with_expanded_kernel_options_in_loader_entries %}}
+{{%- if check_boot_loader_entries %}}
+ {{# Check EACH /boot/loader/entries/*.conf (omit rescue) for {{{ ARG_NAME_VALUE }}} on options line.
+ Platforms: RHEL 9+, Fedora, OL9 (check_boot_loader_entries)
+ No $kernelopts indirection -- RHEL 9+ has all kernel args on the options line. #}}
-
+ {{# Collect the "options" line from each /boot/loader/entries/*.conf.
+ Platforms: RHEL 9+, Fedora, OL9 (check_boot_loader_entries)
+ Each .conf file produces one collected item. Rescue entries (*rescue.conf) excluded.
+ Unlike the RHEL 8 variant, no $kernelopts indirection -- args are always on the options line. #}}
+
/boot/loader/entries/
^.*\.conf$
+ {{# regex: ^options (.*)$ -- captures the full space-separated arg list after "options " #}}
^options (.*)$
1
- state_grub2_rescue_entry_for_{{{ _RULE_ID }}}
+ state_grub2_{{{ _RULE_ID }}}_is_rescue_entry
-
+ {{# Exclude filter: matches filenames ending in "rescue.conf" (same as above). #}}
+
+ {{# regex: .*rescue\.conf$ -- matches rescue entry filenames to exclude them #}}
.*rescue\.conf$
{{%- endif %}}
-{{%- if system_with_expanded_kernel_options_in_grub_cfg %}}
-{{%- macro test_and_object_for_kernel_options_grub_cfg(base_name, path) %}}
-
-
+
-
+ {{# Collect kernel command line from vmlinuz lines in grub.cfg.
+ Captures from "root=" onwards, which includes all kernel args.
+ Each vmlinuz line corresponds to one boot entry. #}}
+
{{{ path }}}
- {{% if product in ["ol7"] or 'ubuntu' in product %}}
- ^.*/vmlinuz.*(root=.*)$
- {{% else %}}
- ^set default_kernelopts=(.*)$
- {{% endif %}}
+ {{# regex: ^.*/vmlinuz.*(root=.*)$ -- captures from "root=" onwards on vmlinuz lines (OL7, Ubuntu) #}}
+ ^.*/vmlinuz.*(root=.*)$
1
-{{%- endmacro %}}
+ {{%- endmacro %}}
-{{{ test_and_object_for_kernel_options_grub_cfg("grub2_" + SANITIZED_ARG_NAME + "_argument_grub_cfg", grub2_boot_path ~ "/grub.cfg") }}}
-{{% if system_with_bios_and_uefi_support -%}}
-{{{ test_and_object_for_kernel_options_grub_cfg("grub2_" + SANITIZED_ARG_NAME + "_argument_grub_cfg_uefi", grub2_uefi_boot_path ~ "/grub.cfg") }}}
-{{%- endif %}}
+ {{{ test_and_object_for_grub_cfg("bios", grub2_boot_path ~ "/grub.cfg") }}}
+ {{%- if has_separate_bios_and_uefi -%}}
+ {{{- test_and_object_for_grub_cfg("uefi", grub2_uefi_boot_path ~ "/grub.cfg") }}}
+ {{%- endif %}}
{{%- endif %}}
-{{% if system_with_expanded_kernel_options_in_loader_entries_or_with_options_in_grubenv %}}
-
+{{% if check_boot_loader_entries_or_grubenv %}}
+ {{# Match "$kernelopts" as a whole word in the captured "options" line.
+ Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
+ $kernelopts is a grub env variable expanded at boot time from grubenv. #}}
+ {{# regex: ^(?:.*\s)?\$kernelopts(?:\s.*)?$ -- checks if $kernelopts appears as a word in the options line (RHEL 8 indirection) #}}
+
^(?:.*\s)?\$kernelopts(?:\s.*)?$
{{% endif %}}
-{{% if not ARG_VARIABLE %}}
-
- ^(?:.*\s)?{{{ ESCAPED_ARG_NAME_VALUE }}}(?:\s.*)?$
-
-{{% else %}}
-
+{{# Match {{{ ARG_NAME_VALUE }}} as a whole word in the captured options/kernelopts/cmdline line.
+ Shared by ALL grub-location tests (except bootc kargs.d which has its own state).
+ If ARG_VARIABLE is set, the regex is built at scan time from an XCCDF variable.
+ Otherwise, the expected value is hardcoded into the regex at build time.
+ NOTE: in the rewrite, this entire state + local_variable goes away --
+ objects will extract just the value, and the state will use operation+datatype natively. #}}
+{{% if ARG_VARIABLE %}}
+ {{# Value comes from XCCDF variable {{{ ARG_VARIABLE }}} -- regex built at scan time. #}}
+
+ {{# regex built at scan time via local_variable concat -- checks "arg=" as a word in the line #}}
+ {{# Build regex: ^(?:.*\s)?{ARG_NAME}=(?:\s.*)?$
+ Matches arg=value as a whole word. IS_SUBSTRING wraps value with \S* (partial match). #}}
^(?:.*\s)?{{{ ARG_NAME }}}=
@@ -314,31 +592,48 @@
-
+ {{# Expected value of {{{ ARG_NAME }}}, provided by XCCDF benchmark at scan time. #}}
+
+{{% else %}}
+ {{# Expected value hardcoded at build time: {{{ ESCAPED_ARG_NAME_VALUE }}} #}}
+
+ {{# regex: ^(?:.*\s)?{ARG_NAME_VALUE}(?:\s.*)?$ -- checks "arg=value" appears as a whole word in the full options line #}}
+ ^(?:.*\s)?{{{ ESCAPED_ARG_NAME_VALUE }}}(?:\s.*)?$
+
{{% endif %}}
-{{% if bootable_containers_supported == "true" %}}
+{{# Bootc / RHEL Image Mode: kernel args live in /usr/lib/bootc/kargs.d/*.toml instead of grub. #}}
+{{%- if bootable_containers_supported == "true" %}}
+ {{# Check /usr/lib/bootc/kargs.d/*.toml for {{{ ARG_NAME_VALUE }}} in the kargs array.
+ Platforms: RHEL 9+, RHEL 10 with bootc support (bootable_containers_supported)
+ Passes if at least one .toml file has it (check="at least one").
+ Has its own state (not shared) because TOML wraps values in quotes. #}}
-
+
-
+ {{# Collect kargs array from /usr/lib/bootc/kargs.d/*.toml.
+ Captures everything between the square brackets: kargs = ["arg=val", "arg2=val2"] #}}
+
/usr/lib/bootc/kargs.d/
^.*\.toml$
+ {{# regex: ^kargs = \[([^\]]+)\]$ -- captures contents of the TOML kargs array, e.g. "arg=val", "arg2=val2" #}}
^kargs = \[([^\]]+)\]$
1
-{{% if not ARG_VARIABLE %}}
-
- ^.*"{{{ ESCAPED_ARG_NAME_VALUE }}}".*$
-
-{{% else %}}
+ {{# Match {{{ ARG_NAME_VALUE }}} as a quoted string in the TOML kargs array (e.g. "arg=val").
+ Separate from state_argument because TOML wraps values in double quotes.
+ Same ARG_VARIABLE / hardcoded split as state_argument. #}}
+ {{%- if ARG_VARIABLE %}}
+ {{# Build regex: ^.*"{ARG_NAME}=".*$
+ Matches "arg=value" as a quoted string in TOML. IS_SUBSTRING wraps value with \S*. #}}
@@ -355,8 +650,14 @@
-
-{{% endif %}}
+ {{# Expected value of {{{ ARG_NAME }}}, same XCCDF variable as above. #}}
+
+ {{%- else %}}
+ {{# Expected value hardcoded at build time, with surrounding quotes for TOML format. #}}
+
+ ^.*"{{{ ESCAPED_ARG_NAME_VALUE }}}".*$
+
+ {{%- endif %}}
{{% endif %}}
From 9ea77852e2ca57e04ec5d8b2dbc734777aa4f623 Mon Sep 17 00:00:00 2001
From: Peter Macko <44851174+macko1@users.noreply.github.com>
Date: Fri, 1 May 2026 10:11:15 +0200
Subject: [PATCH 2/3] Refactor grub_bootloader_argument OVAL template 2
---
.../grub2_bootloader_argument/oval.template | 1006 +++++++++--------
1 file changed, 539 insertions(+), 467 deletions(-)
diff --git a/shared/templates/grub2_bootloader_argument/oval.template b/shared/templates/grub2_bootloader_argument/oval.template
index 13b407ad4f4b..a8917463b4d4 100644
--- a/shared/templates/grub2_bootloader_argument/oval.template
+++ b/shared/templates/grub2_bootloader_argument/oval.template
@@ -1,183 +1,311 @@
{{#-
- grub2_bootloader_argument OVAL template
+ `grub2_bootloader_argument` `OVAL` template
========================================
- Checks that a kernel boot argument (e.g. audit=1) is set across all
- relevant grub configuration files for the given product.
+ Checks that a kernel boot argument (e.g. `audit=1`) is set across all
+ relevant `grub` configuration files for the given product.
LOCATIONS WHERE KERNEL ARGS LIVE (per product):
================================================
- Product Boot entries Persistent config Other
- --------------- ---------------------------------- ------------------------ ---------------------------
- RHEL 9+, Fedora /boot/loader/entries/*.conf /etc/default/grub (bootc: kargs.d/*.toml)
- (args inline on "options" line)
- RHEL 8, OL8 /boot/loader/entries/*.conf /etc/default/grub
- (args inline OR via $kernelopts)
- + /boot/grub2/grubenv (kernelopts=)
- OL7 /boot/grub2/grub.cfg (vmlinuz) /etc/default/grub
- Ubuntu /boot/grub2/grub.cfg (vmlinuz) /etc/default/grub /etc/default/grub.d/*.cfg
+ Product Flags Boot entries Persistent config Other
+ --------------- ---------------------------------- ---------------------------------- ------------------------ ---------------------------
+ RHEL 9+, Fedora `uses_boot_loader_entries` `/boot/loader/entries/*.conf` `/etc/default/grub` (`bootc`: `kargs.d/*.toml`)
+ (args on `options` line)
+ RHEL 8, OL8 `uses_boot_loader_entries` `/boot/loader/entries/*.conf` `/etc/default/grub`
+ `uses_kernelopts` (args on `options` line or via
+ `$kernelopts` from `grubenv`)
+ + `/boot/grub2/grubenv`
+ OL7 `uses_grub_cfg` `/boot/grub2/grub.cfg` `/etc/default/grub`
+ (args on `vmlinuz` lines)
+ Ubuntu `uses_grub_cfg` `/boot/grub/grub.cfg` `/etc/default/grub` `/etc/default/grub.d/*.cfg`
+ `uses_etc_default_grub_d` (args on `vmlinuz` lines)
WHAT THIS TEMPLATE DOES:
========================
- Kernel boot arguments (e.g. audit=1, pti=on, audit_backlog_limit=8192) are
- settings passed to the Linux kernel at boot time via the bootloader (grub2).
+ Kernel boot arguments (e.g. `audit=1`, `pti=on`, `audit_backlog_limit=8192`) are
+ settings passed to the Linux kernel at boot time via the bootloader (`grub2`).
Security policies require certain arguments to be set so the kernel enables
- specific security features (auditing, memory protections, CPU mitigations, etc).
+ specific security features (auditing, memory protections, CPU mitigations, etc.).
- The problem is that grub2 stores these arguments in DIFFERENT files depending
- on the OS version and platform. This template generates an OVAL check that
- verifies the argument is present in ALL the right places for the given product.
+ The problem is that `grub2` stores these arguments in different files depending
+ on the OS version and platform. This template generates an `OVAL` check that
+ verifies the argument is present in all the right places for the given product.
KEY CONCEPTS:
=============
--- Boot entries (what the system actually boots) ---
- /boot/loader/entries/*.conf (BLS = Boot Loader Specification entries)
- One .conf file per installed kernel. Each has an "options" line with kernel args.
- RHEL 9+/Fedora: args are always inline on the options line.
- RHEL 8/OL8: args can be inline OR the entry can say "$kernelopts",
- which grub expands from /boot/grub2/grubenv at boot time.
-
- /boot/grub2/grubenv (RHEL 8 / OL8 only)
- grub environment file. Contains "kernelopts=..." with the actual kernel args.
- Only relevant when boot entries reference $kernelopts instead of listing args inline.
-
- $kernelopts (RHEL 8 / OL8 only)
- A grub environment variable. Boot entries can say "$kernelopts" on their options
- line instead of listing args inline. grub expands it from grubenv at boot time.
- On RHEL 9+ this indirection no longer exists -- args are always inline.
-
- /boot/grub2/grub.cfg (OL7, Ubuntu only)
- Generated grub config. Contains "linux /vmlinuz-... root=... arg=val ..." lines.
- These platforms dont use BLS entries -- grub.cfg IS the boot config.
-
- --- Persistent config (survives grub2-mkconfig regeneration) ---
-
- /etc/default/grub (all products)
- Persistent grub configuration. grub2-mkconfig reads this file and bakes the
- values into grub.cfg or boot entries. Contains two relevant variables:
- GRUB_CMDLINE_LINUX="..." -- args passed to ALL boot entries
- GRUB_CMDLINE_LINUX_DEFAULT="..." -- args passed to non-recovery entries ONLY
- If GRUB_DISABLE_RECOVERY=true is also set, there are no recovery entries,
- so _DEFAULT effectively applies to all entries too.
-
- /etc/default/grub.d/*.cfg (Ubuntu only)
- Drop-in override files for /etc/default/grub. Same variables, same semantics.
- Checked in addition to /etc/default/grub on Ubuntu.
-
- --- bootc / RHEL Image Mode ---
-
- bootc (RHEL Image Mode) is a way to run RHEL as an immutable container-based OS.
- The root filesystem is a container image. There is no traditional grub config --
- the bootloader is managed by bootc itself. Kernel args are defined in TOML files
+ `/boot/loader/entries/*.conf` (`BLS` = Boot Loader Specification entries)
+ `BLS` is a freedesktop.org standard: instead of one monolithic `grub.cfg` listing all
+ kernels, each installed kernel gets its own small `.conf` file with key-value lines
+ (`title`, `linux`, `initrd`, `options`), e.g.:
+ title Red Hat Enterprise Linux 9.3
+ linux /vmlinuz-5.14.0-362.el9.x86_64
+ initrd /initramfs-5.14.0-362.el9.x86_64.img
+ options root=/dev/mapper/rhel-root ro audit=1 pti=on
+ The `options` line is a space-separated list of kernel boot arguments passed
+ to the kernel at boot time. This is the line this template checks.
+ RHEL 9+/Fedora: kernel args are listed directly on the `options` line.
+ RHEL 8/OL8: kernel args are listed directly on the `options` line,
+ OR the `options` line contains `$kernelopts` -- a `grub` variable
+ that `grub` expands from `/boot/grub2/grubenv` at boot time.
+
+ `/boot/grub2/grubenv` (RHEL 8 / OL8 only)
+ Grub environment file. Contains a line like:
+ kernelopts=root=/dev/mapper/rhel-root ro audit=1 pti=on
+ Only relevant when `BLS` entries use `$kernelopts` on their `options` line
+ instead of listing kernel args directly.
+
+ `$kernelopts` (RHEL 8 / OL8 only)
+ `grub` can store named variables in `grubenv` (a `key=value` file on disk). A `BLS`
+ entry can reference a variable with `$name` on `options` line in `/boot/loader/entries/*.conf`, and `grub` substitutes the stored value at boot time -- similar to shell variable expansion.
+ On RHEL 8, the variable `$kernelopts` holds the kernel args. So a `/boot/loader/entries/*.conf` entry
+ might say:
+ options $kernelopts
+ and `grub` replaces `$kernelopts` with the `kernelopts=...` value from `grubenv`.
+ On RHEL 9+, this indirection no longer exists -- args are always listed directly.
+
+ `/boot/grub2/grub.cfg` (exists on all platforms, but content differs)
+ Generated by `grub2-mkconfig` from `/etc/default/grub`.
+ On OL7 and Ubuntu (non-`BLS` platforms), `grub.cfg` contains the actual kernel
+ boot lines with one `linux /vmlinuz-...` line per installed kernel, e.g.:
+ linux /vmlinuz-5.4.0-150-generic root=/dev/mapper/ubuntu-root ro audit=1
+ This template checks these `vmlinuz` lines for the required kernel argument.
+ On `BLS` platforms (RHEL 8+, Fedora), `grub.cfg` just contains a `blscfg`
+ directive that tells `grub` to load `BLS` entries from `/boot/loader/entries/`.
+ The kernel args are NOT in `grub.cfg` -- so this template does not check it.
+
+ --- Persistent config (survives `grub2-mkconfig` regeneration) ---
+
+ `/etc/default/grub` (all products)
+ Persistent `grub` configuration. When `grub2-mkconfig` runs (e.g. after kernel
+ install or `grub` update), it reads this file and writes the values into `grub.cfg`
+ or `BLS` boot entries. Contains two relevant shell variables:
+ GRUB_CMDLINE_LINUX="audit=1 pti=on" -- args added to ALL boot entries
+ GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" -- args added to non-recovery entries ONLY
+ If `GRUB_DISABLE_RECOVERY=true` is also set, there are no recovery entries,
+ so `_DEFAULT` effectively applies to all entries too.
+
+ `/etc/default/grub.d/*.cfg` (Ubuntu only)
+ Drop-in config files that supplement `/etc/default/grub`. Can also set
+ `GRUB_CMDLINE_LINUX` and `GRUB_CMDLINE_LINUX_DEFAULT`. `grub2-mkconfig`
+ reads these in addition to `/etc/default/grub`.
+
+ --- `bootc` / RHEL Image Mode ---
+
+ `bootc` (RHEL Image Mode) is a way to run RHEL as an immutable container-based OS.
+ The root filesystem is a container image. There is no traditional `grub` config --
+ the bootloader is managed by `bootc` itself. Kernel args are defined in `TOML` files
shipped inside the image.
- /usr/lib/bootc/kargs.d/*.toml
- TOML files with kargs = ["arg=val", "arg2=val2"].
- On bootc systems, grub files (/boot/loader/entries, grubenv, grub.cfg, etc.)
+ `/usr/lib/bootc/kargs.d/*.toml`
+ `TOML` files with `kargs = ["arg=val", "arg2=val2"]`.
+ On `bootc` systems, `grub` files (`/boot/loader/entries`, `grubenv`, `grub.cfg`, etc.)
do NOT exist. Kernel args live here instead.
- The OVAL definition has two mutually exclusive branches: one for bootc systems
- (checks kargs.d) and one for normal grub systems (checks grub files).
+ The `OVAL` definition has two mutually exclusive branches: one for `bootc` systems
+ (checks `kargs.d`) and one for normal `grub` systems (checks `grub` files).
CRITERIA TREE (what must pass for the definition to be compliant):
==================================================================
- definition (OR)
+ Products that support both Image Mode (`bootc`) and normal `grub` (e.g. RHEL 9+, RHEL 10)
+ emit BOTH branches. `oscap` evaluates both, but only one can pass -- the guards
+ (`IS bootc` / `NOT bootc`) ensure the wrong branch always fails.
+ Products without `bootc` support emit only the normal `grub` branch.
+
+ definition (OR) -- passes if EITHER branch matches the system
|
- +-- [bootc] AND
- | +-- extend_definition: IS bootc
- | +-- test: kargs.d/*.toml has arg=value
+ +-- [Image Mode] AND (bootable_containers_supported)
+ | +-- extend_definition: system IS `bootc`
+ | +-- test: `/usr/lib/bootc/kargs.d/*.toml` has arg=value
|
+-- [normal grub] AND
- +-- extend_definition: NOT bootc (if bootc supported)
+ +-- extend_definition: system is NOT `bootc` (bootable_containers_supported)
|
- +-- [RHEL 8 / OL8] entries check:
- | +-- test: EACH /boot/loader/entries/*.conf has arg=value inline OR $kernelopts
+ +-- [RHEL 8 / OL8] boot loader entries check (uses_kernelopts):
+ | +-- test: EACH `.conf` has arg=value on `options` line OR contains `$kernelopts`
| +-- (OR)
- | +-- NO entry uses $kernelopts (skip grubenv)
- | +-- grubenv has arg=value (BIOS or UEFI path)
+ | +-- NO entry uses `$kernelopts` (skip `grubenv`)
+ | +-- `grubenv` has arg=value (BIOS or UEFI path)
|
- +-- [RHEL 9+ / Fedora] entries check:
- | +-- test: EACH /boot/loader/entries/*.conf has arg=value inline
+ +-- [RHEL 9+ / Fedora] boot loader entries check (uses_boot_loader_entries, !uses_kernelopts):
+ | +-- test: EACH `.conf` has arg=value on `options` line
|
- +-- [OL7 / Ubuntu] grub.cfg check:
- | +-- (OR) grub.cfg has arg=value (BIOS or UEFI path)
+ +-- [OL7 / Ubuntu] `grub.cfg` check (uses_grub_cfg):
+ | +-- (OR) `grub.cfg` has arg=value (BIOS or UEFI path)
|
- +-- [all products] /etc/default/grub check:
+ +-- [all products] `/etc/default/grub` check:
+-- (OR)
- +-- GRUB_CMDLINE_LINUX has arg=value
- | (+ grub.d drop-in on Ubuntu)
+ +-- `GRUB_CMDLINE_LINUX` has arg=value
+ | (+ `grub.d` drop-in on Ubuntu)
+-- AND
- +-- GRUB_CMDLINE_LINUX_DEFAULT has arg=value
- | (+ grub.d drop-in on Ubuntu)
- +-- GRUB_DISABLE_RECOVERY=true
-
- DATA FLOW (current -- will change in the rewrite):
- ====================================================
-
- Currently, objects extract the FULL options/cmdline line, and the shared
- state uses a regex to check if arg=value appears as a word in that line.
- This means all comparisons are "pattern match" on "string" datatype.
-
- Object (extracts full line) State (regex checks arg=value in line)
- ┌─────────────────────────┐ ┌────────────────────────────────────┐
- │ /boot/loader/entries/ │ │ subexpression ~ "pattern match" │
- │ ^options (.*)$ │────────>│ ^(?:.*\s)?arg=value(?:\s.*)?$ │
- │ │ │ │
- │ /boot/grub2/grubenv │ │ (same state, shared by all tests) │
- │ ^kernelopts=(.*)$ │────────>│ │
- │ │ └────────────────────────────────────┘
- │ /etc/default/grub │ │
- │ ^GRUB_CMDLINE_LINUX= │────────────────────────┘
- │ "(.*)"$ │
- └─────────────────────────┘
-
- TARGET (after rewrite -- sysctl-style extract-then-compare):
-
- Object (extracts JUST the value) State (native OVAL comparison)
- ┌─────────────────────────┐ ┌────────────────────────────────────┐
- │ /boot/loader/entries/ │ │ subexpression │
- │ ^options.*arg=(\S+) │────────>│ operation="equals" │
- │ │ │ datatype="int" │
- │ /boot/grub2/grubenv │ │ value=8192 │
- │ ^kernelopts.*arg= │────────>│ │
- │ (\S+) │ │ (same state, shared by all tests) │
- │ │ └────────────────────────────────────┘
- │ /etc/default/grub │ │
- │ ^GRUB_CMDLINE_LINUX= │────────────────────────┘
- │ ".*arg=(\S+).*"$ │
- └─────────────────────────┘
-
- The check_* flags below control which locations are verified per product.
+ +-- `GRUB_CMDLINE_LINUX_DEFAULT` has arg=value
+ | (+ `grub.d` drop-in on Ubuntu)
+ +-- `GRUB_DISABLE_RECOVERY=true`
+
+ DATA FLOW:
+ ==========
+
+ Each object extracts the FULL line (or array) as `subexpression`.
+ Each state uses `pattern match` to check if `arg=value` appears in that captured text.
+ All `grub`-location tests share one state; `bootc` has its own (quotes around value).
+
+ OBJECTS STATES
+ ═══════ ══════
+
+ ┌─ /boot/loader/entries/ ──────────────────┐
+ │ path: /boot/loader/entries/ │
+ │ filename: ^.*\.conf$ │ ┌─ state_grub2_{ARG}_argument ───────────────┐
+ │ pattern: ^options (.*)$ │─>│ │
+ │ filter: exclude *rescue.conf │ │ ^(?:.*\s)?{ARG_NAME_VALUE}(?:\s.*)?$ │
+ │ (uses_boot_loader_entries) │ │ │
+ └──────────────────────────────────────────┘ │ Shared by ALL objects in this column. │
+ │ Pattern match: is `{ARG_NAME_VALUE}` │
+ ┌─ {grub2_boot_path}/grubenv ─────────────┐ │ present as a standalone word in the │
+ │ filepath: {grub2_boot_path}/grubenv │ │ captured line? The regex enforces │
+ │ pattern: ^kernelopts=(.*)$ │─>│ whitespace/start/end boundaries. │
+ │ (uses_kernelopts) │ │ │
+ └──────────────────────────────────────────┘ │ When `ARG_VARIABLE` is set, the regex │
+ │ cannot be hardcoded at build time (the │
+ │ value is not known yet). Instead, `oscap` │
+ │ assembles it at runtime: an `external_ │
+ │ variable` receives the value from the │
+ │ `XCCDF` profile, a `local_variable` uses │
+ │ `` to join fixed regex fragments │
+ │ around that value, and the state references │
+ │ the resulting string via `var_ref`. │
+ │ │
+ ┌─ {grub2_boot_path}/grub.cfg ────────────┐ │ │
+ │ filepath: {grub2_boot_path}/grub.cfg │ │ │
+ │ pattern: ^.*/vmlinuz.*(root=.*)$ │─>│ │
+ │ (uses_grub_cfg) │ │ │
+ └──────────────────────────────────────────┘ │ │
+ │ │
+ ┌─ /etc/default/grub ─────────────────────┐ │ │
+ │ filepath: /etc/default/grub │ │ │
+ │ pattern: ^\s*GRUB_CMDLINE_LINUX= │ │ │
+ │ "(.*)"$ │─>│ │
+ │ (all products) │ │ │
+ │ + same for GRUB_CMDLINE_LINUX_DEFAULT │─>│ │
+ └──────────────────────────────────────────┘ │ │
+ │ │
+ ┌─ /etc/default/grub.d/*.cfg ─────────────┐ │ │
+ │ filepath: /etc/default/grub.d/[^/]+\.cfg│ │ │
+ │ pattern: ^\s*GRUB_CMDLINE_LINUX= │ │ │
+ │ "(.*)"$ │─>│ │
+ │ (uses_etc_default_grub_d / Ubuntu) │ │ │
+ │ + same for GRUB_CMDLINE_LINUX_DEFAULT │─>│ │
+ └──────────────────────────────────────────┘ └─────────────────────────────────────────────┘
+
+ ┌─ /usr/lib/bootc/kargs.d/*.toml ─────────┐ ┌─ state_grub2_{ARG}_usr_lib_bootc_kargs_d ──┐
+ │ path: /usr/lib/bootc/kargs.d/ │ │ │
+ │ filename: ^.*\.toml$ │ │ ^.*"{ARG_NAME_VALUE}".*$ │
+ │ pattern: ^kargs = \[([^\]]+)\]$ │─>│ │
+ │ (bootable_containers_supported) │ │ Separate state: `TOML` wraps values in │
+ └──────────────────────────────────────────┘ │ double quotes. │
+ └─────────────────────────────────────────────┘
+
+ (uses_kernelopts) -- additional state used with `state_operator="OR"`:
+ ┌─ state_grub2_{ARG}_argument_is_kernelopts ─┐
+ │ ^(?:.*\s)?\$kernelopts(?:\s.*)?$ │
+ │ Matches `$kernelopts` as a word. │
+ │ The RHEL 8 BLS test references BOTH │
+ │ `state_argument` and this state with OR: │
+ │ entry passes if it has arg=value OR │
+ │ contains `$kernelopts`. │
+ │ (Same flag as the `grubenv` object -- │
+ │ both exist only when `$kernelopts` │
+ │ indirection is in play.) │
+ └─────────────────────────────────────────────┘
+
+ REGEX SEMANTICS (what each `pattern` captures as `subexpression`):
+ ─────────────────────────────────────────────────────────────────
+
+ 1. ^options (.*)$
+ Source: `/boot/loader/entries/*.conf`
+ Input: `options root=UUID=abc ro quiet audit_backlog_limit=8192`
+ Capture: `root=UUID=abc ro quiet audit_backlog_limit=8192`
+ (everything after `options ` -- the full space-separated kernel arg list)
+
+ 2. ^kernelopts=(.*)$
+ Source: `{grub2_boot_path}/grubenv`
+ Input: `kernelopts=root=UUID=abc ro quiet audit_backlog_limit=8192`
+ Capture: `root=UUID=abc ro quiet audit_backlog_limit=8192`
+ (everything after `kernelopts=` -- same format as #1)
+
+ 3. ^.*/vmlinuz.*(root=.*)$
+ Source: `{grub2_boot_path}/grub.cfg`
+ Input: ` linux /vmlinuz-5.14 root=UUID=abc ro quiet audit_backlog_limit=8192`
+ Capture: `root=UUID=abc ro quiet audit_backlog_limit=8192`
+ NOTE: the second `.*` is greedy -- it backtracks to the LAST `root=` in the line.
+ Only arguments AT or AFTER `root=` are captured. Arguments before `root=` are invisible.
+
+ 4. ^\s*GRUB_CMDLINE_LINUX="(.*)"$
+ Source: `/etc/default/grub` (or `/etc/default/grub.d/*.cfg`)
+ Input: `GRUB_CMDLINE_LINUX="ro quiet audit_backlog_limit=8192"`
+ Capture: `ro quiet audit_backlog_limit=8192`
+ (everything between the double quotes -- the full arg list)
+ Same pattern for `GRUB_CMDLINE_LINUX_DEFAULT`.
+
+ 5. ^kargs = \[([^\]]+)\]$
+ Source: `/usr/lib/bootc/kargs.d/*.toml`
+ Input: `kargs = ["audit_backlog_limit=8192", "nosmt"]`
+ Capture: `"audit_backlog_limit=8192", "nosmt"`
+ (everything between `[` and `]` -- quoted comma-separated values)
+
+ STATE MATCHING (applied to the captured `subexpression`):
+ ─────────────────────────────────────────────────────────
+
+ state_grub2_{ARG}_argument:
+ ^(?:.*\s)?{ARG_NAME_VALUE}(?:\s.*)?$
+ Checks if `arg=value` appears as a WHOLE WORD (bounded by start/end or whitespace).
+ Example: `^(?:.*\s)?audit_backlog_limit=8192(?:\s.*)?$`
+ Matches: `root=UUID=abc ro quiet audit_backlog_limit=8192` (at end)
+ Matches: `audit_backlog_limit=8192 ro quiet` (at start)
+ Matches: `ro audit_backlog_limit=8192 quiet` (in middle)
+ NO match: `foo_audit_backlog_limit=8192` (not word-bounded on left)
+
+ state_grub2_{ARG}_usr_lib_bootc_kargs_d:
+ ^.*"{ARG_NAME_VALUE}".*$
+ Checks if `"arg=value"` appears anywhere (double-quoted, as per `TOML` syntax).
+ Example: `^.*"audit_backlog_limit=8192".*$`
+ Matches: `"audit_backlog_limit=8192", "nosmt"`
+
+ state_grub2_{ARG}_argument_is_kernelopts:
+ ^(?:.*\s)?\$kernelopts(?:\s.*)?$
+ Checks if `$kernelopts` appears as a whole word (same word-boundary logic).
+ Used by RHEL 8 BLS test with `state_operator="OR"`: entry passes if it has
+ the arg itself OR contains `$kernelopts` (which gets expanded at boot time).
+
+ The `uses_*` flags control which locations are verified per product.
-#}}
-{{% set check_boot_loader_entries = product in ["fedora", "ol9", "rhel9", "rhel10"] %}}
-{{% set check_boot_loader_entries_or_grubenv = product in ["ol8", "rhel8"] %}}
-{{% set check_etc_default_grub = true %}}
-{{% set check_etc_default_grub_d = 'ubuntu' in product %}}
-{{% set check_grub_cfg = product in ["ol7"] or 'ubuntu' in product %}}
-{{% set has_separate_bios_and_uefi = grub2_uefi_boot_path and grub2_uefi_boot_path != grub2_boot_path %}}
-
-{{# DEFINITION: {{{ _RULE_ID }}}
- Class: compliance
- Purpose: ensure the kernel boot argument {{{ ARG_NAME_VALUE }}} is set in all
- relevant grub configuration files for this product.
- Top-level OR: bootc branch vs normal grub branch.
- Only one branch can be true at runtime (bootc XOR normal grub).
- See the ASCII art criteria tree at the top of this file for the full picture. #}}
+{{% set uses_boot_loader_entries = product in ["fedora", "ol8", "ol9", "rhel8", "rhel9", "rhel10"] %}}
+{{% set uses_kernelopts = product in ["ol8", "rhel8"] %}}
+{{% set uses_etc_default_grub_d = 'ubuntu' in product %}}
+{{% set uses_grub_cfg = product in ["ol7"] or 'ubuntu' in product %}}
+{{% set has_separate_bios_and_uefi = grub2_uefi_boot_path and grub2_uefi_boot_path != grub2_boot_path %}}
+
+{{# DEFINITION: `{{{ _RULE_ID }}}`
+ Class: `compliance`
+ Ensure the kernel boot argument {{{ ARG_NAME_VALUE }}} is set in all
+ relevant `grub` configuration files for this product.
+ Top-level `OR`: `bootc` branch vs. normal `grub` branch.
+ Only one branch can be true at runtime (`bootc` XOR normal `grub`).
+ See the ASCII-art criteria tree at the top of this file for the full picture. #}}
{{{ oval_metadata("Ensure " + ARG_NAME_VALUE + " is set as a kernel boot argument.", rule_title=rule_title) }}}
{{%- if bootable_containers_supported == "true" %}}
- {{# CRITERIA BRANCH: bootc / RHEL Image Mode
- Platforms: RHEL 9+, RHEL 10 with bootc support (bootable_containers_supported)
- The system must be bootc AND kargs.d/*.toml must contain {{{ ARG_NAME_VALUE }}}.
- On non-bootc systems, the extend_definition fails and this whole branch is skipped. #}}
+ {{# CRITERIA BRANCH: `bootc` / RHEL Image Mode
+ Platforms: RHEL 9+, RHEL 10 with `bootc` support (`bootable_containers_supported`)
+ The system must be `bootc` AND `kargs.d/*.toml` must contain {{{ ARG_NAME_VALUE }}}.
+ On non-`bootc` systems, the `extend_definition` fails and this whole branch is skipped. #}}
{{%- endif %}}
- {{# CRITERIA BRANCH: normal grub (non-bootc)
+ {{# CRITERIA BRANCH: normal `grub` (non-`bootc`)
Platforms: all products
- All sub-checks must pass: bootc guard, boot entries, and /etc/default/grub.
- The specific sub-checks emitted depend on the check_* flags set per product. #}}
+ All sub-checks must pass: `bootc` guard, boot entries, and `/etc/default/grub`.
+ The specific sub-checks emitted depend on the `uses_*` flags set per product. #}}
{{%- if bootable_containers_supported == "true" %}}
- {{# EXTEND_DEFINITION: NOT bootc guard
- Platforms: RHEL 9+, RHEL 10 with bootc support (bootable_containers_supported)
- Fails this "normal grub" branch on bootc systems where grub files do not exist.
- negate="true" -- passes only if the system is NOT bootc. #}}
+ {{# EXTEND_DEFINITION: NOT `bootc` guard
+ Platforms: RHEL 9+, RHEL 10 with `bootc` support (`bootable_containers_supported`)
+ Fails this "normal `grub`" branch on `bootc` systems where `grub` files do not exist.
+ `negate="true"` -- passes only if the system is NOT `bootc`. #}}
{{%- endif %}}
- {{%- if check_boot_loader_entries_or_grubenv %}}
- {{# CRITERIA BRANCH: RHEL 8 / OL8 boot loader entries + $kernelopts + grubenv
- Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
+ {{%- if uses_kernelopts %}}
+ {{# CRITERIA BRANCH: RHEL 8 / OL8 boot loader entries + `$kernelopts` + `grubenv`
+ Platforms: RHEL 8, OL8 (`uses_kernelopts`)
Two-part check:
- 1. EACH /boot/loader/entries/*.conf (omit rescue) has {{{ ARG_NAME_VALUE }}}
- inline on the options line OR contains $kernelopts.
- 2. If any entry uses $kernelopts, then grubenv must also have {{{ ARG_NAME_VALUE }}}.
- The $kernelopts indirection is an RHEL 8 mechanism: entries say "$kernelopts" and
- grub expands it from /boot/grub2/grubenv at boot time. #}}
+ 1. EACH `/boot/loader/entries/*.conf` (omit rescue) has {{{ ARG_NAME_VALUE }}}
+ on the `options` line OR contains `$kernelopts`.
+ 2. If any entry uses `$kernelopts`, then `grubenv` must also have {{{ ARG_NAME_VALUE }}}.
+ The `$kernelopts` indirection is an RHEL 8 mechanism: entries say `$kernelopts` and
+ `grub` expands it from `/boot/grub2/grubenv` at boot time. #}}
- {{# CRITERIA: $kernelopts grubenv gate
- Passes if no entry uses $kernelopts (all args on options line, grubenv irrelevant)
- OR grubenv has {{{ ARG_NAME_VALUE }}} (BIOS or UEFI path). #}}
+ {{# CRITERIA: `$kernelopts` / `grubenv` gate
+ Passes if no entry uses `$kernelopts` (all args on `options` line, `grubenv` irrelevant)
+ OR `grubenv` has {{{ ARG_NAME_VALUE }}} (BIOS or UEFI path). #}}
- {{# CRITERION: no $kernelopts in any entry (negated)
- If no entry uses $kernelopts, all args are on the options line and grubenv is irrelevant. #}}
+ {{# CRITERION: no `$kernelopts` in any entry (negated)
+ If no entry uses `$kernelopts`, all args are on the `options` line and `grubenv` is irrelevant. #}}
- {{# CRITERIA: grubenv has arg=value
- BIOS: {grub2_boot_path}/grubenv (e.g. /boot/grub2/grubenv)
- UEFI: {grub2_uefi_boot_path}/grubenv (e.g. /boot/efi/EFI/redhat/grubenv)
- UEFI criterion only emitted when has_separate_bios_and_uefi is true. #}}
+ {{# CRITERIA: `grubenv` has arg=value
+ BIOS: `{grub2_boot_path}/grubenv` (e.g. `/boot/grub2/grubenv`)
+ UEFI: `{grub2_uefi_boot_path}/grubenv` (e.g. `/boot/efi/EFI/redhat/grubenv`)
+ UEFI criterion only emitted when `has_separate_bios_and_uefi` is true. #}}
@@ -237,21 +365,21 @@
{{%- endif %}}
- {{%- if check_boot_loader_entries %}}
+ {{%- if uses_boot_loader_entries and not uses_kernelopts %}}
{{# CRITERION: RHEL 9+ / Fedora / OL9 boot loader entries
- Platforms: RHEL 9+, Fedora, OL9 (check_boot_loader_entries)
- Checks: EACH /boot/loader/entries/*.conf (omit rescue) has {{{ ARG_NAME_VALUE }}}
- directly on the options line. No $kernelopts indirection on these platforms. #}}
+ Platforms: RHEL 9+, Fedora, OL9 (`uses_boot_loader_entries`, not `uses_kernelopts`)
+ Each `/boot/loader/entries/*.conf` (omit rescue) must have {{{ ARG_NAME_VALUE }}}
+ directly on the `options` line. No `$kernelopts` indirection on these platforms. #}}
{{%- endif %}}
- {{%- if check_grub_cfg %}}
- {{# CRITERIA: OL7 / Ubuntu grub.cfg
- Platforms: OL7, Ubuntu (check_grub_cfg)
- BIOS: {grub2_boot_path}/grub.cfg (e.g. /boot/grub2/grub.cfg)
- UEFI: {grub2_uefi_boot_path}/grub.cfg (e.g. /boot/efi/EFI/redhat/grub.cfg)
- UEFI criterion only emitted when has_separate_bios_and_uefi is true. #}}
+ {{%- if uses_grub_cfg %}}
+ {{# CRITERIA: OL7 / Ubuntu `grub.cfg`
+ Platforms: OL7, Ubuntu (`uses_grub_cfg`)
+ BIOS: `{grub2_boot_path}/grub.cfg` (e.g. `/boot/grub2/grub.cfg`)
+ UEFI: `{grub2_uefi_boot_path}/grub.cfg` (e.g. `/boot/efi/EFI/redhat/grub.cfg`)
+ UEFI criterion only emitted when `has_separate_bios_and_uefi` is true. #}}
@@ -262,34 +390,33 @@
{{%- endif %}}
- {{% if check_etc_default_grub %}}
- {{# CRITERIA: /etc/default/grub persistent configuration
- Platforms: all (check_etc_default_grub = true)
- Passes if GRUB_CMDLINE_LINUX has {{{ ARG_NAME_VALUE }}} (applies to all entries),
- OR GRUB_CMDLINE_LINUX_DEFAULT has it + GRUB_DISABLE_RECOVERY=true.
- _DEFAULT only covers non-recovery boots, so disabling recovery ensures coverage. #}}
+ {{# CRITERIA: `/etc/default/grub` persistent configuration
+ Platforms: all
+ Passes if `GRUB_CMDLINE_LINUX` has {{{ ARG_NAME_VALUE }}} (applies to all entries),
+ OR `GRUB_CMDLINE_LINUX_DEFAULT` has it + `GRUB_DISABLE_RECOVERY=true`.
+ `_DEFAULT` only covers non-recovery boots, so disabling recovery ensures coverage. #}}
- {{# CRITERIA: GRUB_CMDLINE_LINUX (applies to all entries)
- Checks /etc/default/grub, plus grub.d drop-ins on Ubuntu. #}}
+ {{# CRITERIA: `GRUB_CMDLINE_LINUX` (applies to all entries)
+ Checks `/etc/default/grub`, plus `grub.d` drop-ins on Ubuntu. #}}
- {{% if check_etc_default_grub_d %}}
+ {{% if uses_etc_default_grub_d %}}
{{% endif %}}
- {{# CRITERIA: GRUB_CMDLINE_LINUX_DEFAULT + GRUB_DISABLE_RECOVERY=true
- _DEFAULT only applies to non-recovery entries, so recovery must be disabled
- to ensure the arg is on ALL boot entries. Checks /etc/default/grub,
- plus grub.d drop-ins on Ubuntu. #}}
+ {{# CRITERIA: `GRUB_CMDLINE_LINUX_DEFAULT` + `GRUB_DISABLE_RECOVERY=true`
+ `_DEFAULT` only applies to non-recovery entries, so recovery must be disabled
+ to ensure the arg is on ALL boot entries. Checks `/etc/default/grub`,
+ plus `grub.d` drop-ins on Ubuntu. #}}
- {{% if check_etc_default_grub_d %}}
+ {{% if uses_etc_default_grub_d %}}
{{% endif %}}
@@ -298,284 +425,121 @@
comment="Verify GRUB_DISABLE_RECOVERY=true in /etc/default/grub" />
- {{% endif %}}
+{{# OBJECTS, TESTS, STATES, VARIABLES #}}
+{{# --- MACROS --- #}}
-
-{{# TESTS #}}
-{{% if check_boot_loader_entries_or_grubenv %}}
- {{# Check that EACH /boot/loader/entries/*.conf (omit rescue) has {{{ ARG_NAME_VALUE }}}
- on the options line OR references $kernelopts (grub env variable).
- Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
- Extracts the full "options" line as subexpression.
- state_operator="OR" because entries may have args on options line or via $kernelopts. #}}
-
-
-
-
-
-
- {{# Collect the "options" line from each /boot/loader/entries/*.conf.
- Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
- Each .conf file produces one collected item. Rescue entries (*rescue.conf) excluded.
- Shared by two tests: the arg-or-$kernelopts check and the $kernelopts-presence check. #}}
-
- /boot/loader/entries/
- ^.*\.conf$
- {{# regex: ^options (.*)$ -- captures the full space-separated arg list after "options " #}}
- ^options (.*)$
- 1
- state_grub2_{{{ _RULE_ID }}}_is_rescue_entry
-
-
- {{# Exclude filter: matches filenames ending in "rescue.conf".
- Used by boot entry objects to skip rescue entries -- we only care about normal entries. #}}
-
- {{# regex: .*rescue\.conf$ -- matches rescue entry filenames to exclude them #}}
- .*rescue\.conf$
-
-
- {{# Check if at least one /boot/loader/entries/*.conf references $kernelopts.
- Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
- Uses the same object as above. Used negated in criteria: if NO entry has $kernelopts,
- all args are on the options line and the grubenv check is skipped. #}}
-
-
-
-
-{{% endif %}}
-
-{{%- if check_etc_default_grub %}}
- {{# Check GRUB_CMDLINE_LINUX in /etc/default/grub for {{{ ARG_NAME_VALUE }}}.
- Platforms: all (check_etc_default_grub = true)
- GRUB_CMDLINE_LINUX is passed to ALL boot entries by grub2-mkconfig. #}}
-
-
-
-
-
- {{# Collect GRUB_CMDLINE_LINUX="..." from /etc/default/grub.
- Platforms: all (check_etc_default_grub = true)
- Extracts everything between the double quotes as subexpression. #}}
-
- /etc/default/grub
- {{# regex: ^\s*GRUB_CMDLINE_LINUX="(.*)"$ -- captures everything between the quotes #}}
- ^\s*GRUB_CMDLINE_LINUX="(.*)"$
- 1
-
-
- {{# Check GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub for {{{ ARG_NAME_VALUE }}}.
- Platforms: all (check_etc_default_grub = true)
- _DEFAULT only applies to non-recovery entries, so criteria requires
- GRUB_DISABLE_RECOVERY=true alongside this test. #}}
-
-
-
-
-
- {{# Collect GRUB_CMDLINE_LINUX_DEFAULT="..." from /etc/default/grub.
- Platforms: all (check_etc_default_grub = true)
- Extracts everything between the double quotes as subexpression.
- Only passed to non-recovery boot entries by grub2-mkconfig. #}}
-
- /etc/default/grub
- {{# regex: ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ -- captures everything between the quotes #}}
- ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$
+{{# MACRO (OBJECT + TEST): Collect a `grub` shell variable (`GRUB_CMDLINE_LINUX` or
+ `GRUB_CMDLINE_LINUX_DEFAULT`) from `/etc/default/grub` or `/etc/default/grub.d/*.cfg`
+ drop-ins, and check it for {{{ ARG_NAME_VALUE }}}.
+ Parameters:
+ `grub_var`: `grub` config variable (`GRUB_CMDLINE_LINUX` or `GRUB_CMDLINE_LINUX_DEFAULT`)
+ `from_grub_d`: if true, collect from `/etc/default/grub.d/*.cfg` instead of `/etc/default/grub`
+ `check`: `OVAL` `check` attribute (`all` or `at least one`) #}}
+{{%- macro etc_default_grub_object_and_test(grub_var, from_grub_d=false, check="all") %}}
+ {{%- set name = "grub2_" ~ SANITIZED_ARG_NAME ~ "_" ~ grub_var|lower ~ ("_from_grub_d" if from_grub_d else "") -%}}
+ {{%- set source = "/etc/default/grub.d/*.cfg" if from_grub_d else "/etc/default/grub" -%}}
+
+ {{%- if from_grub_d %}}
+ {{# `operation="pattern match"` is required -- without it `OVAL` defaults to `equals`,
+ and no file is literally named `*.cfg`. #}}
+ /etc/default/grub.d/[^/]+\.cfg
+ {{%- else %}}
+ {{{ source }}}
+ {{%- endif %}}
+ ^\s*{{{ grub_var }}}="(.*)"$
1
-{{%- endif %}}
-{{%- if check_etc_default_grub_d %}}
- {{# Check GRUB_CMDLINE_LINUX in /etc/default/grub.d/*.cfg drop-ins for {{{ ARG_NAME_VALUE }}}.
- Platforms: Ubuntu (check_etc_default_grub_d)
- Same as the /etc/default/grub check but from drop-in config files.
- Passes if at least one drop-in has it (check="at least one"). #}}
-
-
-
-
-
- {{# Check GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub.d/*.cfg drop-ins for {{{ ARG_NAME_VALUE }}}.
- Platforms: Ubuntu (check_etc_default_grub_d)
- Same as the /etc/default/grub check but from drop-in config files.
- Requires GRUB_DISABLE_RECOVERY=true (same as the non-drop-in variant). #}}
-
-
-
-
-
- {{# Collect GRUB_CMDLINE_LINUX="..." from /etc/default/grub.d/*.cfg drop-in files.
- Platforms: Ubuntu (check_etc_default_grub_d)
- Same regex as the /etc/default/grub object, but iterates over all .cfg drop-ins. #}}
-
- {{# regex: /etc/default/grub.d/[^/]+\.cfg -- match .cfg drop-in files (Ubuntu) #}}
- /etc/default/grub.d/[^/]+\.cfg
- {{# regex: same as /etc/default/grub -- captures everything between the quotes #}}
- ^\s*GRUB_CMDLINE_LINUX="(.*)"$
- 1
-
-
- {{# Collect GRUB_CMDLINE_LINUX_DEFAULT="..." from /etc/default/grub.d/*.cfg drop-in files.
- Platforms: Ubuntu (check_etc_default_grub_d)
- Same regex as the /etc/default/grub object, but iterates over all .cfg drop-ins.
- Only applies to non-recovery entries (same as GRUB_CMDLINE_LINUX_DEFAULT). #}}
-
- /etc/default/grub.d/*.cfg
- {{# regex: same as /etc/default/grub -- captures everything between the quotes #}}
- ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$
- 1
-
-{{%- endif %}}
-
-{{%- if check_boot_loader_entries_or_grubenv %}}
- {{# Check grubenv for {{{ ARG_NAME_VALUE }}} in the kernelopts= line.
- Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
- BIOS: {grub2_boot_path}/grubenv (e.g. /boot/grub2/grubenv)
- UEFI: {grub2_uefi_boot_path}/grubenv (e.g. /boot/efi/EFI/redhat/grubenv)
- UEFI variant only emitted when has_separate_bios_and_uefi is true.
- Used when boot loader entries reference $kernelopts instead of listing args on options line. #}}
- {{%- macro test_and_object_for_grubenv(variant="") %}}
- {{%- set path = (grub2_uefi_boot_path if variant == "uefi" else grub2_boot_path) ~ "/grubenv" -%}}
- {{%- set name = "grub2_" ~ SANITIZED_ARG_NAME ~ "_in_grubenv" ~ ("_uefi" if variant == "uefi" else "") -%}}
+ comment="Check {{{ grub_var }}} in {{{ source }}} for {{{ ARG_NAME_VALUE }}}"
+ check="{{{ check }}}" check_existence="all_exist" version="1">
-
- {{# Collect kernelopts=... from grubenv (everything after "kernelopts=").
- This is the grub environment variable that boot entries expand via $kernelopts. #}}
+{{%- endmacro %}}
+
+{{# MACRO (OBJECT + TEST): Check `grubenv` for {{{ ARG_NAME_VALUE }}} in the `kernelopts=` line.
+ Platforms: RHEL 8, OL8 (`uses_kernelopts`).
+ BIOS: `{grub2_boot_path}/grubenv` (e.g. `/boot/grub2/grubenv`)
+ UEFI: `{grub2_uefi_boot_path}/grubenv` (e.g. `/boot/efi/EFI/redhat/grubenv`)
+ UEFI variant only emitted when `has_separate_bios_and_uefi` is true.
+ Used when boot loader entries reference `$kernelopts` instead of listing args on the `options` line. #}}
+{{%- macro test_and_object_for_grubenv(variant="") %}}
+ {{%- set path = (grub2_uefi_boot_path if variant == "uefi" else grub2_boot_path) ~ "/grubenv" -%}}
+ {{%- set name = "grub2_" ~ SANITIZED_ARG_NAME ~ "_in_grubenv" ~ ("_uefi" if variant == "uefi" else "") -%}}
+ {{# OBJECT: Collect `kernelopts=...` from `grubenv` (everything after `kernelopts=`).
+ This is the `grub` environment variable that boot entries expand via `$kernelopts`. #}}
{{{ path }}}
- {{# regex: ^kernelopts=(.*)$ -- captures the full space-separated arg list from kernelopts #}}
+ {{# regex: ^kernelopts=(.*)$ -- captures the full space-separated arg list from `kernelopts` #}}
^kernelopts=(.*)$
1
- {{%- endmacro %}}
-
- {{{- test_and_object_for_grubenv() }}}
- {{%- if has_separate_bios_and_uefi -%}}
- {{{- test_and_object_for_grubenv(variant="uefi") }}}
- {{%- endif %}}
-{{%- endif %}}
-
-{{%- if check_boot_loader_entries %}}
- {{# Check EACH /boot/loader/entries/*.conf (omit rescue) for {{{ ARG_NAME_VALUE }}} on options line.
- Platforms: RHEL 9+, Fedora, OL9 (check_boot_loader_entries)
- No $kernelopts indirection -- RHEL 9+ has all kernel args on the options line. #}}
-
-
-
-
-
- {{# Collect the "options" line from each /boot/loader/entries/*.conf.
- Platforms: RHEL 9+, Fedora, OL9 (check_boot_loader_entries)
- Each .conf file produces one collected item. Rescue entries (*rescue.conf) excluded.
- Unlike the RHEL 8 variant, no $kernelopts indirection -- args are always on the options line. #}}
-
- /boot/loader/entries/
- ^.*\.conf$
- {{# regex: ^options (.*)$ -- captures the full space-separated arg list after "options " #}}
- ^options (.*)$
- 1
- state_grub2_{{{ _RULE_ID }}}_is_rescue_entry
-
-
- {{# Exclude filter: matches filenames ending in "rescue.conf" (same as above). #}}
-
- {{# regex: .*rescue\.conf$ -- matches rescue entry filenames to exclude them #}}
- .*rescue\.conf$
-
-{{%- endif %}}
-{{%- if check_grub_cfg %}}
- {{# Check grub.cfg vmlinuz lines for {{{ ARG_NAME_VALUE }}} in the kernel command line.
- Platforms: OL7, Ubuntu (check_grub_cfg)
- BIOS: {grub2_boot_path}/grub.cfg (e.g. /boot/grub2/grub.cfg)
- UEFI: {grub2_uefi_boot_path}/grub.cfg (e.g. /boot/efi/EFI/redhat/grub.cfg)
- UEFI variant only emitted when has_separate_bios_and_uefi is true. #}}
- {{%- macro test_and_object_for_grub_cfg(variant, path) %}}
- {{% set name = "grub2_" ~ SANITIZED_ARG_NAME ~ "_in_grub_cfg" ~ ("_uefi" if variant == "uefi" else "") -%}}
+ {{# TEST: Check `grubenv` `kernelopts` line for {{{ ARG_NAME_VALUE }}}.
+ Uses the shared `state_argument` regex. #}}
-
- {{# Collect kernel command line from vmlinuz lines in grub.cfg.
- Captures from "root=" onwards, which includes all kernel args.
- Each vmlinuz line corresponds to one boot entry. #}}
+{{%- endmacro %}}
+
+{{# MACRO (OBJECT + TEST): Check `grub.cfg` `vmlinuz` lines for {{{ ARG_NAME_VALUE }}} in the kernel command line.
+ Platforms: OL7, Ubuntu (`uses_grub_cfg`).
+ BIOS: `{grub2_boot_path}/grub.cfg` (e.g. `/boot/grub2/grub.cfg`)
+ UEFI: `{grub2_uefi_boot_path}/grub.cfg` (e.g. `/boot/efi/EFI/redhat/grub.cfg`)
+ UEFI variant only emitted when `has_separate_bios_and_uefi` is true. #}}
+{{%- macro test_and_object_for_grub_cfg(variant, path) %}}
+ {{% set name = "grub2_" ~ SANITIZED_ARG_NAME ~ "_in_grub_cfg" ~ ("_uefi" if variant == "uefi" else "") -%}}
+ {{# OBJECT: Collect kernel command line from `vmlinuz` lines in `grub.cfg`.
+ Captures from `root=` onwards, which includes all kernel args.
+ Each `vmlinuz` line corresponds to one boot entry. #}}
{{{ path }}}
- {{# regex: ^.*/vmlinuz.*(root=.*)$ -- captures from "root=" onwards on vmlinuz lines (OL7, Ubuntu) #}}
+ {{# regex: ^.*/vmlinuz.*(root=.*)$ -- captures from `root=` onwards on `vmlinuz` lines (OL7, Ubuntu) #}}
^.*/vmlinuz.*(root=.*)$
1
- {{%- endmacro %}}
- {{{ test_and_object_for_grub_cfg("bios", grub2_boot_path ~ "/grub.cfg") }}}
- {{%- if has_separate_bios_and_uefi -%}}
- {{{- test_and_object_for_grub_cfg("uefi", grub2_uefi_boot_path ~ "/grub.cfg") }}}
- {{%- endif %}}
-{{%- endif %}}
+ {{# TEST: Check `grub.cfg` `vmlinuz` lines for {{{ ARG_NAME_VALUE }}}.
+ Uses the shared `state_argument` regex. #}}
+
+
+
+
+{{%- endmacro %}}
-{{% if check_boot_loader_entries_or_grubenv %}}
- {{# Match "$kernelopts" as a whole word in the captured "options" line.
- Platforms: RHEL 8, OL8 (check_boot_loader_entries_or_grubenv)
- $kernelopts is a grub env variable expanded at boot time from grubenv. #}}
- {{# regex: ^(?:.*\s)?\$kernelopts(?:\s.*)?$ -- checks if $kernelopts appears as a word in the options line (RHEL 8 indirection) #}}
-
- ^(?:.*\s)?\$kernelopts(?:\s.*)?$
-
-{{% endif %}}
+{{# --- states + variables --- #}}
+{{# Defines `state_grub2_{ARG}_argument` (used by all `grub`-location tests) and, when
+ `bootc` is supported, `state_grub2_{ARG}_usr_lib_bootc_kargs_d` (used by the `bootc` test).
+ `ARG_VARIABLE` set: the expected value comes from an `XCCDF` variable -- `oscap` builds the
+ regex at runtime by concatenating `literal_component` and `variable_component`.
+ `ARG_VARIABLE` unset: the expected value is hardcoded into the regex at build time. #}}
-{{# Match {{{ ARG_NAME_VALUE }}} as a whole word in the captured options/kernelopts/cmdline line.
- Shared by ALL grub-location tests (except bootc kargs.d which has its own state).
- If ARG_VARIABLE is set, the regex is built at scan time from an XCCDF variable.
- Otherwise, the expected value is hardcoded into the regex at build time.
- NOTE: in the rewrite, this entire state + local_variable goes away --
- objects will extract just the value, and the state will use operation+datatype natively. #}}
{{% if ARG_VARIABLE %}}
- {{# Value comes from XCCDF variable {{{ ARG_VARIABLE }}} -- regex built at scan time. #}}
-
- {{# regex built at scan time via local_variable concat -- checks "arg=" as a word in the line #}}
-
-
+ {{# EXTERNAL VARIABLE: Expected value of `{{{ ARG_NAME }}}`, provided by the `XCCDF` benchmark when `oscap` runs.
+ Referenced by `local_variable` regex builders for both `grub` state and `bootc` `kargs` state. #}}
+
- {{# Build regex: ^(?:.*\s)?{ARG_NAME}=(?:\s.*)?$
- Matches arg=value as a whole word. IS_SUBSTRING wraps value with \S* (partial match). #}}
+ {{# VARIABLE: Build regex by concatenating fixed strings (`literal_component`) and the
+ `XCCDF` variable value (`variable_component`, substituted when `oscap` runs).
+ Result (e.g. `{{{ ARG_NAME }}}` with value `8192`):
+ `IS_SUBSTRING` false: `^(?:.*\s)?{{{ ARG_NAME }}}=8192(?:\s.*)?$` (exact match)
+ `IS_SUBSTRING` true: `^(?:.*\s)?{{{ ARG_NAME }}}=\S*8192\S*(?:\s.*)?$` (partial match) #}}
@@ -592,50 +556,19 @@
- {{# Expected value of {{{ ARG_NAME }}}, provided by XCCDF benchmark at scan time. #}}
-
-{{% else %}}
- {{# Expected value hardcoded at build time: {{{ ESCAPED_ARG_NAME_VALUE }}} #}}
+ {{# STATE (XCCDF variant): Value comes from `XCCDF` variable `{{{ ARG_VARIABLE }}}` -- regex assembled when `oscap` runs. #}}
- {{# regex: ^(?:.*\s)?{ARG_NAME_VALUE}(?:\s.*)?$ -- checks "arg=value" appears as a whole word in the full options line #}}
- ^(?:.*\s)?{{{ ESCAPED_ARG_NAME_VALUE }}}(?:\s.*)?$
-
-{{% endif %}}
-
-{{# Bootc / RHEL Image Mode: kernel args live in /usr/lib/bootc/kargs.d/*.toml instead of grub. #}}
-{{%- if bootable_containers_supported == "true" %}}
- {{# Check /usr/lib/bootc/kargs.d/*.toml for {{{ ARG_NAME_VALUE }}} in the kargs array.
- Platforms: RHEL 9+, RHEL 10 with bootc support (bootable_containers_supported)
- Passes if at least one .toml file has it (check="at least one").
- Has its own state (not shared) because TOML wraps values in quotes. #}}
-
-
-
-
- {{# Collect kargs array from /usr/lib/bootc/kargs.d/*.toml.
- Captures everything between the square brackets: kargs = ["arg=val", "arg2=val2"] #}}
-
- /usr/lib/bootc/kargs.d/
- ^.*\.toml$
- {{# regex: ^kargs = \[([^\]]+)\]$ -- captures contents of the TOML kargs array, e.g. "arg=val", "arg2=val2" #}}
- ^kargs = \[([^\]]+)\]$
- 1
-
- {{# Match {{{ ARG_NAME_VALUE }}} as a quoted string in the TOML kargs array (e.g. "arg=val").
- Separate from state_argument because TOML wraps values in double quotes.
- Same ARG_VARIABLE / hardcoded split as state_argument. #}}
- {{%- if ARG_VARIABLE %}}
-
-
+
- {{# Build regex: ^.*"{ARG_NAME}=".*$
- Matches "arg=value" as a quoted string in TOML. IS_SUBSTRING wraps value with \S*. #}}
+ {{%- if bootable_containers_supported == "true" %}}
+ {{# VARIABLE: Build regex for `bootc` `TOML` format (values wrapped in double quotes).
+ Concatenates `literal_component` (fixed strings) and `variable_component` (`XCCDF` value).
+ Result (e.g. `{{{ ARG_NAME }}}` with value `8192`):
+ `IS_SUBSTRING` false: `^.*"{{{ ARG_NAME }}}=8192".*$` (exact match in `TOML`)
+ `IS_SUBSTRING` true: `^.*"{{{ ARG_NAME }}}=\S*8192\S*".*$` (partial match in `TOML`) #}}
^.*"{{{ ARG_NAME }}}=
@@ -650,14 +583,153 @@
- {{# Expected value of {{{ ARG_NAME }}}, same XCCDF variable as above. #}}
-
- {{%- else %}}
- {{# Expected value hardcoded at build time, with surrounding quotes for TOML format. #}}
+ {{# STATE (XCCDF variant): `bootc` `kargs` state -- regex assembled when `oscap` runs, from `{{{ ARG_VARIABLE }}}`. #}}
+
+
+
+ {{%- endif %}}
+
+{{% else %}}
+ {{# STATE (hardcoded variant): Expected value hardcoded at build time: `{{{ ESCAPED_ARG_NAME_VALUE }}}` #}}
+
+ {{# regex: ^(?:.*\s)?{ARG_NAME_VALUE}(?:\s.*)?$ -- checks `arg=value` appears as a whole word in the full `options` line #}}
+ ^(?:.*\s)?{{{ ESCAPED_ARG_NAME_VALUE }}}(?:\s.*)?$
+
+
+ {{%- if bootable_containers_supported == "true" %}}
+ {{# STATE (hardcoded variant): Expected value hardcoded at build time, with surrounding quotes for `TOML` format. #}}
^.*"{{{ ESCAPED_ARG_NAME_VALUE }}}".*$
{{%- endif %}}
+
+{{% endif %}}
+
+{{# --- /etc/default/grub (all products) --- #}}
+
+{{{ etc_default_grub_object_and_test("GRUB_CMDLINE_LINUX") }}}
+{{{ etc_default_grub_object_and_test("GRUB_CMDLINE_LINUX_DEFAULT") }}}
+
+
+{{% if uses_boot_loader_entries %}}
+ {{# OBJECT: Collect the `options` line from each `/boot/loader/entries/*.conf`.
+ Platforms: all `BLS` platforms (`uses_boot_loader_entries`).
+ Each `.conf` file produces one collected item. Rescue entries (`*rescue.conf`) excluded.
+ Shared by RHEL 8 (arg-or-`$kernelopts` + `$kernelopts`-presence) and RHEL 9+ (arg-only) tests. #}}
+
+ /boot/loader/entries/
+ ^.*\.conf$
+ {{# regex: ^options (.*)$ -- captures the full space-separated arg list after `options` #}}
+ ^options (.*)$
+ 1
+ state_grub2_{{{ _RULE_ID }}}_is_rescue_entry
+
+
+ {{# FILTER STATE: Exclude filenames ending in `rescue.conf`.
+ Boot entry objects use this filter to skip rescue entries. #}}
+
+ {{# regex: .*rescue\.conf$ -- matches rescue entry filenames to exclude them #}}
+ .*rescue\.conf$
+
{{% endif %}}
+{{% if uses_kernelopts %}}
+ {{# STATE: Match `$kernelopts` as a whole word in the captured `options` line.
+ Platforms: RHEL 8, OL8 (`uses_kernelopts`).
+ `$kernelopts` is a `grub` environment variable expanded at boot time from `grubenv`. #}}
+ {{# regex: ^(?:.*\s)?\$kernelopts(?:\s.*)?$ -- checks if `$kernelopts` appears as a word in the `options` line (RHEL 8 indirection) #}}
+
+ ^(?:.*\s)?\$kernelopts(?:\s.*)?$
+
+
+ {{# TEST: Check that EACH `/boot/loader/entries/*.conf` (omit rescue) has {{{ ARG_NAME_VALUE }}}
+ on the `options` line OR references `$kernelopts`.
+ Platforms: RHEL 8, OL8 (`uses_kernelopts`).
+ Extracts the full `options` line as `subexpression`.
+ `state_operator="OR"` because entries may have args on the `options` line or via `$kernelopts`. #}}
+
+
+
+
+
+
+ {{# TEST: Check if at least one `/boot/loader/entries/*.conf` references `$kernelopts`.
+ Platforms: RHEL 8, OL8 (`uses_kernelopts`).
+ Reuses `obj_grub2_{ARG}_entries`. Used negated in criteria: if NO entry has `$kernelopts`,
+ all args are on the `options` line and the `grubenv` check is skipped. #}}
+
+
+
+
+
+ {{# Emit BIOS variant unconditionally; emit UEFI variant only when the platform
+ has separate BIOS and UEFI `grubenv` paths (e.g. `/boot/grub2/` vs. `/boot/efi/EFI/.../`). #}}
+ {{{- test_and_object_for_grubenv() }}}
+ {{%- if has_separate_bios_and_uefi -%}}
+ {{{- test_and_object_for_grubenv(variant="uefi") }}}
+ {{%- endif %}}
+{{% endif %}}
+
+{{%- if uses_etc_default_grub_d %}}
+ {{# `/etc/default/grub.d/*.cfg` drop-in files (Ubuntu).
+ `GRUB_CMDLINE_LINUX` and `GRUB_CMDLINE_LINUX_DEFAULT` from drop-in config files.
+ `GRUB_CMDLINE_LINUX` uses `check="at least one"` -- passes if any drop-in has the arg. #}}
+ {{{ etc_default_grub_object_and_test("GRUB_CMDLINE_LINUX", from_grub_d=true, check="at least one") }}}
+ {{{ etc_default_grub_object_and_test("GRUB_CMDLINE_LINUX_DEFAULT", from_grub_d=true) }}}
+{{%- endif %}}
+
+{{%- if uses_boot_loader_entries and not uses_kernelopts %}}
+ {{# TEST: Check each `/boot/loader/entries/*.conf` (omit rescue) for {{{ ARG_NAME_VALUE }}} on the `options` line.
+ Platforms: RHEL 9+, Fedora, OL9 (`uses_boot_loader_entries`, not `uses_kernelopts`).
+ No `$kernelopts` indirection -- RHEL 9+ lists all kernel args directly on the `options` line. #}}
+
+
+
+
+{{%- endif %}}
+
+{{%- if uses_grub_cfg %}}
+ {{# Emit BIOS variant unconditionally; emit UEFI variant only when the platform
+ has separate BIOS and UEFI `grub.cfg` paths (e.g. `/boot/grub2/` vs. `/boot/efi/EFI/.../`). #}}
+ {{{ test_and_object_for_grub_cfg("bios", grub2_boot_path ~ "/grub.cfg") }}}
+ {{%- if has_separate_bios_and_uefi -%}}
+ {{{- test_and_object_for_grub_cfg("uefi", grub2_uefi_boot_path ~ "/grub.cfg") }}}
+ {{%- endif %}}
+{{%- endif %}}
+
+{{# --- `bootc` / RHEL Image Mode: object + test (ARG_VARIABLE-independent) --- #}}
+
+{{%- if bootable_containers_supported == "true" %}}
+ {{# OBJECT: Collect `kargs` array from `/usr/lib/bootc/kargs.d/*.toml`.
+ Captures everything between the square brackets: `kargs = ["arg=val", "arg2=val2"]` #}}
+
+ /usr/lib/bootc/kargs.d/
+ ^.*\.toml$
+ {{# regex: ^kargs = \[([^\]]+)\]$ -- captures contents of the `TOML` `kargs` array, e.g. `"arg=val", "arg2=val2"` #}}
+ ^kargs = \[([^\]]+)\]$
+ 1
+
+
+ {{# TEST: Check `/usr/lib/bootc/kargs.d/*.toml` for {{{ ARG_NAME_VALUE }}} in the `kargs` array.
+ Platforms: RHEL 9+, RHEL 10 with `bootc` support (`bootable_containers_supported`).
+ Passes if at least one `.toml` file has it (`check="at least one"`).
+ Has its own state (not shared) because `TOML` wraps values in quotes. #}}
+
+
+
+
+{{% endif %}}
+
+
From 8df1575a2dbaba2e4f907f4a6374ad0047225cf7 Mon Sep 17 00:00:00 2001
From: Peter Macko <44851174+macko1@users.noreply.github.com>
Date: Wed, 6 May 2026 17:04:58 +0200
Subject: [PATCH 3/3] Make comments more accessible for screen readers.
---
.../grub2_bootloader_argument/oval.template | 152 +++++++++++++++++-
1 file changed, 144 insertions(+), 8 deletions(-)
diff --git a/shared/templates/grub2_bootloader_argument/oval.template b/shared/templates/grub2_bootloader_argument/oval.template
index a8917463b4d4..568d1f04c417 100644
--- a/shared/templates/grub2_bootloader_argument/oval.template
+++ b/shared/templates/grub2_bootloader_argument/oval.template
@@ -8,6 +8,8 @@
LOCATIONS WHERE KERNEL ARGS LIVE (per product):
================================================
+ [VISUAL TABLE: products vs. config file locations, aligned in columns.]
+
Product Flags Boot entries Persistent config Other
--------------- ---------------------------------- ---------------------------------- ------------------------ ---------------------------
RHEL 9+, Fedora `uses_boot_loader_entries` `/boot/loader/entries/*.conf` `/etc/default/grub` (`bootc`: `kargs.d/*.toml`)
@@ -21,6 +23,31 @@
Ubuntu `uses_grub_cfg` `/boot/grub/grub.cfg` `/etc/default/grub` `/etc/default/grub.d/*.cfg`
`uses_etc_default_grub_d` (args on `vmlinuz` lines)
+ LONG DESCRIPTION (text equivalent of the table):
+
+ RHEL 9+, Fedora:
+ Flags: `uses_boot_loader_entries`
+ Boot entries: `/boot/loader/entries/*.conf` (args on `options` line)
+ Persistent config: `/etc/default/grub`
+ Other: `bootc` systems use `/usr/lib/bootc/kargs.d/*.toml` instead
+
+ RHEL 8, OL8:
+ Flags: `uses_boot_loader_entries`, `uses_kernelopts`
+ Boot entries: `/boot/loader/entries/*.conf` (args on `options` line or via
+ `$kernelopts` indirection from `/boot/grub2/grubenv`)
+ Persistent config: `/etc/default/grub`
+
+ OL7:
+ Flags: `uses_grub_cfg`
+ Boot entries: `/boot/grub2/grub.cfg` (args on `vmlinuz` lines)
+ Persistent config: `/etc/default/grub`
+
+ Ubuntu:
+ Flags: `uses_grub_cfg`, `uses_etc_default_grub_d`
+ Boot entries: `/boot/grub/grub.cfg` (args on `vmlinuz` lines)
+ Persistent config: `/etc/default/grub`
+ Other: `/etc/default/grub.d/*.cfg` (drop-in config files)
+
WHAT THIS TEMPLATE DOES:
========================
@@ -116,6 +143,9 @@
(`IS bootc` / `NOT bootc`) ensure the wrong branch always fails.
Products without `bootc` support emit only the normal `grub` branch.
+ [VISUAL TREE: indented AND/OR hierarchy showing the two branches (Image Mode vs.
+ normal grub) and all sub-checks within the normal grub branch.]
+
definition (OR) -- passes if EITHER branch matches the system
|
+-- [Image Mode] AND (bootable_containers_supported)
@@ -146,6 +176,38 @@
| (+ `grub.d` drop-in on Ubuntu)
+-- `GRUB_DISABLE_RECOVERY=true`
+ LONG DESCRIPTION (text equivalent of the criteria tree):
+
+ The definition passes (compliant) if EITHER of two branches is true:
+
+ Branch 1 -- Image Mode (only emitted when `bootable_containers_supported`):
+ Both must be true:
+ - The system IS a `bootc` deployment.
+ - `/usr/lib/bootc/kargs.d/*.toml` contains `arg=value`.
+
+ Branch 2 -- normal grub:
+ All of the following must be true:
+ - The system is NOT a `bootc` deployment (guard; only when `bootable_containers_supported`).
+ - [RHEL 8 / OL8, `uses_kernelopts`]:
+ EACH `/boot/loader/entries/*.conf` (excluding rescue) has `arg=value`
+ on its `options` line, OR contains `$kernelopts`.
+ AND EITHER no entry references `$kernelopts` (so `grubenv` is irrelevant),
+ OR `{grub2_boot_path}/grubenv` contains `arg=value`.
+ - [RHEL 9+ / Fedora, `uses_boot_loader_entries` without `uses_kernelopts`]:
+ EACH `/boot/loader/entries/*.conf` (excluding rescue) has `arg=value`
+ on its `options` line.
+ - [OL7 / Ubuntu, `uses_grub_cfg`]:
+ `{grub2_boot_path}/grub.cfg` has `arg=value` (BIOS path, or UEFI path if separate).
+ - [all products]:
+ EITHER `GRUB_CMDLINE_LINUX` in `/etc/default/grub` has `arg=value`
+ (plus `grub.d` drop-in on Ubuntu),
+ OR BOTH `GRUB_CMDLINE_LINUX_DEFAULT` has `arg=value`
+ (plus `grub.d` drop-in on Ubuntu)
+ AND `GRUB_DISABLE_RECOVERY=true`.
+
+ Only the sub-checks relevant to the product (controlled by `uses_*` flags) are emitted.
+ Branches 1 and 2 are mutually exclusive at runtime due to the `IS bootc` / `NOT bootc` guards.
+
DATA FLOW:
==========
@@ -153,6 +215,10 @@
Each state uses `pattern match` to check if `arg=value` appears in that captured text.
All `grub`-location tests share one state; `bootc` has its own (quotes around value).
+ [VISUAL DIAGRAM: 6 object boxes on the left (one per config file location) with
+ arrows pointing to 3 state boxes on the right (shared grub state, bootc state,
+ and kernelopts state). Shows which objects feed which states.]
+
OBJECTS STATES
═══════ ══════
@@ -211,16 +277,86 @@
(uses_kernelopts) -- additional state used with `state_operator="OR"`:
┌─ state_grub2_{ARG}_argument_is_kernelopts ─┐
│ ^(?:.*\s)?\$kernelopts(?:\s.*)?$ │
- │ Matches `$kernelopts` as a word. │
- │ The RHEL 8 BLS test references BOTH │
- │ `state_argument` and this state with OR: │
- │ entry passes if it has arg=value OR │
- │ contains `$kernelopts`. │
- │ (Same flag as the `grubenv` object -- │
- │ both exist only when `$kernelopts` │
- │ indirection is in play.) │
+ │ │
+ │ Pattern match: is `$kernelopts` present │
+ │ as a standalone word in the captured │
+ │ `options` line from │
+ │ `/boot/loader/entries/*.conf`? │
+ │ │
+ │ The RHEL 8 BLS test applies BOTH │
+ │ `state_grub2_{ARG}_argument` and this │
+ │ state with `state_operator="OR"`: │
+ │ an entry passes if its `options` line │
+ │ contains `{ARG_NAME_VALUE}` OR contains │
+ │ `$kernelopts`. │
+ │ │
+ │ Same `uses_kernelopts` flag as the │
+ │ `{grub2_boot_path}/grubenv` object -- │
+ │ both exist only on RHEL 8 / OL8. │
└─────────────────────────────────────────────┘
+ LONG DESCRIPTION (text equivalent of the diagram; same information, no box-drawing):
+ ──────────────────────────────────────────────────────────────────────────────────────
+
+ There are 6 objects (data collectors) and 3 states (pass/fail conditions).
+
+ OBJECTS -- each extracts a line of text from a config file:
+
+ A. `/boot/loader/entries/*.conf` (flag: `uses_boot_loader_entries`)
+ OVAL element: `` + ``
+ Pattern: `^options (.*)$` -- captures the kernel arg list after `options `.
+ Filter: excludes filenames matching `*rescue.conf`.
+ Tested against: `state_grub2_{ARG}_argument`.
+
+ B. `{grub2_boot_path}/grubenv` (flag: `uses_kernelopts`)
+ OVAL element: ``
+ Pattern: `^kernelopts=(.*)$` -- captures the arg list after `kernelopts=`.
+ Tested against: `state_grub2_{ARG}_argument`.
+
+ C. `{grub2_boot_path}/grub.cfg` (flag: `uses_grub_cfg`)
+ OVAL element: ``
+ Pattern: `^.*/vmlinuz.*(root=.*)$` -- captures from `root=` to end of line.
+ Tested against: `state_grub2_{ARG}_argument`.
+
+ D. `/etc/default/grub` (all products)
+ OVAL element: ``
+ Pattern: `^\s*GRUB_CMDLINE_LINUX="(.*)"$` -- captures contents between quotes.
+ Same pattern repeated for `GRUB_CMDLINE_LINUX_DEFAULT`.
+ Tested against: `state_grub2_{ARG}_argument`.
+
+ E. `/etc/default/grub.d/*.cfg` (flag: `uses_etc_default_grub_d` / Ubuntu)
+ OVAL element: ``
+ Pattern: same as D.
+ Same pattern repeated for `GRUB_CMDLINE_LINUX_DEFAULT`.
+ Tested against: `state_grub2_{ARG}_argument`.
+
+ F. `/usr/lib/bootc/kargs.d/*.toml` (flag: `bootable_containers_supported`)
+ OVAL element: `` + ``
+ Pattern: `^kargs = \[([^\]]+)\]$` -- captures array contents between brackets.
+ Tested against: `state_grub2_{ARG}_usr_lib_bootc_kargs_d`.
+
+ STATES -- each applies a regex to the captured text:
+
+ 1. `state_grub2_{ARG}_argument` (shared by objects A through E)
+ Regex: `^(?:.*\s)?{ARG_NAME_VALUE}(?:\s.*)?$`
+ Question: is `{ARG_NAME_VALUE}` present as a standalone word (bounded by
+ whitespace or start/end of string)?
+ When `ARG_VARIABLE` is set, the regex is assembled at runtime: `oscap` reads
+ the value from the `XCCDF` profile, a `local_variable` concatenates it into
+ the regex pattern, and the state references it via `var_ref`.
+
+ 2. `state_grub2_{ARG}_usr_lib_bootc_kargs_d` (used by object F only)
+ Regex: `^.*"{ARG_NAME_VALUE}".*$`
+ Question: is `"{ARG_NAME_VALUE}"` present anywhere (with surrounding quotes,
+ as per `TOML` syntax)?
+
+ 3. `state_grub2_{ARG}_argument_is_kernelopts` (flag: `uses_kernelopts`)
+ Regex: `^(?:.*\s)?\$kernelopts(?:\s.*)?$`
+ Question: is `$kernelopts` present as a standalone word?
+ Used ONLY by the RHEL 8 BLS test, which applies both state 1 and state 3
+ with `state_operator="OR"`: an entry passes if it contains `{ARG_NAME_VALUE}`
+ OR contains `$kernelopts`.
+
REGEX SEMANTICS (what each `pattern` captures as `subexpression`):
─────────────────────────────────────────────────────────────────