mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-04-22 08:33:20 +02:00
Compare commits
2 Commits
50845adbfb
...
cf156a2ee5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf156a2ee5 | ||
|
|
4eb0d04808 |
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
|
||||
@@ -102,7 +102,9 @@ boundaries by a malicious guest. Prioritise remediation where
|
||||
|
||||
### `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,
|
||||
`"arm"` for ARM/Cavium/Phytium. Arch-specific fields live under a matching
|
||||
@@ -140,7 +142,7 @@ fields from the other architecture.
|
||||
|
||||
#### `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
|
||||
Intel-specific features).
|
||||
|
||||
@@ -238,7 +240,7 @@ with an unknown CVE ID).
|
||||
| `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_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 |
|
||||
|
||||
#### Status values
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
},
|
||||
|
||||
"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": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
@@ -180,16 +180,16 @@
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"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",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"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" },
|
||||
"ibpb": { "type": ["boolean", "null"], "description": "Indirect Branch Prediction Barrier" },
|
||||
"ibrs": { "type": ["boolean", "null"], "description": "IBRS supported (via SPEC_CTRL, IBRS_SUPPORT, or cpuinfo fallback)" },
|
||||
"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)" },
|
||||
"stibp": { "type": ["boolean", "null"], "description": "Single Thread Indirect Branch Predictors" },
|
||||
"ssbd": { "type": ["boolean", "null"], "description": "Speculative Store Bypass Disable" },
|
||||
"stibp": { "type": ["boolean", "null"], "description": "STIBP supported (Intel/AMD/HYGON or cpuinfo fallback)" },
|
||||
"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" },
|
||||
"md_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers (MDS mitigation)" },
|
||||
"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" },
|
||||
"verw_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers" },
|
||||
"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)" },
|
||||
"avx512": { "type": ["boolean", "null"], "description": "AVX-512 supported (relevant to Downfall / GDS)" }
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ STATUS: summary | perfdata
|
||||
| VULN + UNK | `N/T CVE(s) vulnerable: CVE-A CVE-B ..., M 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 + UNK | `N/T CVE(s) appear vulnerable (unconfirmed, not root): CVE-A ..., M inconclusive` |
|
||||
|
||||
### Lines 2+ (long output)
|
||||
|
||||
@@ -59,15 +60,19 @@ Never parsed by the monitoring core; safe to add or reorder.
|
||||
|
||||
#### 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: 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 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
|
||||
|
||||
One line per non-OK CVE. VULN entries (`[CRITICAL]`) appear before UNK
|
||||
|
||||
@@ -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`.
|
||||
|
||||
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 |
|
||||
|---|---|---|
|
||||
| `kernel_release` | string | Output of `uname -r` (live mode only) |
|
||||
| `kernel_arch` | string | Output of `uname -m` (live mode only) |
|
||||
| `hypervisor_host` | `true` / `false` | Whether this machine is detected as a hypervisor host (running KVM, Xen, VMware, etc.) |
|
||||
| `kernel_release` | string | Output of `uname -r`; emitted only in live mode |
|
||||
| `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.); absent when VMM detection did not run (e.g. `--no-hw`) |
|
||||
|
||||
**Example:**
|
||||
```
|
||||
@@ -114,26 +117,47 @@ a malicious guest. Always prioritise remediation on hosts where
|
||||
### `smc_cpu_info`
|
||||
|
||||
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 |
|
||||
|---|---|---|
|
||||
| `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` |
|
||||
| `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 |
|
||||
| `model_id` | integer string | CPU model number |
|
||||
| `stepping` | integer string | CPU stepping number |
|
||||
| `cpuid` | hex string | Full CPUID value (e.g. `0x000906ed`); absent on some ARM CPUs |
|
||||
| `codename` | string | Intel CPU codename (e.g. `Coffee Lake`); absent on AMD and ARM |
|
||||
| `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 |
|
||||
| `cpuid` | hex string | Full CPUID value (e.g. `0x000906ed`) |
|
||||
| `codename` | string | Intel CPU codename (e.g. `Coffee Lake`); absent on AMD/Hygon |
|
||||
|
||||
**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:**
|
||||
@@ -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`
|
||||
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`)**
|
||||
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.
|
||||
|
||||
**`--sysfs-only`**
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#
|
||||
# Stephane Lesimple
|
||||
#
|
||||
VERSION='26.33.0419451'
|
||||
VERSION='26.33.0420455'
|
||||
|
||||
# --- Common paths and basedirs ---
|
||||
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' ' '
|
||||
}
|
||||
|
||||
# Convert a shell capability value to a JSON token
|
||||
# Args: $1=value (1=true, 0=false, -1/empty=null, other string=quoted string)
|
||||
# Prints: JSON token
|
||||
# Convert a shell capability value to a JSON boolean token
|
||||
# Args: $1=value (1=true, 0=false, -1/empty=null, any other non-empty string=true)
|
||||
# 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() {
|
||||
case "${1:-}" in
|
||||
1) printf 'true' ;;
|
||||
0) printf 'false' ;;
|
||||
-1 | '') printf 'null' ;;
|
||||
*) printf '"%s"' "$(_json_escape "$1")" ;;
|
||||
*) printf 'true' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -2513,7 +2515,7 @@ _build_json_system() {
|
||||
# Sets: g_json_cpu
|
||||
# shellcheck disable=SC2034
|
||||
_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
|
||||
cpuid_hex=$(printf '0x%08x' "$cpu_cpuid")
|
||||
else
|
||||
@@ -2524,6 +2526,15 @@ _build_json_cpu() {
|
||||
codename=$(get_intel_codename 2>/dev/null || true)
|
||||
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
|
||||
case "${cpu_vendor:-}" in
|
||||
GenuineIntel | AuthenticAMD | HygonGenuine)
|
||||
@@ -2577,7 +2588,7 @@ _build_json_cpu() {
|
||||
"$(_json_cap "${cap_tsa_l1_no:-}")" \
|
||||
"$(_json_cap "${cap_verw_clear:-}")" \
|
||||
"$(_json_cap "${cap_autoibrs:-}")" \
|
||||
"$(_json_cap "${cap_sbpb:-}")" \
|
||||
"$(_json_cap "$sbpb_norm")" \
|
||||
"$(_json_cap "${cap_avx2:-}")" \
|
||||
"$(_json_cap "${cap_avx512:-}")")
|
||||
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