mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-04-09 10:13:18 +02:00
chore: create doc/ in -build branch
built from commit 2b1389e5c667a3c10c8e47fca7cb14d81695165c dated 2026-04-08 21:57:03 +0200 by Stéphane Lesimple (speed47_github@speed47.net)
This commit is contained in:
373
doc/batch_json.md
Normal file
373
doc/batch_json.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# 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.
|
||||
|
||||
```sh
|
||||
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](README.md#operating-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:**
|
||||
```json
|
||||
"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: false` means the scan was incomplete. Treat results as lower
|
||||
confidence. Alert separately: results may be missing or wrong.
|
||||
- `sysfs_only: true` means the script trusted the kernel's self-report without
|
||||
independent verification. Some older kernels misreport their mitigation
|
||||
status. Do not use `--sysfs-only` for production fleet monitoring.
|
||||
- `paranoid: true` raises the bar: only compare `vulnerable` counts across
|
||||
hosts with the same `paranoid` value.
|
||||
- `mocked: true` must 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 in `user` mode on a VMM host).
|
||||
- `sysfs_status` is `null` when 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:**
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```json
|
||||
[
|
||||
{"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:
|
||||
|
||||
1. Replace `--batch json-terse` with `--batch json`.
|
||||
2. The equivalent of the old `VULNERABLE` field is `vulnerabilities[].vulnerable`.
|
||||
3. The equivalent of the old `INFOS` field is `vulnerabilities[].info`.
|
||||
4. The equivalent of the old `NAME` field is `vulnerabilities[].name`.
|
||||
5. The old format is still available as `--batch json-terse` for transition
|
||||
periods.
|
||||
Reference in New Issue
Block a user