• v0.12.0 41a4d55b68

    v0.12.0
    All checks were successful
    test / test (push) Successful in 19s
    release / release (push) Successful in 21s
    Stable

    Ghost released this 2026-07-03 12:29:48 +00:00 | 1 commits to main since this release

    Security hardening sweep — a 4-agent audit (deploy.sh, Python worker, infra/web) plus a docs↔code
    security-claims cross-check. Fixes below, ranked by severity.

    Security

    • CRITICAL — root RCE via set-fqdn. An unvalidated FQDN (set-fqdn <arg> / PM_FQDN) was spliced
      into a sed -i script; a | or newline could trigger GNU sed's e command → arbitrary command
      execution as root (and YAML injection into packagemini.yml). FQDNs are now validated against a
      hostname charset (_check_fqdn) before they reach sed / the config writer.
    • HIGH — GoAccess board exposure. The board's :7443 listener — and its unauthenticated /ws
      WebSocket — inherited none of the mirror's IP allow/deny gate, rate-limit, or headers, so an
      allowlist didn't cover the board and basic-auth brute-force was unthrottled. The board block now applies
      the same allow/deny (covering /ws), a rate-limit, and headers; the raw log archive under
      /srv/report/archive is hidden from browsing.
    • HIGH — path traversal. version/arch/pin tokens reached mkdir before validation. A new
      safe_token() guard gates every token before it builds a filesystem path or URL — at discovery
      (sources.py), at config load (architectures/pin rejected), and defensively before mkdir.
    • MEDIUM — Caddyfile injection. public_base_url, tls_*, usernames, *_hash_env, and the server
      timeouts are validated at config load (reject braces/tabs/newlines; env-name + username charsets),
      matching the existing rate / CIDR validation.
    • MEDIUM — secret & permission exposure. Passwords are hashed via stdin (no longer visible in
      ps / argv); .env and the imported TLS key are written under a private umask (no world-readable
      window); the public-facing web container gets no-new-privileges (matching sync/goaccess).
    • MEDIUM — resource DoS. Response reads are capped (8 MiB text / 4 GiB artifact) so a misbehaving
      upstream can't exhaust worker memory; the main npk is size-checked after download (verify_npk wired
      into the sweep) and re-fetched if it fails.
    • LOW / defense-in-depth. git refs rejected if they start with - or contain metacharacters; strict
      CSP on the (fully self-contained) mirror site; the feed uses quoteattr for XML attributes; the
      SSH key-only guard now requires a non-empty authorized_keys (avoids lockout); marker dotfiles
      (.complete/.size/.absent) hidden from the file server.

    Docs

    • Corrected security claims that over-promised: sources.archive_backfill is now marked not yet
      implemented
      (it was a silent no-op stub), and the false "checks NPK magic bytes" claim is replaced
      with the actual behavior (TLS-verified fetch + a size sanity check; no cryptographic verification).
      SYSTEM-HARDENING documents the 7443/tcp firewall opening and the board's public-listener caveat.
    • New doc/REAL-TIME-MONITORING.md — the GoAccess board + live-update
      WebSocket architecture, with a dashboard screenshot.
    Downloads