mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-04-23 09:03:19 +02:00
Compare commits
2 Commits
6732eb141b
...
e2d110a3b5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2d110a3b5 | ||
|
|
1bb33d5cf2 |
36
.github/workflows/autoupdate.yml
vendored
36
.github/workflows/autoupdate.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: autoupdate
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: '42 9 * * *'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
autoupdate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Install prerequisites
|
|
||||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends iucode-tool sqlite3 unzip
|
|
||||||
- name: Update microcode versions
|
|
||||||
run: ./spectre-meltdown-checker.sh --update-builtin-fwdb
|
|
||||||
- name: Check git diff
|
|
||||||
id: diff
|
|
||||||
run: |
|
|
||||||
echo change="$(git diff spectre-meltdown-checker.sh | awk '/MCEDB/ { if(V) { print V" to "$4; exit } else { V=$4 } }')" >> "$GITHUB_OUTPUT"
|
|
||||||
echo nbdiff="$(git diff spectre-meltdown-checker.sh | grep -cE -- '^\+# [AI],')" >> "$GITHUB_OUTPUT"
|
|
||||||
git diff
|
|
||||||
cat "$GITHUB_OUTPUT"
|
|
||||||
- name: Create Pull Request if needed
|
|
||||||
if: steps.diff.outputs.nbdiff != '0'
|
|
||||||
uses: peter-evans/create-pull-request@v7
|
|
||||||
with:
|
|
||||||
branch: autoupdate-fwdb
|
|
||||||
commit-message: "update: fwdb from ${{ steps.diff.outputs.change }}, ${{ steps.diff.outputs.nbdiff }} microcode changes"
|
|
||||||
title: "[Auto] Update fwdb from ${{ steps.diff.outputs.change }}"
|
|
||||||
body: |
|
|
||||||
Automated PR to update fwdb from ${{ steps.diff.outputs.change }}
|
|
||||||
Detected ${{ steps.diff.outputs.nbdiff }} microcode changes
|
|
||||||
199
.github/workflows/daily_vuln_scan_prompt.md
vendored
199
.github/workflows/daily_vuln_scan_prompt.md
vendored
@@ -1,199 +0,0 @@
|
|||||||
# Daily transient-execution vulnerability scan
|
|
||||||
|
|
||||||
You are a scheduled agent running inside a GitHub Actions job. Your job
|
|
||||||
is to audit public news/advisory sources for **transient-execution and
|
|
||||||
CPU side-channel vulnerabilities** that may need to be added to
|
|
||||||
**spectre-meltdown-checker** (this repository).
|
|
||||||
|
|
||||||
## What counts as "relevant"
|
|
||||||
|
|
||||||
spectre-meltdown-checker detects, reports, and suggests mitigations for
|
|
||||||
CPU vulnerabilities such as: Spectre v1/v2/v4, Meltdown, Foreshadow/L1TF,
|
|
||||||
MDS (ZombieLoad/RIDL/Fallout), TAA, SRBDS, iTLB Multihit, Zenbleed,
|
|
||||||
Downfall (GDS), Retbleed, Inception, SRSO, BHI, RFDS, Reptar, FP-DSS,
|
|
||||||
and any similar microarchitectural side-channel or speculative-execution
|
|
||||||
issue on x86 (Intel/AMD) or ARM CPUs. It also surfaces related hardware
|
|
||||||
mitigation features (SMAP/SMEP/UMIP/IBPB/eIBRS/STIBP…) when they gate
|
|
||||||
the remediation for a tracked CVE.
|
|
||||||
|
|
||||||
It does **not** track generic software CVEs, GPU driver bugs, networking
|
|
||||||
stacks, filesystem bugs, userspace crypto issues, or unrelated kernel
|
|
||||||
subsystems.
|
|
||||||
|
|
||||||
## Inputs handed to you by the workflow
|
|
||||||
|
|
||||||
- Working directory: the repo root (`/github/workspace` in Actions, or
|
|
||||||
wherever `actions/checkout` placed it). You may `grep` the repo to
|
|
||||||
check whether a CVE or codename is already covered.
|
|
||||||
- `state/seen.json` — memory carried over from the previous run, with
|
|
||||||
shape:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"last_run": "2026-04-17T08:00:12Z",
|
|
||||||
"seen": {
|
|
||||||
"<stable-id-1>": { "bucket": "unrelated", "seen_at": "2026-04-17T08:00:12Z", "source": "phoronix" },
|
|
||||||
"<stable-id-2>": { "bucket": "tocheck", "seen_at": "2026-04-17T08:00:12Z", "source": "oss-sec", "cve": "CVE-2026-1234" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
On the very first run, or when the prior artifact has expired,
|
|
||||||
the file exists but `seen` is empty and `last_run` is `null`.
|
|
||||||
|
|
||||||
- Environment: `SCAN_DATE` (ISO-8601 timestamp of the run start, set by
|
|
||||||
the workflow). Treat this as "now" for all time-window decisions.
|
|
||||||
|
|
||||||
## Time window
|
|
||||||
|
|
||||||
This is a belt-and-suspenders design — use **both** mechanisms:
|
|
||||||
|
|
||||||
1. **Primary: stable-id dedup.** If an item's stable identifier (see
|
|
||||||
below) is already present in `state.seen`, skip it entirely — it
|
|
||||||
was classified on a previous day.
|
|
||||||
2. **Secondary: 25-hour window.** Among *new* items, prefer those whose
|
|
||||||
publication/update timestamp is within the last 25 h relative to
|
|
||||||
`SCAN_DATE`. This bounds work when the prior artifact expired
|
|
||||||
(90-day retention) or when `last_run` is stale (missed runs).
|
|
||||||
If `last_run` is older than 25 h, widen the window to
|
|
||||||
`now - last_run + 1h` so no items are lost across missed runs.
|
|
||||||
3. Items without a parseable timestamp: include them (fail-safe).
|
|
||||||
|
|
||||||
## Sources to poll
|
|
||||||
|
|
||||||
Fetch each URL with
|
|
||||||
`curl -sS -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" -L --max-time 20`.
|
|
||||||
On non-2xx or timeout, record the failure in the run summary and
|
|
||||||
continue — do not abort.
|
|
||||||
|
|
||||||
### RSS / Atom feeds (primary — parse feed timestamps)
|
|
||||||
|
|
||||||
| Short name | URL |
|
|
||||||
|-----------------|-----|
|
|
||||||
| phoronix | https://www.phoronix.com/rss.php |
|
|
||||||
| oss-sec | https://seclists.org/rss/oss-sec.rss |
|
|
||||||
| lwn | https://lwn.net/headlines/newrss |
|
|
||||||
| project-zero | https://googleprojectzero.blogspot.com/feeds/posts/default |
|
|
||||||
| vusec | https://www.vusec.net/feed/ |
|
|
||||||
| comsec-eth | https://comsec.ethz.ch/category/news/feed/ |
|
|
||||||
| msrc | https://msrc.microsoft.com/update-guide/rss |
|
|
||||||
| cisa | https://www.cisa.gov/cybersecurity-advisories/all.xml |
|
|
||||||
| cert-cc | https://www.kb.cert.org/vuls/atomfeed/ |
|
|
||||||
|
|
||||||
### HTML pages (no RSS — fetch, extract dated entries)
|
|
||||||
|
|
||||||
| Short name | URL |
|
|
||||||
|-----------------|-----|
|
|
||||||
| intel-psirt | https://www.intel.com/content/www/us/en/security-center/default.html |
|
|
||||||
| amd-psirt | https://www.amd.com/en/resources/product-security.html |
|
|
||||||
| arm-spec | https://developer.arm.com/Arm%20Security%20Center/Speculative%20Processor%20Vulnerability |
|
|
||||||
| transient-fail | https://transient.fail/ |
|
|
||||||
|
|
||||||
For HTML pages: look for advisory tables or listings with dates. Extract
|
|
||||||
the advisory title, permalink, and date. If a page has no dates at all,
|
|
||||||
compare its content against `state.seen` — any new advisory IDs not yet
|
|
||||||
classified count as "new this run".
|
|
||||||
|
|
||||||
## Stable identifier per source
|
|
||||||
|
|
||||||
Use the first available of these, in order, as the dedup key:
|
|
||||||
|
|
||||||
1. Vendor advisory ID (`INTEL-SA-01234`, `AMD-SB-7001`, `ARM-2024-0042`,
|
|
||||||
`VU#123456`, `CVE-YYYY-NNNNN`)
|
|
||||||
2. RSS `<guid>` / Atom `<id>`
|
|
||||||
3. Permalink URL (`<link>`)
|
|
||||||
|
|
||||||
Always also record the permalink URL in the output file so a human can
|
|
||||||
click through.
|
|
||||||
|
|
||||||
## Classification rules
|
|
||||||
|
|
||||||
For each **new** item (not in `state.seen`) that passes the time window,
|
|
||||||
pick exactly one bucket:
|
|
||||||
|
|
||||||
- **toimplement** — a clearly-identified new transient-execution / CPU
|
|
||||||
side-channel vulnerability in scope, **and not already covered by
|
|
||||||
this repo**. Verify the second half by grepping the repo for the CVE
|
|
||||||
ID *and* the codename before classifying; if either matches existing
|
|
||||||
code, demote to `tocheck`.
|
|
||||||
- **tocheck** — plausibly in-scope but ambiguous: mitigation-only
|
|
||||||
feature (LASS, IBT, APIC-virt, etc.); item seemingly already
|
|
||||||
implemented but worth confirming scope; unclear applicability
|
|
||||||
(e.g. embedded-only ARM SKU); CVE-ID pending; contradictory info
|
|
||||||
across sources. State clearly what would resolve the ambiguity.
|
|
||||||
- **unrelated** — everything else.
|
|
||||||
|
|
||||||
Tie-breakers: prefer `tocheck` over `unrelated` when uncertain. Prefer
|
|
||||||
`tocheck` over `toimplement` when the CVE ID is still "reserved" /
|
|
||||||
"pending" — false positives in `toimplement` waste human time more than
|
|
||||||
false positives in `tocheck`.
|
|
||||||
|
|
||||||
## Outputs
|
|
||||||
|
|
||||||
Compute `TODAY=$(date -u -d "$SCAN_DATE" +%F)`. Write these files under
|
|
||||||
the repo root, overwriting if they already exist (they shouldn't unless
|
|
||||||
the workflow re-ran the same day):
|
|
||||||
|
|
||||||
- `rss_${TODAY}_toimplement.md`
|
|
||||||
- `rss_${TODAY}_tocheck.md`
|
|
||||||
- `rss_${TODAY}_unrelated.md`
|
|
||||||
|
|
||||||
Each file uses level-2 headers per source short-name, then one bullet
|
|
||||||
per item: the stable ID (if any), the permalink URL, and 1–2 sentences.
|
|
||||||
Keep entries terse — a human skims these daily.
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## oss-sec
|
|
||||||
- **CVE-2026-1234** — https://www.openwall.com/lists/oss-security/2026/04/18/3
|
|
||||||
New Intel transient-execution bug "Foo" disclosed today; affects
|
|
||||||
Redwood Cove cores, microcode fix pending. Not yet covered by this
|
|
||||||
repo (grepped for CVE-2026-1234 and "Foo" — no matches).
|
|
||||||
|
|
||||||
## phoronix
|
|
||||||
- https://www.phoronix.com/news/Some-Article
|
|
||||||
Linux 7.2 drops a compiler-target flag; unrelated to CPU side channels.
|
|
||||||
```
|
|
||||||
|
|
||||||
If a bucket has no items, write the file with a single line
|
|
||||||
`(no new items in this window)` so it is obvious the job ran.
|
|
||||||
|
|
||||||
### Run summary
|
|
||||||
|
|
||||||
Append this block to the **tocheck** file (creating it if empty):
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## Run summary
|
|
||||||
- SCAN_DATE: <value>
|
|
||||||
- window cutoff: <computed cutoff>
|
|
||||||
- prior state size: <N> entries, last_run=<value>
|
|
||||||
- per-source new item counts: phoronix=<n>, oss-sec=<n>, lwn=<n>, ...
|
|
||||||
- fetch failures: <list, or "none">
|
|
||||||
- total classified this run: toimplement=<n>, tocheck=<n>, unrelated=<n>
|
|
||||||
```
|
|
||||||
|
|
||||||
### State update
|
|
||||||
|
|
||||||
Rewrite `state/seen.json` with:
|
|
||||||
|
|
||||||
- `last_run` = `SCAN_DATE`
|
|
||||||
- `seen` = union of (pruned prior `seen`) ∪ (all items classified this
|
|
||||||
run, keyed by stable ID, with `{bucket, seen_at=SCAN_DATE, source, cve?}`)
|
|
||||||
|
|
||||||
Pruning (keep state bounded): drop any entry whose `seen_at` is older
|
|
||||||
than 30 days before `SCAN_DATE`. The workflow step also does this as
|
|
||||||
a safety net, but do it here too so the in-memory view is consistent.
|
|
||||||
|
|
||||||
## Guardrails
|
|
||||||
|
|
||||||
- Do NOT modify any repo source code. Only write the three markdown
|
|
||||||
output files and `state/seen.json`.
|
|
||||||
- Do NOT create commits, branches, or PRs.
|
|
||||||
- Do NOT call any tool that posts externally (Slack, GitHub comments,
|
|
||||||
issues, email, etc.).
|
|
||||||
- Do NOT follow links off-site for deeper investigation unless strictly
|
|
||||||
needed to resolve a `tocheck` ambiguity — budget of at most 5 such
|
|
||||||
follow-ups per run.
|
|
||||||
- If a source returns unexpectedly large content, truncate to the first
|
|
||||||
~200 items before parsing.
|
|
||||||
- If total runtime exceeds 15 minutes, finish whatever you can,
|
|
||||||
write partial outputs, and note it in the run summary.
|
|
||||||
33
.github/workflows/stale.yml
vendored
33
.github/workflows/stale.yml
vendored
@@ -1,33 +0,0 @@
|
|||||||
name: 'Manage stale issues and PRs'
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '37 7 * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
action:
|
|
||||||
description: "dry-run"
|
|
||||||
required: true
|
|
||||||
default: "dryrun"
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- dryrun
|
|
||||||
- apply
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stale:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/stale@v10
|
|
||||||
with:
|
|
||||||
any-of-labels: 'needs-more-info,answered'
|
|
||||||
labels-to-remove-when-unstale: 'needs-more-info,answered'
|
|
||||||
days-before-stale: 30
|
|
||||||
days-before-close: 7
|
|
||||||
stale-issue-label: stale
|
|
||||||
remove-stale-when-updated: true
|
|
||||||
debug-only: ${{ case(inputs.action == 'dryrun', true, false) }}
|
|
||||||
129
.github/workflows/vuln-scan.yml
vendored
129
.github/workflows/vuln-scan.yml
vendored
@@ -1,129 +0,0 @@
|
|||||||
name: Online search for vulns
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '42 8 * * *'
|
|
||||||
workflow_dispatch: {} # allow manual trigger
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read # needed to list/download previous run artifacts
|
|
||||||
id-token: write # needed to mint OIDC token
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: vuln-scan
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
scan:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 20
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository (for grep-based dedup against existing checks)
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
# ---- Load previous state ---------------------------------------------
|
|
||||||
# Find the most recent successful run of THIS workflow (other than the
|
|
||||||
# current one) and pull its `vuln-scan-state` artifact. On the very
|
|
||||||
# first run there will be none — that's fine, we start empty.
|
|
||||||
- name: Find previous successful run id
|
|
||||||
id: prev
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
run_id=$(gh run list \
|
|
||||||
--workflow="${{ github.workflow }}" \
|
|
||||||
--status=success \
|
|
||||||
--limit 1 \
|
|
||||||
--json databaseId \
|
|
||||||
--jq '.[0].databaseId // empty')
|
|
||||||
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
|
|
||||||
if [ -n "$run_id" ]; then
|
|
||||||
echo "Found previous successful run: $run_id"
|
|
||||||
else
|
|
||||||
echo "No previous successful run — starting from empty state."
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Download previous state artifact
|
|
||||||
if: steps.prev.outputs.run_id != ''
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
continue-on-error: true # tolerate retention expiry
|
|
||||||
with:
|
|
||||||
name: vuln-scan-state
|
|
||||||
path: state/
|
|
||||||
run-id: ${{ steps.prev.outputs.run_id }}
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Ensure state file exists
|
|
||||||
run: |
|
|
||||||
mkdir -p state
|
|
||||||
if [ ! -f state/seen.json ]; then
|
|
||||||
echo '{"last_run": null, "seen": {}}' > state/seen.json
|
|
||||||
echo "Initialized empty state."
|
|
||||||
fi
|
|
||||||
echo "State size: $(wc -c < state/seen.json) bytes"
|
|
||||||
|
|
||||||
# ---- Run the scan ----------------------------------------------------
|
|
||||||
# Runs Claude Code against daily_vuln_scan_prompt.md.
|
|
||||||
# That prompt file fully specifies: sources to poll, how to read
|
|
||||||
# state/seen.json, the 25-hour window, the output files to write,
|
|
||||||
# and how to rewrite state/seen.json at the end of the run.
|
|
||||||
- name: Research for online mentions of new vulns
|
|
||||||
id: scan
|
|
||||||
uses: anthropics/claude-code-action@v1
|
|
||||||
env:
|
|
||||||
SCAN_DATE: ${{ github.run_started_at }}
|
|
||||||
with:
|
|
||||||
claude_args: |
|
|
||||||
--model claude-opus-4-7 --allowedTools "Read,Write,Edit,Bash,Grep,Glob,WebFetch"
|
|
||||||
prompt: |
|
|
||||||
Read the full task instructions from .github/workflows/daily_vuln_scan_prompt.md and execute them end-to-end. That file fully specifies: sources to poll, how to read and update state/seen.json, the 25-hour window, which rss_YYYY-MM-DD_*.md files to write, and the run guardrails. Use $SCAN_DATE (env var) as "now" for time-window decisions.
|
|
||||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
||||||
|
|
||||||
- name: Upload Claude execution log
|
|
||||||
if: ${{ always() && steps.scan.outputs.execution_file != '' }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: claude-execution-log-${{ github.run_id }}
|
|
||||||
path: ${{ steps.scan.outputs.execution_file }}
|
|
||||||
retention-days: 30
|
|
||||||
if-no-files-found: warn
|
|
||||||
|
|
||||||
# ---- Persist outputs -------------------------------------------------
|
|
||||||
- name: Prune state (keep only entries from the last 30 days)
|
|
||||||
run: |
|
|
||||||
python3 - <<'PY'
|
|
||||||
import json, datetime, pathlib
|
|
||||||
p = pathlib.Path("state/seen.json")
|
|
||||||
data = json.loads(p.read_text())
|
|
||||||
cutoff = (datetime.datetime.utcnow() - datetime.timedelta(days=30)).isoformat()
|
|
||||||
before = len(data.get("seen", {}))
|
|
||||||
data["seen"] = {
|
|
||||||
k: v for k, v in data.get("seen", {}).items()
|
|
||||||
if v.get("seen_at", "9999") >= cutoff
|
|
||||||
}
|
|
||||||
after = len(data["seen"])
|
|
||||||
p.write_text(json.dumps(data, indent=2, sort_keys=True))
|
|
||||||
print(f"Pruned state: {before} -> {after} entries")
|
|
||||||
PY
|
|
||||||
|
|
||||||
- name: Upload new state artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: vuln-scan-state
|
|
||||||
path: state/seen.json
|
|
||||||
retention-days: 90
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Upload daily report
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: vuln-scan-report-${{ github.run_id }}
|
|
||||||
path: rss_*.md
|
|
||||||
retention-days: 90
|
|
||||||
if-no-files-found: warn
|
|
||||||
8
dist/doc/batch_json.md
vendored
8
dist/doc/batch_json.md
vendored
@@ -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
|
||||||
|
|||||||
14
dist/doc/batch_json.schema.json
vendored
14
dist/doc/batch_json.schema.json
vendored
@@ -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)" }
|
||||||
}
|
}
|
||||||
|
|||||||
9
dist/doc/batch_nrpe.md
vendored
9
dist/doc/batch_nrpe.md
vendored
@@ -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
|
||||||
|
|||||||
62
dist/doc/batch_prometheus.md
vendored
62
dist/doc/batch_prometheus.md
vendored
@@ -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`**
|
||||||
|
|||||||
@@ -15,15 +15,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +128,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
|
||||||
@@ -137,6 +139,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)
|
||||||
@@ -190,7 +201,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}' \
|
||||||
|
|||||||
Reference in New Issue
Block a user