doc: update output formats doc + normalize json to bool

built from commit e2d110a3b5
 dated 2026-04-20 12:47:43 +0200
 by Stéphane Lesimple (speed47_github@speed47.net)
This commit is contained in:
github-actions[bot]
2026-04-20 10:56:59 +00:00
parent 4eb0d04808
commit cf156a2ee5
5 changed files with 84 additions and 36 deletions

View File

@@ -102,7 +102,9 @@ boundaries by a malicious guest. Prioritise remediation where
### `cpu` ### `cpu`
CPU hardware identification. `null` when `--no-hw` is active. CPU hardware identification. `null` when `--no-hw` is active, or when
`--arch-prefix` is set (host CPU info is then suppressed to avoid mixing
with a different-arch target kernel).
The object uses `arch` as a discriminator: `"x86"` for Intel/AMD/Hygon CPUs, The object uses `arch` as a discriminator: `"x86"` for Intel/AMD/Hygon CPUs,
`"arm"` for ARM/Cavium/Phytium. Arch-specific fields live under a matching `"arm"` for ARM/Cavium/Phytium. Arch-specific fields live under a matching
@@ -140,7 +142,7 @@ fields from the other architecture.
#### `cpu.x86.capabilities` #### `cpu.x86.capabilities`
Each capability is a **tri-state**: `true` (present), `false` (absent), or Every capability is a **tri-state**: `true` (present), `false` (absent), or
`null` (not applicable or could not be read, e.g. when not root or on AMD for `null` (not applicable or could not be read, e.g. when not root or on AMD for
Intel-specific features). Intel-specific features).
@@ -238,7 +240,7 @@ with an unknown CVE ID).
| `status` | string | `"OK"` / `"VULN"` / `"UNK"` | Check outcome (see below) | | `status` | string | `"OK"` / `"VULN"` / `"UNK"` | Check outcome (see below) |
| `vulnerable` | boolean \| null | `false` / `true` / `null` | `false`=OK, `true`=VULN, `null`=UNK | | `vulnerable` | boolean \| null | `false` / `true` / `null` | `false`=OK, `true`=VULN, `null`=UNK |
| `info` | string | | Human-readable description of the specific mitigation state or reason | | `info` | string | | Human-readable description of the specific mitigation state or reason |
| `sysfs_status` | string \| null | `"OK"` / `"VULN"` / `"UNK"` / null | Status as reported by the kernel via `/sys/devices/system/cpu/vulnerabilities/`; null if sysfs was not consulted for this CVE | | `sysfs_status` | string \| null | `"OK"` / `"VULN"` / `"UNK"` / null | Status as reported by the kernel via `/sys/devices/system/cpu/vulnerabilities/`; null if sysfs was not consulted for this CVE, or if the CVE's check read sysfs in silent/quiet mode (raw message is still captured in `sysfs_message`) |
| `sysfs_message` | string \| null | | Raw text from the sysfs file (e.g. `"Mitigation: PTI"`); null if sysfs was not consulted | | `sysfs_message` | string \| null | | Raw text from the sysfs file (e.g. `"Mitigation: PTI"`); null if sysfs was not consulted |
#### Status values #### Status values

View File

@@ -127,7 +127,7 @@
}, },
"cpu": { "cpu": {
"description": "CPU hardware identification. Null when --no-hw is active. Contains an 'arch' discriminator ('x86' or 'arm') and a matching arch-specific sub-object with identification fields and capabilities.", "description": "CPU hardware identification. Null when --no-hw is active or when --arch-prefix is set (host CPU info is then suppressed to avoid mixing with a different-arch target kernel). Contains an 'arch' discriminator ('x86' or 'arm') and a matching arch-specific sub-object with identification fields and capabilities.",
"oneOf": [ "oneOf": [
{ "type": "null" }, { "type": "null" },
{ {
@@ -180,16 +180,16 @@
"type": ["string", "null"] "type": ["string", "null"]
}, },
"capabilities": { "capabilities": {
"description": "CPU feature flags detected via CPUID and MSR reads. Each value is true (present), false (absent), or null (not applicable or could not be read).", "description": "CPU feature flags detected via CPUID and MSR reads. Every value is tri-state: true=present, false=absent, null=not applicable or unreadable.",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"spec_ctrl": { "type": ["boolean", "null"], "description": "SPEC_CTRL MSR present (Intel; enables IBRS + IBPB via WRMSR)" }, "spec_ctrl": { "type": ["boolean", "null"], "description": "SPEC_CTRL MSR present (Intel; enables IBRS + IBPB via WRMSR)" },
"ibrs": { "type": ["boolean", "null"], "description": "Indirect Branch Restricted Speculation" }, "ibrs": { "type": ["boolean", "null"], "description": "IBRS supported (via SPEC_CTRL, IBRS_SUPPORT, or cpuinfo fallback)" },
"ibpb": { "type": ["boolean", "null"], "description": "Indirect Branch Prediction Barrier" }, "ibpb": { "type": ["boolean", "null"], "description": "IBPB supported (via SPEC_CTRL, IBPB_SUPPORT, or cpuinfo fallback)" },
"ibpb_ret": { "type": ["boolean", "null"], "description": "IBPB on return (enhanced form)" }, "ibpb_ret": { "type": ["boolean", "null"], "description": "IBPB on return (enhanced form)" },
"stibp": { "type": ["boolean", "null"], "description": "Single Thread Indirect Branch Predictors" }, "stibp": { "type": ["boolean", "null"], "description": "STIBP supported (Intel/AMD/HYGON or cpuinfo fallback)" },
"ssbd": { "type": ["boolean", "null"], "description": "Speculative Store Bypass Disable" }, "ssbd": { "type": ["boolean", "null"], "description": "SSBD supported (SPEC_CTRL, VIRT_SPEC_CTRL, non-architectural MSR, or cpuinfo fallback)" },
"l1d_flush": { "type": ["boolean", "null"], "description": "L1D cache flush instruction" }, "l1d_flush": { "type": ["boolean", "null"], "description": "L1D cache flush instruction" },
"md_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers (MDS mitigation)" }, "md_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers (MDS mitigation)" },
"arch_capabilities": { "type": ["boolean", "null"], "description": "IA32_ARCH_CAPABILITIES MSR is present" }, "arch_capabilities": { "type": ["boolean", "null"], "description": "IA32_ARCH_CAPABILITIES MSR is present" },
@@ -231,7 +231,7 @@
"tsa_l1_no": { "type": ["boolean", "null"], "description": "Not susceptible to TSA-L1" }, "tsa_l1_no": { "type": ["boolean", "null"], "description": "Not susceptible to TSA-L1" },
"verw_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers" }, "verw_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers" },
"autoibrs": { "type": ["boolean", "null"], "description": "AMD AutoIBRS (equivalent to enhanced IBRS on Intel)" }, "autoibrs": { "type": ["boolean", "null"], "description": "AMD AutoIBRS (equivalent to enhanced IBRS on Intel)" },
"sbpb": { "type": ["boolean", "null"], "description": "Selective Branch Predictor Barrier (AMD Inception mitigation)" }, "sbpb": { "type": ["boolean", "null"], "description": "Selective Branch Predictor Barrier (AMD Inception mitigation): true if PRED_CMD MSR SBPB bit write succeeded; false if write failed; null if not verifiable (non-root, CPUID error, or CPU does not report SBPB support)" },
"avx2": { "type": ["boolean", "null"], "description": "AVX2 supported (relevant to Downfall / GDS)" }, "avx2": { "type": ["boolean", "null"], "description": "AVX2 supported (relevant to Downfall / GDS)" },
"avx512": { "type": ["boolean", "null"], "description": "AVX-512 supported (relevant to Downfall / GDS)" } "avx512": { "type": ["boolean", "null"], "description": "AVX-512 supported (relevant to Downfall / GDS)" }
} }

View File

@@ -51,6 +51,7 @@ STATUS: summary | perfdata
| VULN + UNK | `N/T CVE(s) vulnerable: CVE-A CVE-B ..., M inconclusive` | | VULN + UNK | `N/T CVE(s) vulnerable: CVE-A CVE-B ..., M inconclusive` |
| UNK only | `N/T CVE checks inconclusive` | | UNK only | `N/T CVE checks inconclusive` |
| Non-root + VULN | `N/T CVE(s) appear vulnerable (unconfirmed, not root): CVE-A ...` | | Non-root + VULN | `N/T CVE(s) appear vulnerable (unconfirmed, not root): CVE-A ...` |
| Non-root + VULN + UNK | `N/T CVE(s) appear vulnerable (unconfirmed, not root): CVE-A ..., M inconclusive` |
### Lines 2+ (long output) ### Lines 2+ (long output)
@@ -59,15 +60,19 @@ Never parsed by the monitoring core; safe to add or reorder.
#### Context notes #### Context notes
Printed before per-CVE details when applicable: Printed before per-CVE details when applicable. Notes are emitted in this
order when more than one applies:
| Note | Condition | | Note | Condition |
|---|---| |---|---|
| `NOTE: paranoid mode active, stricter mitigation requirements applied` | `--paranoid` was used | | `NOTE: paranoid mode active, stricter mitigation requirements applied` | `--paranoid` was used |
| `NOTE: hypervisor host detected (reason); L1TF/MDS severity is elevated` | System is a VM host (KVM, Xen, VMware…) | | `NOTE: hypervisor host detected (reason); L1TF/MDS severity is elevated` | System is detected as a VM host (KVM, Xen, VMware…) |
| `NOTE: not a hypervisor host` | System is confirmed not a VM host | | `NOTE: not a hypervisor host` | System is confirmed not a VM host |
| `NOTE: not running as root; MSR reads skipped, results may be incomplete` | Script ran without root privileges | | `NOTE: not running as root; MSR reads skipped, results may be incomplete` | Script ran without root privileges |
When VMM detection did not run (e.g. `--no-hw`), neither the
`hypervisor host detected` nor the `not a hypervisor host` note is printed.
#### Per-CVE detail lines #### Per-CVE detail lines
One line per non-OK CVE. VULN entries (`[CRITICAL]`) appear before UNK One line per non-OK CVE. VULN entries (`[CRITICAL]`) appear before UNK

View File

@@ -90,13 +90,16 @@ smc_build_info{version="25.30.0250400123",mode="live",run_as_root="true",paranoi
Operating system and kernel metadata. Always value `1`. Operating system and kernel metadata. Always value `1`.
Absent in offline mode when neither `uname -r` nor `uname -m` is available. Absent entirely when none of `kernel_release`, `kernel_arch`, or
`hypervisor_host` can be determined (e.g. non-live mode with no VMM detection).
Each label is emitted only when its value is known; missing labels are
omitted rather than set to an empty string.
| Label | Values | Meaning | | Label | Values | Meaning |
|---|---|---| |---|---|---|
| `kernel_release` | string | Output of `uname -r` (live mode only) | | `kernel_release` | string | Output of `uname -r`; emitted only in live mode |
| `kernel_arch` | string | Output of `uname -m` (live mode only) | | `kernel_arch` | string | Output of `uname -m`; emitted only in live mode |
| `hypervisor_host` | `true` / `false` | Whether this machine is detected as a hypervisor host (running KVM, Xen, VMware, etc.) | | `hypervisor_host` | `true` / `false` | Whether this machine is detected as a hypervisor host (running KVM, Xen, VMware, etc.); absent when VMM detection did not run (e.g. `--no-hw`) |
**Example:** **Example:**
``` ```
@@ -114,26 +117,47 @@ a malicious guest. Always prioritise remediation on hosts where
### `smc_cpu_info` ### `smc_cpu_info`
CPU hardware and microcode metadata. Always value `1`. Absent when `--no-hw` CPU hardware and microcode metadata. Always value `1`. Absent when `--no-hw`
is used. is used or when `--arch-prefix` is set (host CPU info is suppressed to avoid
mixing with a different-arch target kernel).
Common labels (always emitted when the data is available):
| Label | Values | Meaning | | Label | Values | Meaning |
|---|---|---| |---|---|---|
| `vendor` | string | CPU vendor (e.g. `Intel`, `AuthenticAMD`) | | `vendor` | string | CPU vendor (e.g. `GenuineIntel`, `AuthenticAMD`, `HygonGenuine`, `ARM`) |
| `model` | string | CPU friendly name from `/proc/cpuinfo` | | `model` | string | CPU friendly name from `/proc/cpuinfo` |
| `arch` | `x86` / `arm` | Architecture family; determines which arch-specific labels follow |
| `smt` | `true` / `false` | Whether SMT (HyperThreading) is currently enabled; absent if undeterminable |
| `microcode` | hex string | Installed microcode version (e.g. `0xf4`); absent if unreadable |
| `microcode_latest` | hex string | Latest known-good microcode version from the firmware database; absent if the CPU is not in the database |
| `microcode_up_to_date` | `true` / `false` | Whether `microcode == microcode_latest`; absent if either is unavailable |
| `microcode_blacklisted` | `true` / `false` | Whether the installed microcode is known to cause problems and should be rolled back; emitted whenever `microcode` is emitted |
x86-only labels (emitted when `arch="x86"`):
| Label | Values | Meaning |
|---|---|---|
| `family` | integer string | CPU family number | | `family` | integer string | CPU family number |
| `model_id` | integer string | CPU model number | | `model_id` | integer string | CPU model number |
| `stepping` | integer string | CPU stepping number | | `stepping` | integer string | CPU stepping number |
| `cpuid` | hex string | Full CPUID value (e.g. `0x000906ed`); absent on some ARM CPUs | | `cpuid` | hex string | Full CPUID value (e.g. `0x000906ed`) |
| `codename` | string | Intel CPU codename (e.g. `Coffee Lake`); absent on AMD and ARM | | `codename` | string | Intel CPU codename (e.g. `Coffee Lake`); absent on AMD/Hygon |
| `smt` | `true` / `false` | Whether SMT (HyperThreading) is currently enabled |
| `microcode` | hex string | Installed microcode version (e.g. `0xf4`) |
| `microcode_latest` | hex string | Latest known-good microcode version from the firmware database |
| `microcode_up_to_date` | `true` / `false` | Whether `microcode == microcode_latest` |
| `microcode_blacklisted` | `true` / `false` | Whether the installed microcode is known to cause problems and should be rolled back |
**Example:** ARM-only labels (emitted when `arch="arm"`):
| Label | Values | Meaning |
|---|---|---|
| `part_list` | string | Space-separated list of ARM part numbers across cores (e.g. `0xd0b 0xd05` on big.LITTLE) |
| `arch_list` | string | Space-separated list of ARM architecture levels across cores (e.g. `8 8`) |
**x86 example:**
``` ```
smc_cpu_info{vendor="Intel",model="Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz",family="6",model_id="158",stepping="13",cpuid="0x000906ed",codename="Coffee Lake",smt="true",microcode="0xf4",microcode_latest="0xf4",microcode_up_to_date="true",microcode_blacklisted="false"} 1 smc_cpu_info{vendor="GenuineIntel",model="Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz",arch="x86",family="6",model_id="158",stepping="13",cpuid="0x000906ed",codename="Coffee Lake",smt="true",microcode="0xf4",microcode_latest="0xf4",microcode_up_to_date="true",microcode_blacklisted="false"} 1
```
**ARM example:**
```
smc_cpu_info{vendor="ARM",model="ARM v8 model 0xd0b",arch="arm",part_list="0xd0b 0xd05",arch_list="8 8",smt="false"} 1
``` ```
**Microcode labels:** **Microcode labels:**
@@ -352,9 +376,15 @@ queries. CVE checks that rely on hardware capability detection (`cap_*` flags,
MSR reads) will report `unknown` status. `mode="no-hw"` in `smc_build_info` MSR reads) will report `unknown` status. `mode="no-hw"` in `smc_build_info`
signals this. signals this.
**Cross-arch inspection (`--arch-prefix`)**
When a cross-arch toolchain prefix is passed, the script suppresses the host
CPU metadata so it does not get mixed with data from a different-arch target
kernel: `smc_cpu_info` is not emitted, the same as under `--no-hw`.
**Hardware-only mode (`--hw-only`)** **Hardware-only mode (`--hw-only`)**
Only hardware detection is performed; CVE checks are skipped. `smc_cpu_info` Only hardware detection is performed; CVE checks are skipped. `smc_cpu_info`
is emitted but no `smc_vuln` metrics appear. `mode="hw-only"` in is emitted but no `smc_vulnerability_status` metrics appear (and
`smc_vulnerable_count` / `smc_unknown_count` are `0`). `mode="hw-only"` in
`smc_build_info` signals this. `smc_build_info` signals this.
**`--sysfs-only`** **`--sysfs-only`**

View File

@@ -13,7 +13,7 @@
# #
# Stephane Lesimple # Stephane Lesimple
# #
VERSION='26.33.0420454' VERSION='26.33.0420455'
# --- Common paths and basedirs --- # --- Common paths and basedirs ---
readonly VULN_SYSFS_BASE="/sys/devices/system/cpu/vulnerabilities" readonly VULN_SYSFS_BASE="/sys/devices/system/cpu/vulnerabilities"
@@ -2402,15 +2402,17 @@ _prom_escape() {
printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | tr '\n' ' ' printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | tr '\n' ' '
} }
# Convert a shell capability value to a JSON token # Convert a shell capability value to a JSON boolean token
# Args: $1=value (1=true, 0=false, -1/empty=null, other string=quoted string) # Args: $1=value (1=true, 0=false, -1/empty=null, any other non-empty string=true)
# Prints: JSON token # Prints: JSON token (true/false/null)
# Note: capability variables can be set to arbitrary strings internally to carry
# detection-path context (e.g. cap_ssbd='Intel SSBD'); for the JSON output those
# are normalized to true so consumers see a clean boolean | null type.
_json_cap() { _json_cap() {
case "${1:-}" in case "${1:-}" in
1) printf 'true' ;;
0) printf 'false' ;; 0) printf 'false' ;;
-1 | '') printf 'null' ;; -1 | '') printf 'null' ;;
*) printf '"%s"' "$(_json_escape "$1")" ;; *) printf 'true' ;;
esac esac
} }
@@ -2513,7 +2515,7 @@ _build_json_system() {
# Sets: g_json_cpu # Sets: g_json_cpu
# shellcheck disable=SC2034 # shellcheck disable=SC2034
_build_json_cpu() { _build_json_cpu() {
local cpuid_hex codename caps arch_sub arch_type local cpuid_hex codename caps arch_sub arch_type sbpb_norm
if [ -n "${cpu_cpuid:-}" ]; then if [ -n "${cpu_cpuid:-}" ]; then
cpuid_hex=$(printf '0x%08x' "$cpu_cpuid") cpuid_hex=$(printf '0x%08x' "$cpu_cpuid")
else else
@@ -2524,6 +2526,15 @@ _build_json_cpu() {
codename=$(get_intel_codename 2>/dev/null || true) codename=$(get_intel_codename 2>/dev/null || true)
fi fi
# cap_sbpb uses non-standard encoding (1=YES, 2=NO, 3=UNKNOWN) because the
# CVE-2023-20569 check distinguishes the unknown case. Normalize for JSON.
case "${cap_sbpb:-}" in
1) sbpb_norm=1 ;;
2) sbpb_norm=0 ;;
3) sbpb_norm=-1 ;;
*) sbpb_norm='' ;;
esac
# Determine architecture type and build the arch-specific sub-object # Determine architecture type and build the arch-specific sub-object
case "${cpu_vendor:-}" in case "${cpu_vendor:-}" in
GenuineIntel | AuthenticAMD | HygonGenuine) GenuineIntel | AuthenticAMD | HygonGenuine)
@@ -2577,7 +2588,7 @@ _build_json_cpu() {
"$(_json_cap "${cap_tsa_l1_no:-}")" \ "$(_json_cap "${cap_tsa_l1_no:-}")" \
"$(_json_cap "${cap_verw_clear:-}")" \ "$(_json_cap "${cap_verw_clear:-}")" \
"$(_json_cap "${cap_autoibrs:-}")" \ "$(_json_cap "${cap_autoibrs:-}")" \
"$(_json_cap "${cap_sbpb:-}")" \ "$(_json_cap "$sbpb_norm")" \
"$(_json_cap "${cap_avx2:-}")" \ "$(_json_cap "${cap_avx2:-}")" \
"$(_json_cap "${cap_avx512:-}")") "$(_json_cap "${cap_avx512:-}")")
arch_sub=$(printf '{"family":%s,"model":%s,"stepping":%s,"cpuid":%s,"platform_id":%s,"hybrid":%s,"codename":%s,"capabilities":%s}' \ arch_sub=$(printf '{"family":%s,"model":%s,"stepping":%s,"cpuid":%s,"platform_id":%s,"hybrid":%s,"codename":%s,"capabilities":%s}' \