built from commit 2b1389e5c667a3c10c8e47fca7cb14d81695165c dated 2026-04-08 21:57:03 +0200 by Stéphane Lesimple (speed47_github@speed47.net)
17 KiB
JSON Output Format
--batch json emits a single, self-contained JSON object that describes the
scan environment and the result of every CVE check. You can feed it to your
monitoring system, to a SIEM, to a time-series database, you name it.
sudo ./spectre-meltdown-checker.sh --batch json | jq .
Top-level schema
{
"meta": { ... }, // Run metadata and flags
"system": { ... }, // Kernel and host context
"cpu": { ... }, // CPU hardware identification
"cpu_microcode": { ... }, // Microcode version and status
"vulnerabilities": [ ... ] // One object per checked CVE
}
format_version in meta is an integer that will be incremented on
backward-incompatible schema changes. The current value is 1.
Section reference
meta
Run metadata. Always present.
| Field | Type | Values | Meaning |
|---|---|---|---|
script_version |
string | e.g. "25.30.0250400123" |
Script version |
format_version |
integer | 1 |
JSON schema version; incremented on breaking changes |
timestamp |
string | ISO 8601 UTC, e.g. "2025-04-07T12:00:00Z" |
When the scan started |
os |
string | e.g. "Linux", "FreeBSD" |
Output of uname -s |
mode |
string | "live" / "no-runtime" / "no-hw" / "hw-only" |
Operating mode (see modes) |
run_as_root |
boolean | Whether the script ran as root. Non-root scans skip MSR reads and may miss mitigations | |
reduced_accuracy |
boolean | Kernel image, config, or System.map was missing; some checks fall back to weaker heuristics | |
paranoid |
boolean | --paranoid mode: stricter criteria (e.g. requires SMT disabled, IBPB always-on) |
|
sysfs_only |
boolean | --sysfs-only: only the kernel's own sysfs report was used, not independent detection |
|
no_hw |
boolean | --no-hw: hardware checks (MSR, CPUID) were skipped |
|
extra |
boolean | --extra: additional experimental checks were enabled |
|
mocked |
boolean | One or more CPU values were overridden for testing. Results do not reflect the real system |
Example:
"meta": {
"script_version": "25.30.025040123",
"format_version": 1,
"timestamp": "2026-04-06T12:22:14Z",
"os": "Linux",
"mode": "live",
"run_as_root": true,
"reduced_accuracy": false,
"paranoid": false,
"sysfs_only": false,
"no_hw": false,
"extra": false,
"mocked": false
}
Important flags for fleet operators:
run_as_root: falsemeans the scan was incomplete. Treat results as lower confidence. Alert separately: results may be missing or wrong.sysfs_only: truemeans the script trusted the kernel's self-report without independent verification. Some older kernels misreport their mitigation status. Do not use--sysfs-onlyfor production fleet monitoring.paranoid: trueraises the bar: only comparevulnerablecounts across hosts with the sameparanoidvalue.mocked: truemust never appear on a production host. If it does, every downstream result is fabricated.
system
Kernel and host environment. Always present.
| Field | Type | Values | Meaning |
|---|---|---|---|
kernel_release |
string | null | e.g. "6.1.0-21-amd64" |
Output of uname -r (null in no-runtime, no-hw, and hw-only modes) |
kernel_version |
string | null | e.g. "#1 SMP Debian …" |
Output of uname -v (null in no-runtime, no-hw, and hw-only modes) |
kernel_arch |
string | null | e.g. "x86_64" |
Output of uname -m (null in no-runtime, no-hw, and hw-only modes) |
kernel_image |
string | null | e.g. "/boot/vmlinuz-6.1.0-21-amd64" |
Path passed via --kernel, or null if not specified |
kernel_config |
string | null | Path passed via --config, or null |
|
kernel_version_string |
string | null | Kernel version banner extracted from the image | |
kernel_cmdline |
string | null | Kernel command line from /proc/cmdline (live mode) or the image |
|
cpu_count |
integer | null | Number of logical CPUs detected | |
smt_enabled |
boolean | null | Whether SMT (HyperThreading) is currently active; null if undeterminable | |
hypervisor_host |
boolean | null | Whether this machine is detected as a VM host (running KVM, Xen, VMware, etc.) | |
hypervisor_host_reason |
string | null | Human-readable explanation of why hypervisor_host was set |
hypervisor_host materially changes the risk profile of several CVEs.
L1TF (CVE-2018-3646) and MDS (CVE-2018-12126/12130/12127) are significantly
more severe on hypervisor hosts because they can be exploited across VM
boundaries by a malicious guest. Prioritise remediation where
hypervisor_host: true.
cpu
CPU hardware identification. null when --no-hw is active.
| Field | Type | Values | Meaning |
|---|---|---|---|
vendor |
string | null | e.g. "Intel", "AuthenticAMD" |
CPU vendor string |
friendly_name |
string | null | e.g. "Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz" |
Human-readable CPU model |
family |
integer | null | CPU family number | |
model |
integer | null | CPU model number | |
stepping |
integer | null | CPU stepping number | |
cpuid |
string | null | hex, e.g. "0x000906ed" |
Full CPUID leaf 1 EAX value |
platform_id |
integer | null | Intel platform ID (from MSR 0x17); null on AMD and ARM | |
hybrid |
boolean | null | Whether this is a hybrid CPU (P-cores + E-cores, e.g. Alder Lake) | |
codename |
string | null | e.g. "Coffee Lake" |
Intel CPU codename; null on AMD and ARM |
arm_part_list |
string | null | Space-separated list of ARM part numbers detected across cores | |
arm_arch_list |
string | null | Space-separated list of ARM architecture levels detected across cores | |
capabilities |
object | CPU feature flags detected via CPUID and MSR reads (see below) |
cpu.capabilities
Each 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
Intel-specific features).
| Capability | Meaning |
|---|---|
spec_ctrl |
SPEC_CTRL MSR (Intel: ibrs + ibpb via WRMSR; required for many mitigations) |
ibrs |
Indirect Branch Restricted Speculation |
ibpb |
Indirect Branch Prediction Barrier |
ibpb_ret |
IBPB on return (enhanced form) |
stibp |
Single Thread Indirect Branch Predictors |
ssbd |
Speculative Store Bypass Disable |
l1d_flush |
L1D cache flush instruction |
md_clear |
VERW clears CPU buffers (MDS mitigation) |
arch_capabilities |
IA32_ARCH_CAPABILITIES MSR is present |
rdcl_no |
Not susceptible to RDCL (Meltdown-like attacks) |
ibrs_all |
Enhanced IBRS always-on mode supported |
rsba |
RSB may use return predictions from outside the RSB |
l1dflush_no |
Not susceptible to L1D flush side-channel |
ssb_no |
Not susceptible to Speculative Store Bypass |
mds_no |
Not susceptible to MDS |
taa_no |
Not susceptible to TSX Asynchronous Abort |
pschange_msc_no |
Page-size-change MSC not susceptible |
tsx_ctrl_msr |
TSX_CTRL MSR is present |
tsx_ctrl_rtm_disable |
RTM disabled via TSX_CTRL |
tsx_ctrl_cpuid_clear |
CPUID HLE/RTM bits cleared via TSX_CTRL |
gds_ctrl |
GDS_CTRL MSR present (GDS mitigation control) |
gds_no |
Not susceptible to Gather Data Sampling |
gds_mitg_dis |
GDS mitigation disabled |
gds_mitg_lock |
GDS mitigation locked |
rfds_no |
Not susceptible to Register File Data Sampling |
rfds_clear |
VERW clears register file stale data |
its_no |
Not susceptible to Indirect Target Selection |
sbdr_ssdp_no |
Not susceptible to SBDR/SSDP |
fbsdp_no |
Not susceptible to FBSDP |
psdp_no |
Not susceptible to PSDP |
fb_clear |
Fill buffer cleared on idle/C6 |
rtm |
Restricted Transactional Memory (TSX RTM) present |
tsx_force_abort |
TSX_FORCE_ABORT MSR present |
tsx_force_abort_rtm_disable |
RTM disabled via TSX_FORCE_ABORT |
tsx_force_abort_cpuid_clear |
CPUID RTM cleared via TSX_FORCE_ABORT |
sgx |
Software Guard Extensions present |
srbds |
SRBDS affected |
srbds_on |
SRBDS mitigation active |
amd_ssb_no |
AMD: not susceptible to Speculative Store Bypass |
hygon_ssb_no |
Hygon: not susceptible to Speculative Store Bypass |
ipred |
Indirect Predictor Barrier support |
rrsba |
Restricted RSB Alternate (Intel Retbleed mitigation) |
bhi |
Branch History Injection mitigation support |
tsa_sq_no |
Not susceptible to TSA-SQ |
tsa_l1_no |
Not susceptible to TSA-L1 |
verw_clear |
VERW clears CPU buffers |
autoibrs |
AMD AutoIBRS (equivalent to enhanced IBRS on Intel) |
sbpb |
Selective Branch Predictor Barrier (AMD Inception mitigation) |
avx2 |
AVX2 supported (relevant to Downfall / GDS) |
avx512 |
AVX-512 supported (relevant to Downfall / GDS) |
cpu_microcode
Microcode version and status. null under the same conditions as cpu.
| Field | Type | Values | Meaning |
|---|---|---|---|
installed_version |
string | null | hex, e.g. "0xf4" |
Currently running microcode revision |
latest_version |
string | null | hex | Latest known-good version in the firmware database; null if CPU is not in the database |
microcode_up_to_date |
boolean | null | Whether installed_version == latest_version; null if either is unavailable |
|
is_blacklisted |
boolean | Whether the installed microcode is known to cause instability and must be rolled back | |
message |
string | null | Human-readable note from the firmware database (e.g. changelog excerpt) | |
db_source |
string | null | Which database was used (e.g. "Intel-SA", "MCExtractor") |
|
db_info |
string | null | Database revision or date |
is_blacklisted: true means the installed microcode is known to cause
system instability or incorrect behaviour. Treat this as a P1 incident: roll
back to the previous microcode immediately.
microcode_up_to_date: false means a newer microcode is available. This
does not necessarily mean the system is vulnerable (the current microcode may
still include all required mitigations), but warrants investigation.
vulnerabilities
Array of CVE check results. One object per checked CVE, in check order.
Empty array ([]) if no CVEs were checked (unusual; would require --cve
with an unknown CVE ID).
| Field | Type | Values | Meaning |
|---|---|---|---|
cve |
string | e.g. "CVE-2017-5753" |
CVE identifier |
name |
string | e.g. "SPECTRE VARIANT 1" |
Short key name used in batch formats |
aliases |
string | null | e.g. "Spectre Variant 1, bounds check bypass" |
Full name including all known aliases |
cpu_affected |
boolean | Whether this CPU's hardware design is affected by this CVE | |
status |
string | "OK" / "VULN" / "UNK" |
Check outcome (see below) |
vulnerable |
boolean | null | false / true / null |
false=OK, true=VULN, null=UNK |
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_message |
string | null | Raw text from the sysfs file (e.g. "Mitigation: PTI"); null if sysfs was not consulted |
Status values
status |
vulnerable |
Meaning |
|---|---|---|
"OK" |
false |
CPU is unaffected by design, or all required mitigations are in place |
"VULN" |
true |
CPU is affected and mitigations are missing or insufficient |
"UNK" |
null |
The script could not determine the status (missing kernel info, insufficient privileges, or no detection logic for this platform) |
cpu_affected explained
cpu_affected: false with status: "OK" means the CPU hardware is
architecturally immune — no patch was ever needed.
cpu_affected: true with status: "OK" means the hardware has the weakness
but all required mitigations (kernel, microcode, or both) are in place.
This distinction matters for fleet auditing: filter on cpu_affected: true to
see only systems where mitigation effort was actually required and confirmed.
sysfs_status vs status
sysfs_status is the raw kernel self-report. status is the script's
independent assessment, which may differ:
- The script may upgrade a sysfs
"VULN"to"OK"when it detects a silent backport that the kernel doesn't know about. - The script may downgrade a sysfs
"OK"to"VULN"when it detects an incomplete mitigation the kernel doesn't flag (e.g. L1TF on a hypervisor host with SMT still enabled, or TSA inusermode on a VMM host). sysfs_statusisnullwhen the kernel has no sysfs entry for this CVE (older kernels, or CVEs not yet tracked by the kernel).
Always use status / vulnerable for alerting. Use sysfs_status for
diagnostics and audit trails.
Example:
{
"cve": "CVE-2017-5715",
"name": "SPECTRE VARIANT 2",
"aliases": "Spectre Variant 2, branch target injection",
"cpu_affected": true,
"status": "OK",
"vulnerable": false,
"info": "Full generic retpoline is mitigating the vulnerability",
"sysfs_status": "OK",
"sysfs_message": "Mitigation: Retpolines; IBPB: conditional; IBRS_FW; STIBP: conditional; RSB filling; PBRSB-eIBRS: Not affected; BHI: Not affected"
}
Exit codes
The script exits with:
| Code | Meaning |
|---|---|
0 |
All checked CVEs are OK |
2 |
At least one CVE is VULN |
3 |
No CVEs are VULN, but at least one is UNK |
These exit codes are the same in all batch modes and in interactive mode. Use them in combination with the JSON body for reliable alerting.
Caveats and edge cases
No-runtime mode (--no-runtime)
system.kernel_release, kernel_version, and kernel_arch are null (those
come from uname, which reports the running kernel, not the inspected one).
meta.mode: "no-runtime" signals this. system.kernel_image and
system.kernel_version_string carry the inspected image path and banner
instead.
No-hardware mode (--no-hw)
cpu and cpu_microcode are null. CVE checks that rely on hardware
capability detection (cap_* flags, MSR reads) will report status: "UNK".
cpu_affected will be false for all CVEs (cannot determine affection without
hardware info). meta.mode: "no-hw" signals this.
Hardware-only mode (--hw-only)
Only CPU information and per-CVE affectedness are reported. No kernel
inspection is performed, so vulnerability mitigations are not checked.
meta.mode: "hw-only" signals this.
--sysfs-only
The script trusts the kernel's sysfs report without running independent
detection. meta.sysfs_only: true flags this. Some older kernels misreport
their status. Do not use for production fleet monitoring.
--paranoid
Enables defense-in-depth checks beyond the security community consensus.
A status: "OK" under paranoid: true means a higher bar was met. Do not
compare results across hosts with different paranoid values.
reduced_accuracy
Set when the kernel image, config file, or System.map could not be read.
Some checks fall back to weaker heuristics and may report "UNK" for CVEs
that are actually mitigated.
Non-x86 architectures (ARM, ARM64)
cpu.codename and cpu.platform_id are always null. cpu.arm_part_list
and cpu.arm_arch_list carry the relevant identifiers instead.
Most cpu.capabilities fields are null (those flags are Intel/AMD-specific).
mocked: true
Must never appear on a production host. If it does, the results are
fabricated and every downstream alert is unreliable.
Schema stability
meta.format_version is incremented on backward-incompatible changes (field
removal or type change). Additive changes (new fields) do not increment the
version; consumers must tolerate unknown fields.
Recommended practice: check format_version == 1 at parse time and reject
or alert on any other value until you have tested compatibility with the new
version.
Migration from json-terse
The legacy --batch json-terse format emits a flat array of objects:
[
{"NAME": "SPECTRE VARIANT 1", "CVE": "CVE-2017-5753", "VULNERABLE": false, "INFOS": "..."},
...
]
It carries no system, CPU, or microcode context. It has no sysfs data. It uses uppercase field names.
To migrate:
- Replace
--batch json-tersewith--batch json. - The equivalent of the old
VULNERABLEfield isvulnerabilities[].vulnerable. - The equivalent of the old
INFOSfield isvulnerabilities[].info. - The equivalent of the old
NAMEfield isvulnerabilities[].name. - The old format is still available as
--batch json-tersefor transition periods.