MACGUARD · DOCUMENTATION
Español

Verifying rules, signatures and transparency

How to check a release yourself without trusting this site: commands, real URLs and how to read the results.

A. What you will check

A release is valid if (1) the manifest and the checkpoint are signed by the expected workflow identity, (2) every package file matches the SHA-256 in the signed manifest, (3) the recomputed Merkle root equals rules_root, and (4) the checkpoint contains the release entry and extends the one you already knew.

LIMIT A valid signature and matching hashes prove who published exactly those bytes. They do not prove the rules are good or that scanned files are safe.

B. Requirements

C. Artifacts of the latest release

Release rules-20261018-39150886414. The mandatory artifacts download from this path:

https://github.com/MacGuard/macguard-yara-channel/releases/download/rules-20261018-39150886414/rules-release.tar.gz
https://github.com/MacGuard/macguard-yara-channel/releases/download/rules-20261018-39150886414/manifest.json
https://github.com/MacGuard/macguard-yara-channel/releases/download/rules-20261018-39150886414/manifest.sigstore.json
https://github.com/MacGuard/macguard-yara-channel/releases/download/rules-20261018-39150886414/inclusion-proof.json
https://github.com/MacGuard/macguard-yara-channel/releases/download/rules-20261018-39150886414/checkpoint.txt
https://github.com/MacGuard/macguard-yara-channel/releases/download/rules-20261018-39150886414/checkpoint.sigstore.json

Optional: release.json, changes.json, CHANGELOG.md and consistency.json (the latter proves the log extends the previous release log). To download them all:

mkdir verify && cd verify
gh release download rules-20261018-39150886414 --repo MacGuard/macguard-yara-channel

D. Verify the signature with cosign

Minimal example with the expected identity and issuer:

cosign verify-blob manifest.json \
  --bundle manifest.sigstore.json \
  --certificate-identity "https://github.com/MacGuard/macguard-yara-channel/.github/workflows/publish.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com"

Full version, which also requires the certificate to name this repository, for the manifest and for the checkpoint:

cosign verify-blob manifest.json \
  --bundle manifest.sigstore.json \
  --certificate-identity "https://github.com/MacGuard/macguard-yara-channel/.github/workflows/publish.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --certificate-github-workflow-repository "MacGuard/macguard-yara-channel"

cosign verify-blob checkpoint.txt \
  --bundle checkpoint.sigstore.json \
  --certificate-identity "https://github.com/MacGuard/macguard-yara-channel/.github/workflows/publish.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --certificate-github-workflow-repository "MacGuard/macguard-yara-channel"

Correct result: Verified OK. If the identity or issuer do not match, cosign fails; do not run anything from that release.

E. Verify the package hashes

SHA-256 of the package (must be 97304ccc48e2ab69368618c49dbf40f18cd7d0c310dfd4ee736ed8823170a8d0 for this release) and of the manifest (f75c214683d6c169716be1a5ac6f90a7c9a74f408c5cd139553a87e5a25370ad):

# macOS
shasum -a 256 rules-release.tar.gz manifest.json
# Linux
sha256sum rules-release.tar.gz manifest.json

The package hash is not signed separately; what ties it to the signature is that every extracted file has the SHA-256 listed in manifest.json. The verifier does it for you (section G).

F. Recompute rules_root and inclusion

The manual recomputation of rules_root is in Merkle trees. The inclusion proof (inclusion-proof.json) is checked by the verifier: it carries the release log entry up to the checkpoint root and checks that this entry names the signed rules_root.

G. Public verifier

The verify_public_release.py script does all of the above in one go (Python standard library and cosign). Download it to a file, read it and run it; do not pipe it into an interpreter.

curl -fsSLo verify_public_release.py https://raw.githubusercontent.com/MacGuard/macguard-yara-channel/main/scripts/verify_public_release.py
less verify_public_release.py
python3 verify_public_release.py --latest

Options: --latest, --tag TAG, --directory DIR (already downloaded artifacts), --json, --previous-checkpoint FILE or --trust-state FILE (rollback protection), --token or the GITHUB_TOKEN variable (optional). It works without a token: the anonymous GitHub API allows 60 requests per hour per IP address; if that runs out (HTTP 403 with X-RateLimit-Remaining: 0) the verifier says so with the reset time and, without using the API, resolves latest through the github.com redirect and downloads the same artifacts (Content-Length is checked, not the API size) and notes it in the report. A 403 with no exhausted quota (firewall, proxy) is reported as such. Real output, abridged, on the latest release:

MacGuard release verification — PASS (exit 0)

Release        rules-20261018-39150886414  (latest)
Files          1174   individual YARA rules: 9958
rules_root     16d082395ef1ec53d0ad25d5840d83e7f5f3a75a283ce66f8fa75da106790ec5

  [PASS] signature.manifest
  [PASS] signature.checkpoint
  [PASS] files.sha256
  [PASS] merkle.rules_root
  [PASS] merkle.inclusion
  [n/a ] consistency: no previous checkpoint was given
CURRENT Without --previous-checkpoint or --trust-state the verifier says so explicitly: it is a first verification and rollback protection is NOT established by that run.

Exit codes

CodeMeaning
0All mandatory checks done and passed.
1Integrity: a hash does not match.
2Invalid signature or identity.
3Merkle, inclusion or consistency.
4Incomplete download or network error.
5A dependency is missing (cosign).
6Invalid format or incomplete set.

If there are several failures the report lists them all and the exit code is the first by this precedence: 5, 4, 6, 2, 1, 3.

Diagram: Independent verifier

Drawing the diagram…

Diagram description

verify_public_release.py resolves the release once and pins the tag, downloads the mandatory artifacts, verifies the Sigstore identity of the manifest and the checkpoint, extracts the package with limits, compares every hash, recomputes rules_root, checks counts, checkpoint, inclusion and optional consistency, and exits with a code that names the kind of failure.

Mermaid code
flowchart TD
  A["resolve tag once<br/>--latest or --tag"]:::src --> B["download mandatory artifacts<br/>exit 4 if incomplete"]:::src
  B --> C["cosign verify-blob<br/>manifest and checkpoint<br/>exit 2 or 5"]:::sec
  C --> D["safe extraction<br/>exit 6 if unsafe"]:::val
  D --> E["SHA-256 of every file<br/>exit 1 if different"]:::val
  E --> F["recompute rules_root<br/>exit 3"]:::sec
  F --> G["checkpoint equals manifest log head<br/>inclusion proof<br/>exit 3"]:::sec
  G --> H{"previous checkpoint<br/>given?"}:::gate
  H -->|yes| I["consistency proof and rollback check<br/>exit 3"]:::sec
  H -->|no| J["report: rollback protection not established"]:::qua
  I --> K["PASS exit 0"]:::pub
  J --> K
  classDef src fill:#cfe3ff,stroke:#2f6df0,color:#0b1b33
  classDef val fill:#ffe9b8,stroke:#b8860b,color:#2b1d00
  classDef sec fill:#cdeedd,stroke:#1b6e3c,color:#08210f
  classDef pub fill:#e4d7ff,stroke:#6a3fd1,color:#1b0b3b
  classDef qua fill:#ffd4cc,stroke:#c0392b,color:#33100b
  classDef gate fill:#ffffff,stroke:#111111,stroke-width:3px,color:#111111

H. How to know the verifier itself is the right one

The verifier is code you run: read it (about 400 lines) and compare the commit on GitHub. Downloading it from the same site that hosts the rules is not an independent root of trust.

PROPOSED Signing verify_public_release.py with Sigstore in publish.yml and attaching it to every release is a proposed improvement: it does not exist yet, so current releases carry no signed copy of the verifier.

I. What to do if it fails