Fix audit findings: bakery index signing, artifact checksums, stale theme docs

- Add minisign-based signing/verification for the bakery index:
  scripts/gen-index.sh signs index.json (MINISIGN_SEC_KEY env var, dormant
  no-op with a loud warning until a key is provisioned); bakery/src/manifest.rs
  fetches index.json.minisig and verifies it with minisign-verify against a
  hardcoded PUBKEY before parsing/caching, and re-verifies the cached copy
  on every load (falls back to one re-fetch if the cache predates signing
  or fails verification; a fresh fetch that fails verification is a hard
  error).
- Close the previously-unchecksummed config-example and systemd-unit
  downloads in bakery/src/install.rs (scaffold_config, install_service):
  index.json now carries `sha256`/`example_sha256` for these artifacts
  (computed in gen-index.sh), verified via the same download::verify_sha256
  used for binaries. Downloads without a matching sha256 in the index are
  refused rather than installed unverified.
- scripts/get.sh now verifies the bakery release binary itself against a
  pinned minisign public key before installing it (falls back to the
  existing sha256-only check with a loud warning if no .minisig is
  published yet or minisign isn't installed; a present-but-invalid
  signature is a hard failure).
- Add dormant "sign release binary" steps to the bakery and bread-theme
  release workflows (.github/workflows/release.yml,
  .forgejo/workflows/release-bread-theme.yml), gated on secrets that are
  not yet configured — binaries ship unsigned exactly as before until the
  owner wires up the secret.
- .gitignore: add *.minisign-sec / minisign.key so the signing key can
  never be committed by accident.
- bread-theme: fix stale docs describing a "Catppuccin Mocha fallback"
  (BREAD_DESIGN_SYSTEM.md, README.md, Cargo.toml/bakery.toml/registry
  descriptions) — the actual implementation (palette.rs) uses a fixed BOS
  dark base with only accent colors from pywal.
- bread-theme: fix the legacy css_vars() path, which had its own
  hand-written @define-color block that predated the `accent` and computed
  `on-*` ink colors used by the rest of the stylesheet — any caller whose
  CSS referenced those names against css_vars()'s output would hit
  undefined colors (the illegible-text bug). css_vars() now delegates to
  the same define_colors() the full stylesheet uses, so the two can't
  drift apart again.
This commit is contained in:
Breadway 2026-07-17 03:37:51 +08:00
parent fa0597f482
commit 394a252f9e
18 changed files with 472 additions and 84 deletions

View file

@ -4,6 +4,13 @@
# Or: curl -sSfL https://breadway.dev/get | sh
set -eu
# Pinned minisign public key for the bakery release binary. Matches the
# PUBKEY constant in bakery/src/manifest.rs (same keypair signs both
# index.json and the bakery binary itself). Do not source this from the
# network — it must be baked into this script so a compromised dl server
# can't swap it out along with a malicious binary.
BAKERY_MINISIGN_PUBKEY="RWRh2Zr5SUinvVFCtD7S7HwGjfrye6j31Xq2mYXRdkGFDWe3yHF7W11K"
BAKERY_VERSION="${BAKERY_VERSION:-latest}"
BIN_DIR="${BAKERY_BIN_DIR:-$HOME/.local/bin}"
@ -19,12 +26,16 @@ if [ "${BAKERY_VERSION}" = "latest" ]; then
DL_PRIMARY="https://dl.breadway.dev/bakery/latest/bakery-x86_64"
DL_FALLBACK="https://github.com/Breadway/bread-ecosystem/releases/latest/download/bakery-x86_64"
SHA256_URL="https://dl.breadway.dev/bakery/latest/bakery-x86_64.sha256"
SIG_URL="https://dl.breadway.dev/bakery/latest/bakery-x86_64.minisig"
SIG_FALLBACK="https://github.com/Breadway/bread-ecosystem/releases/latest/download/bakery-x86_64.minisig"
else
# Strip a leading 'v' if the caller included it, then add it back consistently.
ver="${BAKERY_VERSION#v}"
DL_PRIMARY="https://dl.breadway.dev/bakery/${ver}/bakery-x86_64"
DL_FALLBACK="https://github.com/Breadway/bread-ecosystem/releases/download/v${ver}/bakery-x86_64"
SHA256_URL="https://dl.breadway.dev/bakery/${ver}/bakery-x86_64.sha256"
SIG_URL="https://dl.breadway.dev/bakery/${ver}/bakery-x86_64.minisig"
SIG_FALLBACK="https://github.com/Breadway/bread-ecosystem/releases/download/v${ver}/bakery-x86_64.minisig"
fi
# Pick a download tool.
@ -38,30 +49,63 @@ fi
mkdir -p "${BIN_DIR}"
TMP="$(mktemp)"
trap 'rm -f "${TMP}" "${TMP}.sha256"' EXIT
trap 'rm -f "${TMP}" "${TMP}.sha256" "${TMP}.minisig"' EXIT
echo "downloading bakery…"
if fetch "${DL_PRIMARY}" "${TMP}" 2>/dev/null; then
echo " from dl.breadway.dev"
# Verify checksum when available from primary.
if fetch "${SHA256_URL}" "${TMP}.sha256" 2>/dev/null; then
expected="$(awk '{print $1}' "${TMP}.sha256")"
actual="$(sha256sum "${TMP}" | awk '{print $1}')"
if [ "${expected}" != "${actual}" ]; then
die "SHA-256 checksum mismatch (expected ${expected}, got ${actual})"
fi
echo " checksum verified"
else
echo " warning: could not fetch checksum — skipping verification"
fi
sig_url="${SIG_URL}"
checksum_only_fallback_note=" warning: could not fetch checksum — skipping verification"
elif fetch "${DL_FALLBACK}" "${TMP}" 2>/dev/null; then
echo " from GitHub (fallback)"
# No .sha256 on the GitHub fallback path; proceed without verification.
echo " warning: checksum not verified for GitHub fallback download"
sig_url="${SIG_FALLBACK}"
checksum_only_fallback_note=" warning: no checksum available for GitHub fallback download"
else
die "failed to download bakery from both primary and fallback URLs"
fi
# Signature verification is the authoritative check: it proves the binary
# was produced by whoever holds the bakery signing key, not just that bytes
# match whatever the same (possibly compromised) server also reports as the
# checksum. Prefer it whenever both a .minisig is published and a minisign
# verifier is available on this machine.
sig_verified=0
if fetch "${sig_url}" "${TMP}.minisig" 2>/dev/null; then
if command -v minisign >/dev/null 2>&1; then
if minisign -V -q -m "${TMP}" -x "${TMP}.minisig" -P "${BAKERY_MINISIGN_PUBKEY}"; then
echo " signature verified (minisign)"
sig_verified=1
else
die "minisign signature verification FAILED — refusing to install a binary that doesn't match the pinned bakery key"
fi
else
echo " warning: 'minisign' is not installed — cannot verify the binary's" >&2
echo " warning: signature, only its checksum. Install minisign for the" >&2
echo " warning: strongest guarantee: pacman -S minisign / apt install minisign" >&2
fi
else
echo " warning: no .minisig published for this release yet — signature not verified" >&2
fi
# Checksum is a secondary, best-effort check (kept for defense in depth and
# for the case where minisign isn't installed). It is not a substitute for
# signature verification: both the binary and its checksum typically come
# from the same server, so a compromised server can serve a matching pair.
if fetch "${SHA256_URL}" "${TMP}.sha256" 2>/dev/null; then
expected="$(awk '{print $1}' "${TMP}.sha256")"
actual="$(sha256sum "${TMP}" | awk '{print $1}')"
if [ "${expected}" != "${actual}" ]; then
die "SHA-256 checksum mismatch (expected ${expected}, got ${actual})"
fi
echo " checksum verified"
else
echo "${checksum_only_fallback_note}"
fi
if [ "${sig_verified}" -ne 1 ]; then
echo " warning: proceeding WITHOUT a verified signature on the bakery binary" >&2
fi
chmod +x "${TMP}"
cp "${TMP}" "${BIN_DIR}/bakery"
echo "installed bakery to ${BIN_DIR}/bakery"