diff --git a/cfbs.json b/cfbs.json index 3064b4d..2892337 100644 --- a/cfbs.json +++ b/cfbs.json @@ -33,6 +33,15 @@ "subdirectory": "reporting/client-initiated-reporting", "steps": ["json def.json def.json"] }, + "compliance-report-fwupd": { + "description": "Compliance report for firmware security posture via fwupd HSI and update status.", + "tags": ["reporting", "compliance", "security", "hardware"], + "subdirectory": "reporting/compliance-report-fwupd", + "dependencies": ["inventory-fwupd"], + "steps": [ + "copy fwupd-compliance-report.json .no-distrib/compliance-report-definitions/fwupd-compliance-report.json" + ] + }, "command-dispatcher": { "description": "Command dispatcher for running shell commands on schedule", "subdirectory": "management/command-dispatcher", @@ -246,6 +255,17 @@ "bundles inventory_fde:main" ] }, + "inventory-fwupd": { + "description": "Inventory hardware firmware versions, pending firmware updates, and host security attributes via fwupd.", + "tags": ["inventory", "monitoring", "hardware", "security"], + "subdirectory": "inventory/inventory-fwupd", + "steps": [ + "copy policy.cf services/cfbs/modules/inventory-fwupd/policy.cf", + "copy fwupd-inventory.mustache services/cfbs/modules/inventory-fwupd/fwupd-inventory.mustache", + "policy_files services/cfbs/modules/inventory-fwupd/policy.cf", + "bundles inventory_fwupd_main" + ] + }, "inventory-smartctl": { "description": "Inventory SMART drive health, temperature, and wear data.", "tags": ["inventory", "monitoring", "hardware", "storage"], @@ -278,6 +298,50 @@ "bundles maintainers_in_motd" ] }, + "manage-fwupd": { + "description": "Ensure fwupd is installed and its refresh timer is enabled. Optionally apply firmware updates for devices matching an allow-list.", + "subdirectory": "management/manage-fwupd", + "dependencies": ["inventory-fwupd"], + "steps": [ + "copy main.cf services/cfbs/modules/manage-fwupd/main.cf", + "policy_files services/cfbs/modules/manage-fwupd/main.cf", + "bundles manage_fwupd:allowed manage_fwupd:main", + "input ./input.json def.json" + ], + "input": [ + { + "type": "string", + "variable": "apply_updates", + "namespace": "manage_fwupd", + "bundle": "allowed", + "label": "Apply firmware updates", + "question": "Class expression for when firmware updates should be applied (e.g. 'any', 'linux', '(env_dev|env_qa).Night.(cohort_A|cohort_C)'). Default '!any' means disabled.", + "default": "!any" + }, + { + "type": "list", + "variable": "device_name_reglist", + "namespace": "manage_fwupd", + "bundle": "allowed", + "label": "Allowed devices", + "subtype": { + "type": "string", + "label": "Device name pattern", + "question": "Device name (regex) to allow firmware updates for" + }, + "while": "Do you want to allow firmware updates for more devices?" + }, + { + "type": "string", + "variable": "reboot_after_update", + "namespace": "manage_fwupd", + "bundle": "allowed", + "label": "Reboot after firmware update", + "question": "Class expression for when to reboot after a firmware update is applied (e.g. 'any', 'Night', 'Hr04.Min00_05'). Default '!any' means disabled (no automatic reboot).", + "default": "!any" + } + ] + }, "powershell-execution-policy": { "description": "Inventory and bundle for PowerShell Execution Policy", "subdirectory": "management/powershell-execution-policy", diff --git a/inventory/inventory-fwupd/README.org b/inventory/inventory-fwupd/README.org new file mode 100644 index 0000000..689769e --- /dev/null +++ b/inventory/inventory-fwupd/README.org @@ -0,0 +1,101 @@ +Surface fwupd state as inventory attributes: current firmware versions +and vendors, pending updates from LVFS, and Host Security Identifier +(HSI) security posture. + +Pair with *manage-fwupd* to optionally apply firmware updates for +devices matching an allow-list. + +* Requirements + +- Linux (silently no-ops on other platforms) +- =fwupd= package for full functionality (manage-fwupd installs it + automatically). Without fwupd, the module still runs but reports + =Firmware update status= as =FWUPD_MISSING=. +- =fwupd-refresh.timer= enabled so the LVFS firmware catalog stays + current (manage-fwupd handles this automatically) + +* Mission Portal + +The inventory attributes appear in Mission Portal's column selector +under "fwupd": + +[[file:mp-inventory-fwupd-columns.png]] + +* Inventory Attributes + +** Rolled-up status + +| Attribute | Values | +|----------------------------+-----------------------------------------------------------------| +| *Firmware update status* | =OK= -- no pending updates | +| | =UPDATES_AVAILABLE= -- one or more devices have pending updates | +| | =NO_DEVICES= -- fwupd present but no updatable devices | +| | =FWUPD_MISSING= -- fwupd is not installed | + +** Counters + +| Attribute | Description | +|------------------------------+------------------------------------------| +| *Firmware devices total* | Number of devices fwupd is tracking | +| *Firmware updates available* | Number of devices with a pending update | + +** Per-device attributes + +For every device fwupd reports (keyed by DeviceId): + +| Attribute | Format | +|------------------------------------+---------------------------------------------------------| +| *Firmware devices* | =Name | Vendor | vX.Y.Z | [plugin]= | +| *Firmware device pending update* | =Name: current -> new= (only when an update is pending) | + +** HSI attributes + +| Attribute | Format | +|---------------------------------+------------------------------------------------------------| +| *Firmware HSI level* | =HSI:0= through =HSI:4= | +| *Firmware HSI L: * | =PASS= or =FAIL= (one per security check) | +| *Firmware HSI attributes* | =Name (HSI L): [PASS|FAIL]= (slist) | +| *Firmware HSI failing attributes* | Integer count of failing checks | + +*Firmware HSI level* is the rolled-up Host Security Identifier level. +fwupd walks levels 1--4 sequentially; the result is the highest level +where all attributes pass, stopping at the first level with any failure. + +*Firmware HSI L: * variables (e.g. =Firmware HSI L1: TPM v2.0=) +are individual string attributes with value =PASS= or =FAIL=. These are +consumed by *compliance-report-fwupd* for per-check compliance conditions. +Two normalizations are applied to keep inventory attribute names stable +and aligned with the HSI specification: + +- *Name normalization:* The CSME version attribute is emitted as + =Firmware HSI L1: CSME version= regardless of the firmware version + string fwupd reports (which varies per host). +- *Level normalization:* fwupd marks some runtime checks at HsiLevel 0 + even though they contribute to scored HSI levels. The module maps + these to their specification levels: =UEFI secure boot= is emitted + at L1 (not L0) and =CET OS Support= at L3 (not L0). + +*Firmware HSI attributes* is an slist with one detailed entry per +security check, useful for drill-down in Mission Portal inventory views. + +* Classes + +The module defines namespace-scoped classes for platform-specific +compliance report targeting: + +| Class | Source | Matches | +|--------------------------+--------------------------------+--------------------------------| +| =fwupd_cpu_vendor_intel= | =/proc/cpuinfo= vendor_id | =GenuineIntel= | +| =fwupd_cpu_vendor_amd= | =/proc/cpuinfo= vendor_id | =AuthenticAMD= | +| =fwupd_oem_vendor_hp= | =/sys/class/dmi/id/sys_vendor= | =HP Inc.= or =Hewlett-Packard= | + +These classes are used by *compliance-report-fwupd* =host_filter= +fields to restrict Intel-only, AMD-only, and HP-only conditions to +the relevant hardware. + +* Limitations + +The module is /read-only/ -- it never applies firmware updates. +Use *manage-fwupd* for that. + + diff --git a/inventory/inventory-fwupd/fwupd-inventory.mustache b/inventory/inventory-fwupd/fwupd-inventory.mustache new file mode 100644 index 0000000..0346de6 --- /dev/null +++ b/inventory/inventory-fwupd/fwupd-inventory.mustache @@ -0,0 +1,7 @@ +^context=inventory_fwupd_cache +^meta=inventory,attribute_name=Firmware update status +=fwupd_status={{{status}}} +^meta=inventory,attribute_name=Firmware devices total +=fwupd_device_count={{{device_count}}} +^meta=inventory,attribute_name=Firmware updates available +=fwupd_updates_available={{{updates_count}}} diff --git a/inventory/inventory-fwupd/mp-inventory-fwupd-columns.png b/inventory/inventory-fwupd/mp-inventory-fwupd-columns.png new file mode 100644 index 0000000..34e4760 Binary files /dev/null and b/inventory/inventory-fwupd/mp-inventory-fwupd-columns.png differ diff --git a/inventory/inventory-fwupd/policy.cf b/inventory/inventory-fwupd/policy.cf new file mode 100644 index 0000000..32dac4a --- /dev/null +++ b/inventory/inventory-fwupd/policy.cf @@ -0,0 +1,428 @@ +# inventory-fwupd policy +# +# Inventories hardware firmware versions, pending firmware updates, and the +# Host Security ID (HSI) attribute scores reported by fwupd. +# +# Implementation notes: +# - Device list is read directly from /var/cache/fwupd/devices.json, which +# fwupd's own systemd timer (fwupd-refresh.timer) keeps up to date. No +# fwupdmgr call is needed for the common steady-state inventory. +# - Pending updates and HSI attributes are not cached as files by fwupd, so +# they are sourced via fwupdmgr and re-fetched only when their respective +# JSON caches age past their TTL. +# - All inventory variables are emitted via a single module-protocol cache +# rendered from JSON with an inline_mustache template. The cache is +# consumed in one call to read_module_protocol(), which loads dozens of +# inventory-tagged variables at far lower runtime cost than iterating the +# raw JSON with classic arrays. +# +# Rolled-up status (Mission Portal attribute "Firmware update status"): +# OK - fwupd present, no pending updates +# UPDATES_AVAILABLE - one or more devices have firmware updates pending +# NO_DEVICES - fwupd present but reports no updatable devices +# FWUPD_MISSING - fwupd not installed on the host + +bundle agent inventory_fwupd_main +{ + vars: + linux:: + # Public API: referenced by manage-fwupd and other consumers. + "fwupdmgr" + string => ifelse( + fileexists("/usr/bin/fwupdmgr"), "/usr/bin/fwupdmgr", + fileexists("/usr/local/bin/fwupdmgr"), "/usr/local/bin/fwupdmgr", + "/usr/bin/fwupdmgr"); + + "_devices_src" string => "/var/cache/fwupd/devices.json"; + "_updates_src" string => "$(sys.statedir)/fwupd_inventory_updates.json"; + "_security_src" string => "$(sys.statedir)/fwupd_inventory_security.json"; + "_cache" string => "$(sys.statedir)/fwupd_inventory_cache"; + "_template" string => "$(this.promise_dirname)/fwupd-inventory.mustache"; + + # TTLs in seconds for the fwupdmgr-derived JSON caches. + "_updates_ttl" string => "43200"; # 12 h + "_security_ttl" string => "86400"; # 24 h + + # Pre-compute file mtimes here rather than calling filestat() inline + # inside class expressions: nested $(filestat($(path), mtime)) does not + # expand reliably inside isgreaterthan(), so the staleness comparisons + # silently never match. Defaulting to "0" when a file is missing lets + # the class expressions below stay simple and total. + "_cache_mtime" + string => ifelse(fileexists("$(_cache)"), + filestat("$(_cache)", "mtime"), "0"); + "_devices_mtime" + string => ifelse(fileexists("$(_devices_src)"), + filestat("$(_devices_src)", "mtime"), "0"); + "_updates_mtime" + string => ifelse(fileexists("$(_updates_src)"), + filestat("$(_updates_src)", "mtime"), "0"); + "_security_mtime" + string => ifelse(fileexists("$(_security_src)"), + filestat("$(_security_src)", "mtime"), "0"); + + "_updates_age" string => eval("$(sys.systime) - $(_updates_mtime)", "math", "infix"); + "_security_age" string => eval("$(sys.systime) - $(_security_mtime)", "math", "infix"); + + # /proc/1 mtime is when PID 1 (init) was exec'd, i.e., boot time on + # Linux. Used below to force a refresh of the updates cache after a + # reboot, so consumers like manage-fwupd don't act on a pre-boot + # snapshot that still lists devices whose updates have already been + # activated. + "_boot_mtime" + string => ifelse(fileexists("/proc/1"), + filestat("/proc/1", "mtime"), "0"); + + classes: + linux:: + "have_fwupdmgr" + scope => "namespace", + expression => isexecutable("$(fwupdmgr)"); + "_have_devices" expression => fileexists("$(_devices_src)"); + + # CPU vendor classes for platform-specific compliance conditions. + # /proc/cpuinfo vendor_id: "GenuineIntel" or "AuthenticAMD". + "fwupd_cpu_vendor_intel" + scope => "namespace", + meta => { "report" }, + expression => regline("vendor_id.*GenuineIntel", "/proc/cpuinfo"); + "fwupd_cpu_vendor_amd" + scope => "namespace", + meta => { "report" }, + expression => regline("vendor_id.*AuthenticAMD", "/proc/cpuinfo"); + + # OEM vendor class for HP-specific compliance conditions. + # /sys/class/dmi/id/sys_vendor: "HP Inc." (post-2015) or + # "Hewlett-Packard" (pre-2015). + "fwupd_oem_vendor_hp" + scope => "namespace", + meta => { "report" }, + expression => regline("(HP Inc\.|Hewlett-Packard)", + "/sys/class/dmi/id/sys_vendor"); + + linux.have_fwupdmgr:: + "_updates_stale" + not => fileexists("$(_updates_src)"); + "_updates_stale" + expression => isgreaterthan("$(_updates_age)", "$(_updates_ttl)"), + if => fileexists("$(_updates_src)"); + # Force refresh after a reboot: a pre-boot cache may still list + # devices whose updates have been activated by the boot, and + # consumers (manage-fwupd) would otherwise repeatedly re-apply + # them and re-trigger reboots. + "_updates_stale" + expression => islessthan("$(_updates_mtime)", "$(_boot_mtime)"), + if => fileexists("$(_updates_src)"); + + "_security_stale" + not => fileexists("$(_security_src)"); + "_security_stale" + expression => isgreaterthan("$(_security_age)", "$(_security_ttl)"), + if => fileexists("$(_security_src)"); + + linux._have_devices:: + # Module-protocol cache must be rebuilt when any of the source JSON + # files is newer than the cache itself, or when the cache is missing. + "_cache_missing" not => fileexists("$(_cache)"); + "_cache_stale_devices" + expression => isgreaterthan("$(_devices_mtime)", "$(_cache_mtime)"); + "_cache_stale_updates" + expression => isgreaterthan("$(_updates_mtime)", "$(_cache_mtime)"), + if => "have_fwupdmgr"; + "_cache_stale_security" + expression => isgreaterthan("$(_security_mtime)", "$(_cache_mtime)"), + if => "have_fwupdmgr"; + "_rebuild_cache" + or => { "_cache_missing", "_cache_stale_devices", + "_cache_stale_updates", "_cache_stale_security" }; + + vars: + linux.have_fwupdmgr._updates_stale:: + "_updates_raw" + string => execresult("$(fwupdmgr) get-updates --json", "noshell"); + "_updates_payload" + string => ifelse(regcmp("\s*\{.*", "$(_updates_raw)"), + "$(_updates_raw)", + '{"Devices":[]}'); + + linux.have_fwupdmgr._security_stale:: + "_security_raw" + string => execresult("$(fwupdmgr) security --json", "noshell"); + "_security_payload" + string => ifelse(regcmp("\s*\{.*", "$(_security_raw)"), + "$(_security_raw)", + '{"SecurityAttributes":[]}'); + + files: + linux.have_fwupdmgr._updates_stale:: + "$(_updates_src)" + create => "true", + content => "$(_updates_payload)"; + + linux.have_fwupdmgr._security_stale:: + "$(_security_src)" + create => "true", + content => "$(_security_payload)"; + + methods: + linux._have_devices._rebuild_cache:: + "render" usebundle => inventory_fwupd_render( + "$(_devices_src)", "$(_updates_src)", "$(_security_src)", + "$(_template)", "$(_cache)"); + + linux.have_fwupdmgr:: + # HSI rollup is small (typically <40 attributes) and computed inline. + # Emitted as direct policy variables rather than via the module-protocol + # cache because filtering objects out of a data container and reshaping + # them into a mustache-iterable list is more code than just iterating + # and tagging the few results with inventory meta. + "hsi" usebundle => inventory_fwupd_hsi("$(_security_src)"), + if => fileexists("$(_security_src)"); + + # Per-device pending firmware update inventory, emitted natively + # from the updates JSON cache. Mirrors the HSI bundle idiom. + "updates" usebundle => inventory_fwupd_updates("$(_updates_src)"), + if => fileexists("$(_updates_src)"); + + # Per-device firmware inventory, emitted natively from the devices + # JSON cache. Mirrors the HSI bundle idiom. + "devices" usebundle => inventory_fwupd_devices("$(_devices_src)"), + if => fileexists("$(_devices_src)"); + + classes: + linux._have_devices:: + "_cache_was_read" if => read_module_protocol("$(_cache)"); + + linux.!_have_devices:: + # No source file from fwupd - emit a single status attribute. + "_emit_missing_status"; + + vars: + linux.!_have_devices._emit_missing_status:: + "fwupd_status" + string => "FWUPD_MISSING", + meta => { "inventory", "attribute_name=Firmware update status" }; + + reports: + linux._cache_was_read.verbose_mode:: + "inventory_fwupd: loaded $(inventory_fwupd_cache.fwupd_device_count) devices, status=$(inventory_fwupd_cache.fwupd_status)"; + + !linux.verbose_mode:: + "$(this.promise_filename): inventory-fwupd is Linux-only."; +} + +bundle agent inventory_fwupd_render(devices_src, updates_src, security_src, template, cache) +# @brief Render the module-protocol cache file from the JSON sources. +# +# The cache must be a single mustache render: read_module_protocol() in the +# caller then loads every inventory variable in one shot. +{ + vars: + "_template_body" string => readfile("$(template)", "inf"); + + "_devices_json" data => readjson("$(devices_src)"); + "_dev_count" int => length("_devices_json[Devices]"); + + "_updates_json" + data => readjson("$(updates_src)"), + if => fileexists("$(updates_src)"); + "_updates_count" + int => length("_updates_json[Devices]"), + if => isvariable("_updates_json[Devices]"); + + "_security_json" + data => readjson("$(security_src)"), + if => fileexists("$(security_src)"); + + # Default counters when fwupdmgr-derived data is not present. + "_updates_count" + int => "0", + if => not(isvariable("_updates_count")); + + # Status rollup + "_status" + string => ifelse( + isgreaterthan("$(_updates_count)", "0"), "UPDATES_AVAILABLE", + isgreaterthan("$(_dev_count)", "0"), "OK", + "NO_DEVICES"); + + # HSI inventory is emitted directly by inventory_fwupd_hsi rather than + # threaded through this cache, since reshaping filtered objects into a + # mustache-iterable list is more code than just tagging them in policy. + "_extra_json" string => format( + '{ "status": "%s", "device_count": "%d", "updates_count": "%d" }', + "$(_status)", "$(_dev_count)", "$(_updates_count)"); + "_extra" data => parsejson("$(_extra_json)"); + + # Mergedata combines device data, status counters, and optional + # extra top-level keys into a single data structure for mustache rendering. + # Note: pending-update inventory is emitted natively via + # inventory_fwupd_updates() rather than threaded through this cache, + # because Releases[0].Version cannot be accessed from mustache templates. + "_merged" data => mergedata("_devices_json", "_extra"); + + files: + "$(cache)" + create => "true", + template_method => "inline_mustache", + edit_template_string => "$(_template_body)", + template_data => @(_merged); +} + +bundle agent inventory_fwupd_hsi(security_src) +# @brief Surface fwupd Host Security Identifier (HSI) attributes and +# compute the rolled-up HSI level. +# +# Emits: +# - Per-attribute PASS/FAIL strings named "Firmware HSI L: " +# for use in compliance report conditions. +# - An slist of detailed results formatted "Name (HSI L): +# [PASS|FAIL]" for inventory drill-down. +# - A count of failing attributes. +# - The rolled-up HSI level (e.g. "HSI:3"). +# +# The rollup mirrors fwupd's own algorithm: walk levels 1-4 sequentially; +# at each level, if any non-obsoleted attribute fails, stop — the result +# is the highest level that had at least one success before the walk +# stopped. Empty levels (no attributes) are skipped without advancing +# or stopping. +# +# An attribute is failing when its HsiResult differs from HsiResultSuccess. +{ + classes: + "inventory_fwupd_hsi_failing_$(_idxes)" + not => strcmp("$(_result[$(_idxes)])", "$(_success[$(_idxes)])"); + + # Per-level failure/success classes for the HSI rollup. + # If ANY attribute at level N fails, _hsi_LN_fail is defined. + # If ANY attribute at level N passes, _hsi_LN_pass is defined. + "_hsi_L$(_level[$(_idxes)])_fail" + expression => "inventory_fwupd_hsi_failing_$(_idxes)"; + "_hsi_L$(_level[$(_idxes)])_pass" + not => "inventory_fwupd_hsi_failing_$(_idxes)"; + + vars: + "_sec" data => readjson("$(security_src)"); + "_idxes" slist => getindices("_sec[SecurityAttributes]"); + + "_name[$(_idxes)]" + string => "$(_sec[SecurityAttributes][$(_idxes)][Name])"; + "_level[$(_idxes)]" + string => "$(_sec[SecurityAttributes][$(_idxes)][HsiLevel])"; + "_result[$(_idxes)]" + string => "$(_sec[SecurityAttributes][$(_idxes)][HsiResult])"; + "_success[$(_idxes)]" + string => "$(_sec[SecurityAttributes][$(_idxes)][HsiResultSuccess])"; + + "_status[$(_idxes)]" + string => ifelse(strcmp("$(_result[$(_idxes)])", "$(_success[$(_idxes)])"), + "PASS", "FAIL"); + + # Normalized name for per-attribute inventory: the CSME version + # attribute has a dynamic Name containing the firmware version + # (e.g. "csme v0:16.1.40.2765") which would create a different + # inventory attribute on every host. Normalize it to a fixed name + # so compliance conditions can reference it statically. + "_inv_name[$(_idxes)]" + string => ifelse(regcmp("csme v.*", "$(_name[$(_idxes)])"), + "CSME version", + "$(_name[$(_idxes)])"); + + # Normalized level for per-attribute inventory: fwupd marks some + # attributes as HsiLevel 0 with a "runtime-issue" flag even though + # they contribute to scored HSI levels. Map them to their HSI spec + # level so inventory attribute names match compliance conditions. + # UEFI secure boot (org.fwupd.hsi.Uefi.SecureBoot) → HSI:1 + # CET OS Support (org.fwupd.hsi.IntelCet.Active) → HSI:3 + "_inv_level[$(_idxes)]" + string => ifelse(strcmp("$(_name[$(_idxes)])", "UEFI secure boot"), + "1", + strcmp("$(_name[$(_idxes)])", "CET OS Support"), + "3", + "$(_level[$(_idxes)])"); + + # Per-attribute inventory: one string variable per HSI check, + # named "Firmware HSI L: " with value PASS or FAIL. + # This enables per-check compliance report conditions. + "fwupd_hsi_check[$(_idxes)]" + string => "$(_status[$(_idxes)])", + meta => { "inventory", + "attribute_name=Firmware HSI L$(_inv_level[$(_idxes)]): $(_inv_name[$(_idxes)])" }; + + "fwupd_hsi_attr[$(_idxes)]" + string => "$(_name[$(_idxes)]) (HSI L$(_level[$(_idxes)])): $(_result[$(_idxes)]) [$(_status[$(_idxes)])]", + meta => { "inventory", "attribute_name=Firmware HSI attributes" }; + + "fwupd_hsi_failing_count" + int => countclassesmatching("inventory_fwupd_hsi_failing_[0-9]+"), + meta => { "inventory", "attribute_name=Firmware HSI failing attributes" }; + + # Rolled-up HSI level: highest level with a pass where no level + # at or below it has a failure. Checks highest first so the + # first match wins. Mirrors fwupd's fu_security_attrs_calculate_hsi(). + "fwupd_hsi_level" + string => ifelse( + and("_hsi_L4_pass", not("_hsi_L1_fail"), not("_hsi_L2_fail"), not("_hsi_L3_fail"), not("_hsi_L4_fail")), "HSI:4", + and("_hsi_L3_pass", not("_hsi_L1_fail"), not("_hsi_L2_fail"), not("_hsi_L3_fail")), "HSI:3", + and("_hsi_L2_pass", not("_hsi_L1_fail"), not("_hsi_L2_fail")), "HSI:2", + and("_hsi_L1_pass", not("_hsi_L1_fail")), "HSI:1", + "HSI:0"), + meta => { "inventory", "attribute_name=Firmware HSI level" }; +} + +bundle agent inventory_fwupd_updates(updates_src) +# @brief Surface per-device pending firmware update inventory. +# +# Reads the updates JSON cache and emits one inventory entry per device +# that has a pending update. Format: +# "Device Name: current_version -> new_version" +# +# Unlike the HSI bundle, devices here are iterated from the Devices array +# in fwupd_inventory_updates.json rather than SecurityAttributes. +# Releases[0].Version is the new version (mustache templates can't index +# into JSON arrays, so we flatten it natively in CFEngine). +{ + vars: + "_upd" data => readjson("$(updates_src)"); + "_idxes" slist => getindices("_upd[Devices]"); + + "_did[$(_idxes)]" string => "$(_upd[Devices][$(_idxes)][DeviceId])"; + "_name[$(_idxes)]" string => "$(_upd[Devices][$(_idxes)][Name])"; + "_oldv[$(_idxes)]" string => "$(_upd[Devices][$(_idxes)][Version])"; + "_newv[$(_idxes)]" string => "$(_upd[Devices][$(_idxes)][Releases][0][Version])"; + + "fwupd_dev_pending[$(_did[$(_idxes)])]" + string => "$(_name[$(_idxes)]): $(_oldv[$(_idxes)]) -> $(_newv[$(_idxes)])", + meta => { "inventory", "attribute_name=Firmware device pending update" }; +} + +bundle agent inventory_fwupd_devices(devices_src) +# @brief Surface per-device firmware inventory, one entry per non-empty +# device. Format: "Name | Vendor | vVersion | [Plugin]" +# +# Reads the devices JSON cache and emits one inventory entry per device +# that has a non-empty Name. Emits via indexed arrays (same idiom as +# inventory_fwupd_hsi / inventory_fwupd_updates). Devices with empty +# Name silently skip via the if => not(strcmp(...)) guard. +{ + vars: + "_dev" data => readjson("$(devices_src)"); + "_idxes" slist => getindices("_dev[Devices]"); + + "_did[$(_idxes)]" string => "$(_dev[Devices][$(_idxes)][DeviceId])"; + "_name[$(_idxes)]" string => "$(_dev[Devices][$(_idxes)][Name])"; + "_vend[$(_idxes)]" string => "$(_dev[Devices][$(_idxes)][Vendor])"; + "_ver[$(_idxes)]" string => "$(_dev[Devices][$(_idxes)][Version])"; + "_plug[$(_idxes)]" string => "$(_dev[Devices][$(_idxes)][Plugin])"; + + "fwupd_dev[$(_did[$(_idxes)])]" + string => "$(_name[$(_idxes)]) | $(_vend[$(_idxes)]) | v$(_ver[$(_idxes)]) | [$(_plug[$(_idxes)])]", + meta => { "inventory", "attribute_name=Firmware devices" }, + if => not(strcmp("$(_name[$(_idxes)])", "")); +} + +bundle agent __main__ +{ + methods: + "main" usebundle => inventory_fwupd_main; +} diff --git a/management/manage-fwupd/README.org b/management/manage-fwupd/README.org new file mode 100644 index 0000000..3bbd59b --- /dev/null +++ b/management/manage-fwupd/README.org @@ -0,0 +1,111 @@ +Ensure fwupd is installed and its metadata refresh timer is enabled. +Optionally apply firmware updates for devices matching an allow-list. + +Depends on *inventory-fwupd*, which surfaces firmware versions, +pending updates, and HSI security attributes as inventory. This module +reads the updates cache that inventory-fwupd maintains rather than +calling fwupdmgr itself, avoiding duplicate work. + +* Inputs + +All three inputs live in the =manage_fwupd:allowed= bundle and can be +set via ~cfbs input~ (interactive), augments (=def.json=), CMDB, or +host-specific data. The variable names are: + +| Variable | Type | Default | +|--------------------------------------------+--------------------------+---------------| +| =manage_fwupd:allowed.apply_updates= | string (class expression) | =!any= | +| =manage_fwupd:allowed.device_name_reglist= | list (pcre patterns) | ={}= (empty) | +| =manage_fwupd:allowed.reboot_after_update= | string (class expression) | =!any= | + +*Augments example* (=def.json=): + +#+begin_src json +{ + "variables": { + "manage_fwupd:allowed.apply_updates": { + "value": "any" + }, + "manage_fwupd:allowed.device_name_reglist": { + "value": ["UEFI dbx", "System Firmware"] + }, + "manage_fwupd:allowed.reboot_after_update": { + "value": "Night" + } + } +} +#+end_src + +** Apply firmware updates + +A CFEngine class expression controlling when firmware updates are applied. +The default is =!any=, which means updates are never applied until you +explicitly opt in by changing this value. + +Examples: + +| Expression | Meaning | +|-------------------------------------------------+-----------------------------------------------------| +| =!any= | Disabled (default) -- never apply firmware updates | +| =any= | Apply on all hosts, always | +| =linux= | Apply on all Linux hosts | +| =(env_dev\vert{}env_qa).Night.(cohort_A\vert{}cohort_C)= | Dev/QA environments, at night, for cohorts A and C | +| =Hr04.Min00_05= | All hosts, but only during a 4:00--4:05 AM window | +| =laptop_updates_enabled= | Only hosts where you've defined this custom class | + +Use =.= for AND, =|= for OR, =!= for NOT, and parentheses for grouping. +These are CFEngine class expressions, not regular expressions. + +** Allowed devices + +A list of device name patterns (regular expressions) controlling which +devices are eligible for firmware updates. Even when =apply_updates= is +enabled, only devices whose name matches an entry in this list will be +updated. + +Use =.*= to match all devices. Use specific patterns to target individual +hardware, e.g. =UEFI dbx=, =System Firmware=, =TPM=. + +** Reboot after update + +A CFEngine class expression controlling when the host reboots after a +firmware update is applied. The default is =!any= (disabled). Some firmware +updates (e.g. UEFI capsule) only activate after a reboot. + +When enabled, the module waits for cf-agent to finish before rebooting. +On systemd systems this uses a transient service that polls for cf-agent's +PID to exit, then reboots immediately. On non-systemd systems it falls +back to ~shutdown -r +1~. + +* Mission Portal + +The module reports =fwupd management status= as an inventory attribute +visible in Mission Portal's inventory reports: + +[[file:mp-inventory-fwupd-status.png]] + +* Behavior + +1. *Always:* Ensures the =fwupd= package is installed +2. *Always:* Ensures =fwupd-refresh.timer= is enabled (keeps the LVFS firmware catalog current) +3. *When =apply_updates= class resolves true AND a device matches the allow-list:* + Runs ~fwupdmgr update --no-reboot-check ~ for each matching device. + A marker file prevents the same update from re-executing within the + same boot cycle. +4. *When =reboot_after_update= resolves true AND a firmware update was applied:* + Schedules a deferred reboot after cf-agent exits. + +The =--no-reboot-check= flag suppresses the interactive reboot prompt. +It does not skip or defer the update itself. + +* Requirements + +Linux only. The =fwupd= package must be available in the system's +package repositories. + +* Warnings + +Firmware updates carry inherent risk (bricking, required reboots, AC +power requirements). Use the allow-list to limit updates to +well-understood device classes. Test in a non-production environment +first. diff --git a/management/manage-fwupd/input.json b/management/manage-fwupd/input.json new file mode 100644 index 0000000..5a4d05c --- /dev/null +++ b/management/manage-fwupd/input.json @@ -0,0 +1,33 @@ +[ + { + "type": "string", + "variable": "apply_updates", + "namespace": "manage_fwupd", + "bundle": "allowed", + "label": "Apply firmware updates", + "question": "Class expression for when firmware updates should be applied (e.g. 'any', 'linux', '(env_dev|env_qa).Night.(cohort_A|cohort_C)'). Default '!any' means disabled.", + "default": "!any" + }, + { + "type": "list", + "variable": "device_name_reglist", + "namespace": "manage_fwupd", + "bundle": "allowed", + "label": "Allowed devices", + "subtype": { + "type": "string", + "label": "Device name pattern", + "question": "Device name (regex) to allow firmware updates for" + }, + "while": "Do you want to allow firmware updates for more devices?" + }, + { + "type": "string", + "variable": "reboot_after_update", + "namespace": "manage_fwupd", + "bundle": "allowed", + "label": "Reboot after firmware update", + "question": "Class expression for when to reboot after a firmware update is applied (e.g. 'any', 'Night', 'Hr04.Min00_05'). Default '!any' means disabled (no automatic reboot).", + "default": "!any" + } +] diff --git a/management/manage-fwupd/main.cf b/management/manage-fwupd/main.cf new file mode 100644 index 0000000..a164ba2 --- /dev/null +++ b/management/manage-fwupd/main.cf @@ -0,0 +1,196 @@ +body file control +{ + namespace => "manage_fwupd"; +@if minimum_version(3.27) + evaluation_order => "top_down"; +@endif +} + +bundle agent allowed +# @brief Default values for firmware update configuration. +# Override apply_updates, device_name_reglist, and reboot_after_update +# via CMDB or augments to control firmware update behavior. +{ + vars: + "apply_updates" + string => "!any", + if => not(isvariable("apply_updates")), + comment => "Default disabled; set to a class expression to enable firmware updates"; + + "device_name_reglist" + slist => {}, + if => not(isvariable("device_name_reglist")), + comment => "Default empty allow-list; override via CMDB or augments"; + + "reboot_after_update" + string => "!any", + if => not(isvariable("reboot_after_update")), + comment => "Default disabled; set to a class expression to reboot after firmware update"; +} + +bundle agent main +# @brief Ensure fwupd is installed, its refresh timer is enabled, and +# optionally apply firmware updates for devices matching an allow-list. +# +# Depends on inventory-fwupd, which provides: +# - inventory_fwupd_main.fwupdmgr (path to fwupdmgr binary) +# - default:have_fwupdmgr (class: fwupdmgr is present) +# - updates cache at $(sys.statedir)/fwupd_inventory_updates.json +# +# Firmware updates are applied when the apply_updates class expression resolves +# true AND a device name matches at least one pcre regular expression defined in +# manage_fwupd:allowed.device_name_reglist. +# +# A marker file ($(sys.statedir)/fwupd_update_applied) prevents the +# same update from re-executing on subsequent agent runs within the +# same boot cycle. The marker's mtime is compared against /proc/1 +# (boot time) via isnewerthan(). Any marker remaining from a prior boot +# is deleted so the update can re-evaluate after reboot. +# +# When reboot_after_update resolves true the marker is removed and +# a deferred reboot is scheduled. On systemd systems a transient +# service polls for cf-agent to exit then reboots immediately; on +# non-systemd systems the legacy shutdown -r +1 fallback is used. +# +# --no-reboot-check suppresses the interactive "reboot now?" prompt +# that fwupdmgr emits after certain updates. It does not skip or +# defer the update itself. +# +# @inventory fwupd management status - INSTALLED, NOT_INSTALLED, or NOT_SUPPORTED +{ + vars: + linux:: + "_update_marker" + string => "$(sys.statedir)/fwupd_update_applied"; + + "_systemd_run" + string => ifelse(isexecutable("/bin/systemd-run"), "/bin/systemd-run", + isexecutable("/usr/bin/systemd-run"), "/usr/bin/systemd-run", + ""), + comment => "Path to systemd-run when available (empty string otherwise)"; + + linux.default:have_fwupdmgr.manage_fwupd:apply_updates:: + "_updates_cache" + string => "$(sys.statedir)/fwupd_inventory_updates.json"; + + "_updates" + data => readjson("$(_updates_cache)"), + if => fileexists("$(_updates_cache)"); + + "_dev_idx" + slist => getindices("_updates[Devices]"), + if => isvariable("_updates"); + + "_dev_name[$(_dev_idx)]" + string => "$(_updates[Devices][$(_dev_idx)][Name])"; + + "_dev_id[$(_dev_idx)]" + string => "$(_updates[Devices][$(_dev_idx)][DeviceId])"; + + classes: + linux:: + "apply_updates" + scope => "namespace", + meta => { "report" }, + expression => "$(manage_fwupd:allowed.apply_updates)", + comment => "Opt-in via class expression (default '!any' = disabled)"; + + "reboot_after_update" + scope => "namespace", + meta => { "report" }, + expression => "$(manage_fwupd:allowed.reboot_after_update)", + comment => "Opt-in reboot after firmware update (default '!any' = disabled)"; + + "_have_systemd_run" + expression => isgreaterthan(string_length("$(_systemd_run)"), 0), + comment => "True when systemd-run is available for deferred reboot"; + + "_update_applied" + expression => and( + fileexists("$(_update_marker)"), + isnewerthan("$(_update_marker)", "/proc/1") + ), + comment => "A firmware update was applied during this boot cycle"; + + linux.default:have_fwupdmgr.manage_fwupd:apply_updates:: + "_device_allowed_$(_dev_idx)" + expression => regcmp("$(manage_fwupd:allowed.device_name_reglist)", "$(_dev_name[$(_dev_idx)])"), + comment => "Device $(_dev_name[$(_dev_idx)]) matches allow-list entry"; + + packages: + linux:: + "fwupd" + policy => "present", + comment => "Ensure fwupd is installed for firmware inventory and management"; + + services: + linux.default:have_fwupdmgr:: + "fwupd-refresh.timer" + service_policy => "enabled", + comment => "Ensure the fwupd metadata refresh timer is enabled so the LVFS firmware catalog stays current"; + + # --- Stale marker cleanup ---------------------------------------- + files: + linux:: + "$(_update_marker)" + delete => default:tidy, + if => not("_update_applied"), + comment => "Remove stale marker from a previous boot cycle"; + + # --- Apply firmware updates -------------------------------------- + commands: + linux.default:have_fwupdmgr.manage_fwupd:apply_updates:: + "$(default:inventory_fwupd_main.fwupdmgr)" + arglist => { "update", "--no-reboot-check", "$(_dev_id[$(_dev_idx)])" }, + handle => canonify("manage_fwupd_apply_device_$(_dev_idx)_$(_dev_name[$(_dev_idx)])"), + if => and("_device_allowed_$(_dev_idx)", not("_update_applied")), + classes => default:results("bundle", "manage_fwupd_update_$(_dev_idx)"), + comment => "Apply firmware update to allowed device $(_dev_name[$(_dev_idx)])"; + + # --- Post-update marker and reboot -------------------------------- + files: + linux.default:have_fwupdmgr.manage_fwupd:apply_updates:: + "$(_update_marker)" + create => "true", + if => "manage_fwupd_update_$(_dev_idx)_repaired", + comment => "Record that a firmware update was applied this boot cycle (prevents re-execution)"; + + manage_fwupd:_update_applied.manage_fwupd:reboot_after_update:: + "$(_update_marker)" + delete => default:tidy, + comment => "Clear marker before issuing reboot"; + + commands: + manage_fwupd:_update_applied.manage_fwupd:reboot_after_update.manage_fwupd:_have_systemd_run:: + "$(_systemd_run)" + arglist => { + "--unit=cfengine-fwupd-reboot", + "--description=Reboot after cf-agent exits for firmware update", + "/bin/bash", "-c", + "while kill -0 $(this.promiser_pid) 2>/dev/null; do sleep 1; done; /sbin/shutdown -r now 'Rebooting for firmware update applied by CFEngine'" + }, + handle => "manage_fwupd_reboot_after_update", + comment => "Deferred reboot: polls for cf-agent to exit, then reboots immediately"; + + manage_fwupd:_update_applied.manage_fwupd:reboot_after_update.!manage_fwupd:_have_systemd_run:: + "/sbin/shutdown" + arglist => { "-r", "+1", "Rebooting for firmware update applied by CFEngine" }, + handle => "manage_fwupd_reboot_after_update_fallback", + comment => "Fallback reboot for non-systemd systems (shutdown -r +1)"; + + # --- Inventory -------------------------------------------------- + vars: + linux.default:have_fwupdmgr:: + "_status" string => "INSTALLED"; + + linux.!default:have_fwupdmgr:: + "_status" string => "NOT_INSTALLED"; + + !linux:: + "_status" string => "NOT_SUPPORTED"; + + any:: + "inventory_status" + string => "$(_status)", + meta => { "inventory", "attribute_name=fwupd management status" }; +} diff --git a/management/manage-fwupd/mp-inventory-fwupd-status.png b/management/manage-fwupd/mp-inventory-fwupd-status.png new file mode 100644 index 0000000..e2f702d Binary files /dev/null and b/management/manage-fwupd/mp-inventory-fwupd-status.png differ diff --git a/reporting/compliance-report-fwupd/README.org b/reporting/compliance-report-fwupd/README.org new file mode 100644 index 0000000..25f324c --- /dev/null +++ b/reporting/compliance-report-fwupd/README.org @@ -0,0 +1,137 @@ +Compliance report definition for firmware security posture via fwupd. + +Imports a "Firmware Security (fwupd)" report into Mission Portal that +tracks Host Security Identifier (HSI) levels, fwupd installation, and +firmware update status across the fleet. + +The report contains 49 conditions: 7 rolled-up checks (HSI level +thresholds, fwupd installation, update status) and 42 per-attribute +checks covering every individual HSI firmware security test. + +* Mission Portal + +[[file:mp-compliance-report-fwupd.png]] + +* Requirements + +- *inventory-fwupd* module providing the inventory attributes and + platform classes (=fwupd_cpu_vendor_intel=, =fwupd_cpu_vendor_amd=, + =fwupd_oem_vendor_hp=) that the compliance conditions reference +- *compliance-report-imports* autorun bundle on the hub to import + the JSON definition into Mission Portal + +* Rolled-up Conditions + +| Condition | Category | Severity | +|-----------------------------------+--------------------+----------| +| *fwupd installed* | Firmware tooling | high | +| *HSI Level 1+ (Critical)* | HSI Level Overview | high | +| *HSI Level 2+ (Important)* | HSI Level Overview | medium | +| *HSI Level 3+ (Recommended)* | HSI Level Overview | low | +| *HSI Level 4 (Complete)* | HSI Level Overview | low | +| *No pending firmware updates* | Firmware updates | medium | +| *Firmware status healthy* | Firmware updates | medium | + +HSI level checks are cumulative thresholds -- a host at HSI:3 passes +the Level 1+, 2+, and 3+ conditions but fails Level 4. + +* Per-Attribute Conditions + +Each individual HSI firmware security check is a separate compliance +condition. The inventory attribute name follows the pattern +=Firmware HSI L: = with value =PASS= or =FAIL=. + +Per-attribute conditions use ~condition_for: "failing"~ -- a host is +only marked failing when the attribute is explicitly =FAIL=. Hosts +that don't report a given attribute (e.g., Intel-only checks on AMD +hardware, or VMs without HSI data) show as "not evaluated" rather +than failing. The checks are defined by the +[[https://fwupd.github.io/libfwupdplugin/hsi.html][fwupd HSI specification]]. + +** Level 1 -- Critical (19 conditions, severity: high) + +| Condition | Inventory attribute | Platform | +|------------------------------+--------------------------------------------+----------| +| UEFI SecureBoot | =Firmware HSI L1: UEFI secure boot= | All | +| TPM 2.0 Present | =Firmware HSI L1: TPM v2.0= | All | +| Empty PCR in TPM | =Firmware HSI L1: TPM empty PCRs= | All | +| UEFI Platform Key | =Firmware HSI L1: UEFI platform key= | All | +| BIOS Capsule Updates | =Firmware HSI L1: BIOS firmware updates= | All | +| Supported CPU | =Firmware HSI L1: Supported CPU= | All | +| UEFI BootService Variables | =Firmware HSI L1: UEFI bootservice variables= | All | +| BIOS Write Enable (BWE) | =Firmware HSI L1: SPI write= | Intel | +| BIOS Lock Enable (BLE) | =Firmware HSI L1: SPI lock= | Intel | +| SMM BIOS Write Protect | =Firmware HSI L1: SPI BIOS region= | Intel | +| Read-only SPI Descriptor | =Firmware HSI L1: SPI descriptor= | Intel | +| Platform Debug (Intel DCI) | =Firmware HSI L1: Platform debugging= | Intel | +| ME Manufacturing Mode | =Firmware HSI L1: csme manufacturing mode= | Intel | +| ME Flash Descriptor Override | =Firmware HSI L1: csme override= | Intel | +| ME BootGuard Platform Key | =Firmware HSI L1: MEI key manifest= | Intel | +| CSME Version | =Firmware HSI L1: CSME version= | Intel | +| Part is Fused | =Firmware HSI L1: Part is fused= | Intel | +| AMD Microcode Signature | =Firmware HSI L1: AMD microcode signature= | AMD | +| SMM Locked Down | =Firmware HSI L1: SMM locked down= | AMD | + +** Level 2 -- Important (12 conditions, severity: medium) + +| Condition | Inventory attribute | Platform | +|----------------------------+-------------------------------------------------+----------| +| DMA Protection (IOMMU) | =Firmware HSI L2: IOMMU= | All | +| PCR0 TPM Event Log | =Firmware HSI L2: TPM PCR0 reconstruction= | All | +| BIOS Rollback Protection | =Firmware HSI L2: BIOS rollback protection= | All | +| Intel BootGuard Enabled | =Firmware HSI L2: Intel BootGuard= | Intel | +| Intel BootGuard Verified | =Firmware HSI L2: Intel BootGuard verified boot= | Intel | +| Intel BootGuard ACM | =Firmware HSI L2: Intel BootGuard ACM protected= | Intel | +| Intel BootGuard OTP | =Firmware HSI L2: Intel BootGuard OTP fuse= | Intel | +| Part is Debug Locked | =Firmware HSI L2: Platform debugging= | Intel | +| Intel GDS Mitigation | =Firmware HSI L2: Intel GDS mitigation= | Intel | +| AMD Platform Secure Boot | =Firmware HSI L2: AMD platform secure boot= | AMD | +| AMD SPI Write Protections | =Firmware HSI L2: AMD SPI write protections= | AMD | +| HP SureStart | =Firmware HSI L2: HP SureStart= | HP | + +** Level 3 -- Recommended (8 conditions, severity: low) + +| Condition | Inventory attribute | Platform | +|------------------------------------+----------------------------------------------+----------| +| Suspend-to-Idle | =Firmware HSI L3: Suspend-to-idle= | All | +| Suspend to RAM Disabled | =Firmware HSI L3: Suspend-to-ram= | All | +| Pre-boot DMA Protection | =Firmware HSI L3: Pre-boot DMA protection= | All | +| CET Available | =Firmware HSI L3: CET Platform= | All | +| CET Utilized by OS | =Firmware HSI L3: CET OS Support= | All | +| Early-boot UEFI Memory Protections | =Firmware HSI L3: UEFI memory protections= | All | +| Intel BootGuard Policy | =Firmware HSI L3: Intel BootGuard error policy= | Intel | +| AMD SPI Replay Protections | =Firmware HSI L3: AMD SPI replay protections= | AMD | + +** Level 4 -- Complete (3 conditions, severity: low) + +| Condition | Inventory attribute | Platform | +|-------------------------------+--------------------------------------------+----------| +| DRAM Memory Encryption | =Firmware HSI L4: Encrypted RAM= | All | +| SMAP | =Firmware HSI L4: SMAP= | All | +| AMD Secure Processor Rollback | =Firmware HSI L4: AMD rollback protection= | AMD | + +* Categories + +| Category | Conditions | Scope | +|---------------------------+------------+----------------------------| +| HSI Level Overview | 4 | Rolled-up level thresholds | +| HSI Level 1 - Critical | 19 | Per-attribute checks | +| HSI Level 2 - Important | 12 | Per-attribute checks | +| HSI Level 3 - Recommended | 7 | Per-attribute checks | +| HSI Level 4 - Complete | 4 | Per-attribute checks | +| Firmware updates | 2 | Update status | +| Firmware tooling | 1 | fwupd installation | + +* Platform support + +Linux only. Platform-specific conditions use =host_filter= class +expressions so they only activate on relevant hardware: + +| Platform | =host_filter= | Conditions | Detection | +|----------+----------------------------+------------+--------------------------------| +| All | =linux= | 25 | -- | +| Intel | =fwupd_cpu_vendor_intel= | 17 | =/proc/cpuinfo= vendor_id | +| AMD | =fwupd_cpu_vendor_amd= | 6 | =/proc/cpuinfo= vendor_id | +| HP | =fwupd_oem_vendor_hp= | 1 | =/sys/class/dmi/id/sys_vendor= | + +These classes are defined by the *inventory-fwupd* module. diff --git a/reporting/compliance-report-fwupd/fwupd-compliance-report.json b/reporting/compliance-report-fwupd/fwupd-compliance-report.json new file mode 100644 index 0000000..d4dd021 --- /dev/null +++ b/reporting/compliance-report-fwupd/fwupd-compliance-report.json @@ -0,0 +1,895 @@ +{ + "reports": { + "fwupd-firmware-security": { + "id": "fwupd-firmware-security", + "type": "compliance", + "title": "Firmware Security (fwupd)", + "conditions": [ + "fwupd:hsi-installed", + "fwupd:hsi-level-1", + "fwupd:hsi-level-2", + "fwupd:hsi-level-3", + "fwupd:hsi-level-4", + "fwupd:no-pending-updates", + "fwupd:status-ok", + "fwupd:hsi-l1-uefi-secure-boot", + "fwupd:hsi-l1-tpm-v2.0", + "fwupd:hsi-l1-tpm-empty-pcrs", + "fwupd:hsi-l1-uefi-platform-key", + "fwupd:hsi-l1-bios-firmware-updates", + "fwupd:hsi-l1-spi-write", + "fwupd:hsi-l1-spi-lock", + "fwupd:hsi-l1-spi-bios-region", + "fwupd:hsi-l1-spi-descriptor", + "fwupd:hsi-l1-supported-cpu", + "fwupd:hsi-l1-platform-debugging", + "fwupd:hsi-l1-csme-manufacturing-mode", + "fwupd:hsi-l1-csme-override", + "fwupd:hsi-l1-mei-key-manifest", + "fwupd:hsi-l1-csme-version", + "fwupd:hsi-l1-uefi-bootservice-vars", + "fwupd:hsi-l1-amd-microcode-signature", + "fwupd:hsi-l1-platform-fused", + "fwupd:hsi-l1-smm-locked-down", + "fwupd:hsi-l2-iommu", + "fwupd:hsi-l2-intel-bootguard", + "fwupd:hsi-l2-intel-bootguard-verified", + "fwupd:hsi-l2-intel-bootguard-acm", + "fwupd:hsi-l2-intel-bootguard-otp", + "fwupd:hsi-l2-tpm-pcr0-reconstruction", + "fwupd:hsi-l2-bios-rollback-protection", + "fwupd:hsi-l2-platform-debugging", + "fwupd:hsi-l2-intel-gds-mitigation", + "fwupd:hsi-l2-amd-platform-secure-boot", + "fwupd:hsi-l2-amd-spi-write-protections", + "fwupd:hsi-l2-hp-surestart", + "fwupd:hsi-l3-suspend-to-idle", + "fwupd:hsi-l3-suspend-to-ram", + "fwupd:hsi-l3-pre-boot-dma-protection", + "fwupd:hsi-l3-intel-bootguard-policy", + "fwupd:hsi-l3-cet-platform", + "fwupd:hsi-l3-amd-spi-replay-protections", + "fwupd:hsi-l3-uefi-memory-protections", + "fwupd:hsi-l4-encrypted-ram", + "fwupd:hsi-l4-smap", + "fwupd:hsi-l3-cet-os-support", + "fwupd:hsi-l4-amd-rollback-protection" + ] + } + }, + "conditions": { + "fwupd:hsi-installed": { + "id": "fwupd:hsi-installed", + "name": "fwupd installed", + "description": "Checks that fwupd is installed on the host.\n\nHosts without fwupd report a firmware update status of FWUPD_MISSING and cannot provide firmware security inventory. This is a prerequisite for all other firmware security checks.", + "type": "inventory", + "condition_for": "passing", + "rules": [ + { + "attribute": "Firmware update status", + "operator": "not_match", + "value": "FWUPD_MISSING" + } + ], + "category": "Firmware tooling", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-level-1": { + "id": "fwupd:hsi-level-1", + "name": "HSI Level 1+ - Critical firmware protections", + "description": "Rolled-up check: the host achieves at least HSI Level 1 (Critical). See the individual Level 1 conditions for specific checks.", + "type": "inventory", + "condition_for": "passing", + "rules": [ + { + "attribute": "Firmware HSI level", + "operator": "not_match", + "value": "HSI:0" + } + ], + "category": "HSI Level Overview", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-level-2": { + "id": "fwupd:hsi-level-2", + "name": "HSI Level 2+ - Important firmware protections", + "description": "Rolled-up check: the host achieves at least HSI Level 2 (Important). See the individual Level 2 conditions for specific checks.", + "type": "inventory", + "condition_for": "passing", + "rules": [ + { + "attribute": "Firmware HSI level", + "operator": "regex_matches", + "value": "HSI:[2-4]" + } + ], + "category": "HSI Level Overview", + "severity": "medium", + "host_filter": "linux" + }, + "fwupd:hsi-level-3": { + "id": "fwupd:hsi-level-3", + "name": "HSI Level 3+ - Recommended firmware protections", + "description": "Rolled-up check: the host achieves at least HSI Level 3 (Recommended). See the individual Level 3 conditions for specific checks.", + "type": "inventory", + "condition_for": "passing", + "rules": [ + { + "attribute": "Firmware HSI level", + "operator": "regex_matches", + "value": "HSI:[3-4]" + } + ], + "category": "HSI Level Overview", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-level-4": { + "id": "fwupd:hsi-level-4", + "name": "HSI Level 4 - Complete firmware protections", + "description": "Rolled-up check: the host achieves HSI Level 4 (Complete). See the individual Level 4 conditions for specific checks.", + "type": "inventory", + "condition_for": "passing", + "rules": [ + { + "attribute": "Firmware HSI level", + "operator": "matches", + "value": "HSI:4" + } + ], + "category": "HSI Level Overview", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:no-pending-updates": { + "id": "fwupd:no-pending-updates", + "name": "No pending firmware updates", + "description": "Checks that the host has no outstanding firmware updates.\n\nHosts with pending firmware updates have known firmware versions that should be applied. Use the manage-fwupd module to automate firmware update application.", + "type": "inventory", + "condition_for": "passing", + "rules": [ + { + "attribute": "Firmware update status", + "operator": "not_match", + "value": "UPDATES_AVAILABLE" + } + ], + "category": "Firmware updates", + "severity": "medium", + "host_filter": "linux" + }, + "fwupd:status-ok": { + "id": "fwupd:status-ok", + "name": "Firmware status healthy", + "description": "Checks that fwupd is present, has discoverable devices, and no pending updates.\n\nThis is the strictest firmware status check. A host passes only when fwupd is installed, reports at least one updatable device, and has no outstanding firmware updates.", + "type": "inventory", + "condition_for": "passing", + "rules": [ + { + "attribute": "Firmware update status", + "operator": "matches", + "value": "OK" + } + ], + "category": "Firmware updates", + "severity": "medium", + "host_filter": "linux" + }, + "fwupd:hsi-l1-uefi-secure-boot": { + "id": "fwupd:hsi-l1-uefi-secure-boot", + "name": "UEFI SecureBoot", + "description": "Secure Boot is enabled and functional.\n\nUEFI Secure Boot prevents unauthorized code from running during the boot process by verifying digital signatures of boot loaders and drivers.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: UEFI secure boot", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-l1-tpm-v2.0": { + "id": "fwupd:hsi-l1-tpm-v2.0", + "name": "TPM 2.0 Present", + "description": "TPM 2.0 device exists and is enabled.\n\nA Trusted Platform Module provides hardware-backed cryptographic operations and secure measurement storage for verified boot.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: TPM v2.0", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-l1-tpm-empty-pcrs": { + "id": "fwupd:hsi-l1-tpm-empty-pcrs", + "name": "Empty PCR in TPM", + "description": "All TPM PCRs 0-7 have valid measurements.\n\nEmpty Platform Configuration Registers indicate that the firmware boot process is not being measured, weakening the chain of trust.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: TPM empty PCRs", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-l1-uefi-platform-key": { + "id": "fwupd:hsi-l1-uefi-platform-key", + "name": "UEFI Platform Key", + "description": "Valid production platform key is installed (not a test key).\n\nThe UEFI Platform Key (PK) is the root of trust for Secure Boot. A test key allows any code to be signed and run.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: UEFI platform key", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-l1-bios-firmware-updates": { + "id": "fwupd:hsi-l1-bios-firmware-updates", + "name": "BIOS Capsule Updates", + "description": "Firmware update mechanism (UEFI capsule updates) is enabled.\n\nWhen disabled, the system cannot receive firmware security patches through the standard UEFI update mechanism.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: BIOS firmware updates", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-l1-spi-write": { + "id": "fwupd:hsi-l1-spi-write", + "name": "BIOS Write Enable (BWE)", + "description": "SPI ROM is protected from userspace writes.\n\nThe BIOS Write Enable bit controls whether the SPI flash containing the BIOS can be written from the operating system. It should be disabled to prevent firmware tampering.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: SPI write", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-spi-lock": { + "id": "fwupd:hsi-l1-spi-lock", + "name": "BIOS Lock Enable (BLE)", + "description": "SMI protection on BIOSWE bit changes is enabled.\n\nBIOS Lock Enable generates a System Management Interrupt when software attempts to change the BIOS Write Enable bit, preventing silent firmware tampering.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: SPI lock", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-spi-bios-region": { + "id": "fwupd:hsi-l1-spi-bios-region", + "name": "SMM BIOS Write Protect", + "description": "BIOS region is non-writable outside System Management Mode.\n\nSMM BIOS Write Protect ensures that the BIOS SPI flash region cannot be modified except from within the trusted SMM environment.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: SPI BIOS region", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-spi-descriptor": { + "id": "fwupd:hsi-l1-spi-descriptor", + "name": "Read-only SPI Descriptor", + "description": "SPI flash descriptor region is locked.\n\nThe SPI descriptor defines access permissions for SPI flash regions. If unlocked, an attacker could modify these permissions to gain write access to the BIOS.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: SPI descriptor", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-supported-cpu": { + "id": "fwupd:hsi-l1-supported-cpu", + "name": "Supported CPU", + "description": "Platform CPU has HSI test support in fwupd.\n\nUnsupported CPUs cannot have their firmware security attributes fully evaluated.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: Supported CPU", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-l1-platform-debugging": { + "id": "fwupd:hsi-l1-platform-debugging", + "name": "Platform Debug (Intel DCI)", + "description": "USB3 debugging interface (Intel DCI) is disabled.\n\nDirect Connect Interface allows deep hardware debugging over USB3. If enabled in production, it provides an attack surface for physical access exploits.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: Platform debugging", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-csme-manufacturing-mode": { + "id": "fwupd:hsi-l1-csme-manufacturing-mode", + "name": "ME Manufacturing Mode", + "description": "Intel Management Engine is not in manufacturing mode.\n\nManufacturing mode is intended for factory provisioning. If left enabled, it exposes additional attack surface on the ME subsystem.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: csme manufacturing mode", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-csme-override": { + "id": "fwupd:hsi-l1-csme-override", + "name": "ME Flash Descriptor Override", + "description": "Intel ME flash descriptor override (debug mode) is not accessible.\n\nThe override strap allows bypassing SPI flash access controls, enabling firmware tampering through physical access.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: csme override", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-mei-key-manifest": { + "id": "fwupd:hsi-l1-mei-key-manifest", + "name": "ME BootGuard Platform Key", + "description": "CPU fuses are not using leaked or compromised keys.\n\nThe MEI key manifest validates that the BootGuard keys burned into the CPU fuses have not been compromised.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: MEI key manifest", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-csme-version": { + "id": "fwupd:hsi-l1-csme-version", + "name": "CSME Version", + "description": "Intel Converged Security and Management Engine firmware is not vulnerable to known critical CVEs.\n\nOutdated CSME versions may contain exploitable vulnerabilities that compromise the hardware root of trust.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: CSME version", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-uefi-bootservice-vars": { + "id": "fwupd:hsi-l1-uefi-bootservice-vars", + "name": "UEFI BootService Variables", + "description": "Boot-only UEFI variables are not readable at OS runtime.\n\nBootService variables should be inaccessible after ExitBootServices() to prevent information leakage to the operating system.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: UEFI bootservice variables", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "linux" + }, + "fwupd:hsi-l1-amd-microcode-signature": { + "id": "fwupd:hsi-l1-amd-microcode-signature", + "name": "AMD Microcode Signature", + "description": "Firmware prevents loading of maliciously crafted CPU microcode.\n\nAMD processors vulnerable to microcode signature verification bypass (EntrySign) allow arbitrary microcode patches, undermining hardware security guarantees.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: AMD microcode signature", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_amd" + }, + "fwupd:hsi-l1-platform-fused": { + "id": "fwupd:hsi-l1-platform-fused", + "name": "Part is Fused", + "description": "Hardware one-time-programmable fuses are blown for tampering protection.\n\nProduction hardware should have security fuses blown to lock down debugging interfaces and enforce boot chain verification.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: Part is fused", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l1-smm-locked-down": { + "id": "fwupd:hsi-l1-smm-locked-down", + "name": "SMM Locked Down", + "description": "System Management Mode save state protection prevents data exfiltration.\n\nSMM lockdown prevents SMM handlers from reading or modifying OS memory, closing a class of privilege escalation attacks.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L1: SMM locked down", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 1 - Critical", + "severity": "high", + "host_filter": "fwupd_cpu_vendor_amd" + }, + "fwupd:hsi-l2-iommu": { + "id": "fwupd:hsi-l2-iommu", + "name": "DMA Protection (IOMMU)", + "description": "IOMMU is enabled for PCIe DMA attack prevention.\n\nThe IOMMU restricts DMA-capable devices to their assigned memory regions, preventing malicious peripherals from reading or writing arbitrary system memory.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: IOMMU", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "linux" + }, + "fwupd:hsi-l2-intel-bootguard": { + "id": "fwupd:hsi-l2-intel-bootguard", + "name": "Intel BootGuard Enabled", + "description": "Intel BootGuard feature is activated.\n\nBootGuard provides hardware-rooted verified boot by validating the Initial Boot Block before execution.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: Intel BootGuard", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l2-intel-bootguard-verified": { + "id": "fwupd:hsi-l2-intel-bootguard-verified", + "name": "Intel BootGuard Verified", + "description": "Intel BootGuard boot chain verification is functional.\n\nVerified Boot mode ensures each stage of the boot process is cryptographically validated before execution.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: Intel BootGuard verified boot", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l2-intel-bootguard-acm": { + "id": "fwupd:hsi-l2-intel-bootguard-acm", + "name": "Intel BootGuard ACM", + "description": "Intel Authenticated Code Modules are protected.\n\nACMs are signed microcode modules executed by the CPU before any other code. Their integrity is essential for the hardware root of trust.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: Intel BootGuard ACM protected", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l2-intel-bootguard-otp": { + "id": "fwupd:hsi-l2-intel-bootguard-otp", + "name": "Intel BootGuard OTP", + "description": "SOC is locked via one-time programmable fuses for BootGuard.\n\nOTP fuses permanently bind the BootGuard key hash to the CPU, preventing key substitution attacks.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: Intel BootGuard OTP fuse", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l2-tpm-pcr0-reconstruction": { + "id": "fwupd:hsi-l2-tpm-pcr0-reconstruction", + "name": "PCR0 TPM Event Log", + "description": "TPM PCR0 value matches the reconstructed firmware measurements.\n\nPCR0 reconstruction verifies that the TPM event log is consistent with the actual PCR values, detecting tampering with boot measurements.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: TPM PCR0 reconstruction", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "linux" + }, + "fwupd:hsi-l2-bios-rollback-protection": { + "id": "fwupd:hsi-l2-bios-rollback-protection", + "name": "BIOS Rollback Protection", + "description": "BIOS prevents installing older vulnerable firmware versions.\n\nWithout rollback protection, an attacker with firmware write access could downgrade to a version with known vulnerabilities.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: BIOS rollback protection", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "linux" + }, + "fwupd:hsi-l2-platform-debugging": { + "id": "fwupd:hsi-l2-platform-debugging", + "name": "Part is Debug Locked", + "description": "Hardware debugging access is locked.\n\nDebug interfaces (JTAG, etc.) allow full hardware access. In production systems these should be permanently locked to prevent physical attack vectors.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: Platform debugging", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l2-intel-gds-mitigation": { + "id": "fwupd:hsi-l2-intel-gds-mitigation", + "name": "Intel GDS Mitigation", + "description": "CPU microcode patches mitigate Gather Data Sampling.\n\nGDS (Downfall) is a transient execution side-channel vulnerability in Intel CPUs that can leak data across security boundaries.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: Intel GDS mitigation", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l2-amd-platform-secure-boot": { + "id": "fwupd:hsi-l2-amd-platform-secure-boot", + "name": "AMD Platform Secure Boot", + "description": "AMD Platform Secure Boot prevents non-manufacturer firmware execution.\n\nThe AMD Secure Processor validates firmware authenticity before allowing execution, providing a hardware root of trust.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: AMD platform secure boot", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_amd" + }, + "fwupd:hsi-l2-amd-spi-write-protections": { + "id": "fwupd:hsi-l2-amd-spi-write-protections", + "name": "AMD SPI Write Protections", + "description": "SPI bus write control prevents unauthorized firmware changes on AMD systems.\n\nAMD SPI write protections lock the SPI flash to prevent unauthorized firmware modifications.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: AMD SPI write protections", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_cpu_vendor_amd" + }, + "fwupd:hsi-l2-hp-surestart": { + "id": "fwupd:hsi-l2-hp-surestart", + "name": "HP SureStart", + "description": "HP SureStart BIOS self-healing and corruption detection is enabled.\n\nHP SureStart monitors the BIOS for corruption or tampering and automatically restores it from a protected copy.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L2: HP SureStart", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 2 - Important", + "severity": "medium", + "host_filter": "fwupd_oem_vendor_hp" + }, + "fwupd:hsi-l3-suspend-to-idle": { + "id": "fwupd:hsi-l3-suspend-to-idle", + "name": "Suspend-to-Idle", + "description": "Default sleep state is suspend-to-idle (S0ix) rather than S3.\n\nSuspend-to-idle keeps the IOMMU and other security features active during sleep, unlike S3 which powers down protections.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: Suspend-to-idle", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l3-suspend-to-ram": { + "id": "fwupd:hsi-l3-suspend-to-ram", + "name": "Suspend to RAM Disabled", + "description": "S3 suspend-to-RAM sleep is not available or not the default.\n\nS3 sleep powers down the IOMMU, disabling DMA protections and creating a window for cold boot or DMA attacks on resume.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: Suspend-to-ram", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l3-pre-boot-dma-protection": { + "id": "fwupd:hsi-l3-pre-boot-dma-protection", + "name": "Pre-boot DMA Protection", + "description": "IOMMU is configured for pre-boot DMA attack mitigation.\n\nPre-boot DMA protection ensures the IOMMU is active before the OS loads, preventing DMA attacks during early boot when the OS IOMMU driver is not yet running.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: Pre-boot DMA protection", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l3-intel-bootguard-policy": { + "id": "fwupd:hsi-l3-intel-bootguard-policy", + "name": "Intel BootGuard Policy", + "description": "BootGuard error enforcement policy halts boot on verification failure.\n\nIn enforcement mode, BootGuard stops the boot process if signature verification fails rather than continuing with potentially compromised firmware.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: Intel BootGuard error policy", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "fwupd_cpu_vendor_intel" + }, + "fwupd:hsi-l3-cet-platform": { + "id": "fwupd:hsi-l3-cet-platform", + "name": "CET Available", + "description": "Control Flow Enforcement Technology is supported and enabled.\n\nCET provides hardware-enforced shadow stacks and indirect branch tracking to mitigate return-oriented programming (ROP) and jump-oriented programming (JOP) attacks.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: CET Platform", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l3-amd-spi-replay-protections": { + "id": "fwupd:hsi-l3-amd-spi-replay-protections", + "name": "AMD SPI Replay Protections", + "description": "Monotonic counter prevents SPI flash replay attacks on AMD systems.\n\nSPI replay protection uses a hardware counter to detect when an older SPI flash image has been re-applied, preventing firmware downgrade attacks.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: AMD SPI replay protections", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "fwupd_cpu_vendor_amd" + }, + "fwupd:hsi-l3-uefi-memory-protections": { + "id": "fwupd:hsi-l3-uefi-memory-protections", + "name": "Early-boot UEFI Memory Protections", + "description": "NX and read-only protections are applied to UEFI boot memory.\n\nEarly-boot memory protections prevent code injection and data corruption during the UEFI boot phase by enforcing W^X (write XOR execute) policies.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: UEFI memory protections", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l4-encrypted-ram": { + "id": "fwupd:hsi-l4-encrypted-ram", + "name": "DRAM Memory Encryption", + "description": "Total Memory Encryption (Intel TME) or Secure Memory Encryption (AMD SME) encrypts all data on the memory bus.\n\nMemory encryption protects against physical memory access attacks including cold boot attacks and DMA-based memory scraping.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L4: Encrypted RAM", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 4 - Complete", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l4-smap": { + "id": "fwupd:hsi-l4-smap", + "name": "SMAP", + "description": "Supervisor Mode Access Prevention is enabled.\n\nSMAP prevents the kernel from accidentally accessing user-space memory, mitigating a class of privilege escalation exploits that trick the kernel into dereferencing user-controlled pointers.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L4: SMAP", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 4 - Complete", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l3-cet-os-support": { + "id": "fwupd:hsi-l3-cet-os-support", + "name": "CET Utilized by OS", + "description": "The operating system actively uses Control Flow Enforcement Technology.\n\nWhile CET hardware support (Level 3) is necessary, the OS must also enable and use it for the protection to be effective.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L3: CET OS Support", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 3 - Recommended", + "severity": "low", + "host_filter": "linux" + }, + "fwupd:hsi-l4-amd-rollback-protection": { + "id": "fwupd:hsi-l4-amd-rollback-protection", + "name": "AMD Secure Processor Rollback", + "description": "AMD Secure Processor prevents firmware downgrade attacks.\n\nThe AMD Platform Security Processor maintains anti-rollback counters that prevent installation of older firmware versions with known vulnerabilities.", + "type": "inventory", + "condition_for": "failing", + "rules": [ + { + "attribute": "Firmware HSI L4: AMD rollback protection", + "operator": "matches", + "value": "FAIL" + } + ], + "category": "HSI Level 4 - Complete", + "severity": "low", + "host_filter": "fwupd_cpu_vendor_amd" + } + } +} diff --git a/reporting/compliance-report-fwupd/mp-compliance-report-fwupd.png b/reporting/compliance-report-fwupd/mp-compliance-report-fwupd.png new file mode 100644 index 0000000..366ceb9 Binary files /dev/null and b/reporting/compliance-report-fwupd/mp-compliance-report-fwupd.png differ