Skip to content

Releases: shuaiZend/PingWAF

v0.32.2

Choose a tag to compare

@github-actions github-actions released this 28 Oct 06:00

Security and robustness release. One new table (refresh_tokens) ships with
the automatic migration; /api/v1/version no longer exposes deployment posture.

Security

  • Unauthenticated control-plane crash fixed. An API key whose prefix was
    sliced on a non-UTF-8 boundary panicked; release builds abort on panic, so a
    single RegisterAgent call could stop the process. The prefix is now
    ASCII-checked and sliced with get.
  • Cross-tenant log read fixed. Listing logs with an explicit site id now
    validates site ownership, as every other site-scoped handler already did.
  • WAF request-body coverage. Unknown Content-Encoding (br/zstd/chained)
    is reported as uninspected instead of silently scanning compressed bytes;
    UTF-16 bodies are transcoded before scanning; multipart text parts are
    inspected as fields; h2/h3 GET/HEAD bodies are staged lazily.
  • Body staging is bounded and observable. Per-site quotas, deadline-driven
    sweep with a fail-closed tombstone, body_prefix_only /
    body_encoded_uninspected / body_expanded_cap events, and opt-in
    body_stage_policy=strict (413/408) / encoded_body_policy=reject (415).
  • bcrypt is offloaded and capped, and /mcp plus gRPC RegisterAgent are
    rate-limited per peer.
  • Refresh tokens are single-use. Every refresh token carries a jti; the
    new refresh_tokens table makes replay fail across restarts and instances.
  • Smaller hardening: control-plane WAF skips large upload bodies (no more
    413 on GeoIP/backup upload), API allowlist keeps its last known-good set when
    a referenced group disappears, access-log query strings redact known secrets,
    and configuration rollback is serialised with an advisory lock.

Fixed

  • Heartbeats cap blocked_ips (4096, with a blocked_ips_truncated flag) so a
    large block list cannot exceed the gRPC frame limit and wedge the link;
    truncation no longer revokes unseen rows.
  • Agent id is resolved once (empty config no longer mints a new UUID per call).
  • cgroup memory pressure no longer falls back to host-wide free memory.
  • Domain index removal only deletes entries still owned by the site.
  • Custom WAF rules that fail to compile are logged instead of dropped silently.
  • Config files are written atomically with mode 0600; header size limits count
    bytes; least-connections counters recover from a poisoned lock.
  • Cache keys include the request host; circuit breakers return to OPEN when a
    half-open probe is never answered; responses with cookies and no freshness
    are not cached via max_ttl.
  • CLI: config history/show/rollback are routed to maintenance mode; registry
    lists are pinned to clap; unknown config categories return 400.
  • Release: tag/version mismatch aborts before the release is created;
    install.sh honours PINGWAF_MODE/PINGWAF_DB_URL; the runtime image's
    healthcheck handles agent mode; publish.sh stops on the first failure.

Changed

  • GET /api/v1/version no longer returns grpc_tls. The flag moved to the
    admin-only GET /api/v1/system/status, and the console fetches it there.
  • New migration refresh_tokens.

Full Changelog: v0.0.0...v0.32.2

v0.32.1

Choose a tag to compare

@github-actions github-actions released this 27 Oct 17:01

Patch release focused on WAF memory amplification and control-plane write
amplification. No API, proto or database schema changes.

Fixed

  • WAF disabled/off sites no longer compile an engine. SiteContext only
    builds the per-site engine when enabled is true and mode is not off;
    off sites still run IP/geo/mTLS/basic-auth/bot/rate-limit controls and keep
    their access-log body prefix.
  • Site contexts are shared per site id. Alternate/wildcard hosts now point
    at the same Arc<SiteContext>; cache invalidation uses the immutable
    Arc<SiteRules> identity instead of the global config hash. The by-id table
    has its own TTL/entry sweep.
  • Regex compilation is bounded and cached. Parsed expressions are cached
    by expression text (512 entries; no id/name key), and regex construction has
    a 4MiB size/DFA limit with fallback to the previous builder.
  • Heartbeat certificate writes are coalesced. A heartbeat reads current
    certificate rows once and only updates sites whose status or expiry changed,
    instead of issuing one UPDATE per site per heartbeat.
  • SQLx pool acquisition is bounded. Requests now fail within 5s when the
    pool is saturated instead of waiting out the 30s default.

Full Changelog: v0.0.0...v0.32.1

v0.32.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 08:39

Major update: the control-plane listener, WAF resource bounds, site-level
timeouts and the dashboard ranking surfaces all move together in this release.

Added

  • Per-site downstream timeout. Site → Basic configures a 1–3600s read/write
    budget (default 60s), persisted on the site and delivered over proto tag 11.
    It applies after the Location matches; HTTP/1.1 keep-alive restores the
    listener default before the next request's headers, and HTTP/2 write budgets
    are per stream.
  • Shared dashboard ranking tables. The dashboard's top threat IPs/rules and
    the traffic page now use one table implementation with a window-aware log
    drill-down, loading/empty/error states and a consistent action tone map.
  • Response replacement on the Rewrite Rules page. The policy moved next to
    the other response-rewriting controls; the stored field and API are unchanged.

Fixed

  • Control-plane admin listener cannot be wedged by silent clients.
    MixedListener now accepts in a dedicated task and performs protocol
    detection/TLS handshakes in bounded per-connection tasks, with 5s peek, 10s
    TLS handshake, 128 pre-handshakes, 512 active connections, a 60s idle
    deadline and a reserved loopback pool. Health checks and readiness reporting
    are bounded as well.
  • WAF resource bounds and normalization cost. Expression depth/token/length
    limits, bounded query/JSON/decompression surfaces, multipart field scanning,
    ACME token validation, max_body_size/threshold clamps and per-request
    Cow normalization reduce both crash and memory-amplification risk.
  • Heartbeat persistence. Added migration 082 indexes, hourly certificate
    sweep throttling, empty blocked-IP fast deletion and monotonic conditional
    mark_offline, so a stale heartbeat cannot overwrite a newer one.
  • Proxy safety defaults. Public listeners get 60s downstream timeouts and
    keepalive/H2 bounds, generated locations get a 16MB body cap, upstream
    certificate verification defaults to strict, and retries are capped at three
    for idempotent methods only.

Full Changelog: v0.0.0...v0.32.0

v0.31.1

Choose a tag to compare

@github-actions github-actions released this 26 Oct 01:52

Patch release for ACME issuance reliability and repeatable browser E2E coverage.
No schema or API breaking changes; 0.31 and latest move to this patch.

Added

  • Browser E2E suite. Playwright scenarios cover site creation, routing and
    rewrites, multi-origin/health behavior, WebSocket, large/streamed bodies,
    cache hits, gzip, upstream TLS, path traversal, host mismatch, uploads and
    ACME staging. Tests require explicit demo/origin/domain inputs and never fall
    back to a shared production hostname.
  • Staging ACME opt-in. PINGAP_ACME_STAGING selects the staging directory
    and account (1/true/yes/on/staging/stage); production remains the default.

Fixed

  • ACME is independent of upstream availability. The issuance task and
    port-80 challenge handler stay enabled across hot reloads, and a site with an
    ACME certificate but no location keeps its certificate/listeners instead of
    dropping the whole data-plane config.
  • ACME retries are bounded. Failed orders back off 5m → 15m → 30m → 1h → 6h
    instead of creating a new order every minute; success clears the state.
  • ACME flags are consistent. PINGAP_DISABLE_ACME=false/0/no/off no longer
    disables issuance merely because the variable is present.
  • Failure diagnostics redact URL query strings before they reach the
    database, console or notifications.
  • New orders are attempted on the next one-minute scan rather than waiting
    for every tenth tick.

Full Changelog: v0.0.0...v0.31.1

v0.31.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 10:20

This release brings the Account page in line with Settings and makes
configuration history a complete, encrypted and restorable safety net.

Added

  • Account page parity with Settings. The same sticky quick navigation,
    scroll-spy and section layout, filtered by role. OIDC providers and visitor
    accounts come first, users next, and API keys are always last.
  • Visitor remarks. Create, edit, list and clear a console-only note of up to
    500 characters for recognising accounts in collaborative operation. Stored by
    a new migration and never exposed to the data plane.
  • Complete encrypted configuration backups. A unified 38-table restorable
    catalogue covers site policy, shared resources, OIDC, visitors and grants,
    notifications, AI, cache and certificates. Password-based Argon2id and
    AES-256-GCM encryption, zstd compression, SHA-256 verification, and bounded
    S3-compatible/GitHub upload tasks with expiring leases. Target credentials are
    sealed, never returned by the API, and remote storage receives only ciphertext.
  • Upload preview and safe restore. Decrypt, validate and preview before/after
    counts, changes, owner mappings and warnings. Confirmation creates an
    encrypted pre-restore backup and applies the replacement in the same
    serializable transaction, with runtime refresh warnings reported separately.
  • Reliable retention. The 30-day and newest-50 rules now run at startup,
    daily and on demand, and also apply to backup records. Backup lists read
    metadata only, not ciphertext.

Fixed

  • Snapshot and rollback correctness. v2 snapshots and v1 readers share one
    engine: missing tables are preserved, explicitly empty tables are cleared,
    global singleton objects are readable, new columns get database defaults, and
    restore uses actual writes rather than ActiveModel's Unchanged state.
  • Review-discovered safety issues. Corrected jsonb_populate_recordset
    argument order, prevented missing-sites pruning, retained complete
    configurations for sites outside ordinary rollbacks, bounded untrusted KDF
    parameters and concurrency, preserved exact download bytes, re-sealed
    instance-managed secrets for the restoring instance, fenced stale upload
    attempts and invalidated stale restore previews.
  • Certificates without a private key restore correctly. An explicit null
    key_pem is a legitimate certificate state, while non-nullable secret columns
    and unresolved secret materials still fail closed.
  • IP-group references validate correctly. Reference ids now use the same raw
    string form as database UUIDs instead of their JSON-quoted representation.

Full Changelog: v0.0.0...v0.31.0

v0.31.0-rc.3

v0.31.0-rc.3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 09:13

Pre-release. Third candidate: rc.2 plus the reference-validation fix from
the end-to-end run. Publishes :0.31.0-rc.3 only — :latest and :0.31 keep
pointing at the newest stable release. Pin it explicitly:
PINGWAF_TAG=0.31.0-rc.3.

Fixed

  • References to existing IP groups validate correctly. UUIDs were compared
    in their JSON-quoted form, so a valid group could be reported missing during
    preview and restore. Reference ids now use the same raw-string form as the
    database ids, and a regression test covers the comparison.

Full Changelog: v0.0.0...v0.31.0-rc.3

v0.31.0-rc.2

v0.31.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 08:21

Pre-release. Second candidate: rc.1 plus a restore fix found by the
end-to-end run. Publishes :0.31.0-rc.2 only — :latest and :0.31 keep
pointing at the newest stable release. Pin it explicitly:
PINGWAF_TAG=0.31.0-rc.2.

Fixed

  • Certificates with no private key can be restored. The secret preflight
    treated an explicit null key_pem as an attempt to bypass secret recovery,
    but that is a legitimate state on certificate tables. Preview and restore now
    accept it, while non-nullable secret columns and unresolved secret materials
    still fail closed.

Full Changelog: v0.0.0...v0.31.0-rc.2

v0.31.0-rc.1

v0.31.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 07:56

Pre-release. First candidate for the account-page and configuration-backup
work. Publishes :0.31.0-rc.1 only — :latest and :0.31 keep pointing at
the newest stable release, and GitHub marks it a pre-release so install.sh
(which resolves /releases/latest) is unaffected. Pin it explicitly:
PINGWAF_TAG=0.31.0-rc.1.

Added

  • Account page parity with Settings. The same sticky quick navigation,
    scroll-spy and section layout, filtered by role. OIDC providers and visitor
    accounts come first, users next, and API keys are always last.
  • Visitor remarks. Create, edit, list and clear a console-only note of up to
    500 characters for recognising accounts in collaborative operation. Stored by
    a new migration and never exposed to the data plane.
  • Complete encrypted configuration backups. A unified 38-table restorable
    catalogue covers site policy, shared resources, OIDC, visitors and grants,
    notifications, AI, cache and certificates. Password-based Argon2id and
    AES-256-GCM encryption, zstd compression, SHA-256 verification, and bounded
    S3/GitHub upload tasks with expiring leases. Target credentials are sealed,
    never returned by the API, and remote storage receives only ciphertext.
  • Upload preview and safe restore. Decrypt, validate and preview before/after
    counts, changes, owner mappings and warnings. Confirmation creates an
    encrypted pre-restore backup and applies the replacement in the same
    serializable transaction, with runtime refresh warnings reported separately.
  • Reliable retention. The 30-day and newest-50 rules now run at startup,
    daily and on demand, and also apply to backup records. Backup lists read
    metadata only, not ciphertext.

Fixed

  • Snapshot and rollback correctness. v2 snapshots and v1 readers share one
    engine: missing tables are preserved, explicitly empty tables are cleared,
    global singleton objects are readable, new columns get database defaults, and
    restore uses actual writes rather than ActiveModel's Unchanged state.
  • Review-discovered safety issues. Corrected jsonb_populate_recordset
    argument order, prevented missing-sites pruning, retained complete
    configurations for sites outside ordinary rollbacks, bounded untrusted KDF
    parameters and concurrency, preserved exact download bytes, re-sealed
    instance-managed secrets for the restoring instance, fenced stale upload
    attempts and invalidated stale restore previews.

Full Changelog: v0.0.0...v0.31.0-rc.1

v0.30.2

Choose a tag to compare

@github-actions github-actions released this 24 Oct 16:24

Security

  • The WAF's step-aside for the visitor login page covered a prefix, which was
    a way around the WAF.
    0.30.1 skipped every path starting with /_pingwaf/
    so a rule could not block the site's own front door. But a prefix matches
    /_pingwaf/<anything>: on a site with no login page — where nothing serves
    that prefix — the request skipped every rule and went straight upstream, and
    the skip sat before the dynamic IP block, so a blocked client could still use
    it. Confirmed against a live deployment: /.env and a random path were
    blocked, while /_pingwaf/anything reached the origin. /_pingwaf/../admin
    matched too, and most origins normalise the .. away — a path-traversal hole
    rather than just an unprotected one.

    The skip is now the exact endpoints the plugin owns, and only when the site's
    chain actually carries the plugin that serves them. On any other site those
    paths are not an endpoint, they are just a path, and a path that skips every
    rule is a hole.

Fixed

  • The stop record was matched without an upper bound. stopped_at >= previous.booted_at alone accepts a row written later — by a second process
    sharing the hostname, by a restored dump, by a fast clock — as evidence about
    a boot it never described, which would label a crash a planned stop. That is
    the one direction the restart-history mechanism must not fail in, so the
    match is bounded at both ends now.

Full Changelog: v0.0.0...v0.30.2

v0.30.1

Choose a tag to compare

@github-actions github-actions released this 24 Oct 15:19

Four findings from a full test pass over 0.30.0, three of them latent since the
features they belong to landed.

Fixed

  • A planned restart no longer looks like a crash. The control-plane restart
    history inferred everything from the gap between two starts, which cannot
    tell a docker compose restart from a kill -9: both are followed by a
    start seconds later, so every planned restart was labelled unclean and an
    ordinary redeploy read as a crash loop. A process that is given a chance to
    stop can say so, so it now writes a stop row on the way out
    (control_plane_stops, migration 077) and the next start reads it. The row
    is matched against the previous boot's own timestamp rather than merely
    existing, so a stop left over from two restarts ago cannot make a crash look
    planned.
  • The restart history's retention bound is per host, not per table. One host
    in a restart loop could push another host's rows out of the window, so the
    replica that is fine lost the history an operator would read to see that it
    is fine. Each host is trimmed to its own window now — also the unit the
    console reads in.
  • A WAF rule could block the visitor login page. The visitor plugin runs
    after the WAF, so a rule that matched /_pingwaf/login answered the site's
    own front door with a block page. Found by driving the flow against a live
    deployment: a burst of sign-ins tripped a rule and every later visit to the
    login page returned 403 until the IP was unblocked by hand — a lockout with
    no way back in from the site itself. The whole /_pingwaf/ surface is now
    skipped, because no rule an operator writes should be able to lock them out
    of the way back in. The endpoints keep their own protection: a proof-of-work
    challenge gates every sign-in.
  • The demo's 443 handshake failure was not a code fault. The site had no
    certificate at all, so the listener fell back to another site's and the
    signature algorithm it offered did not suit the client. A certificate for the
    site fixes it; TLS 1.3 completes and the site answers.

Full Changelog: v0.0.0...v0.30.1