From ea92c616c230f3a9eafbe6bf7d7898afbf12bd10 Mon Sep 17 00:00:00 2001 From: clawbot Date: Wed, 12 Aug 2026 10:42:41 +0000 Subject: [PATCH] Correct release-blocking documentation inaccuracies (closes #141) Four defects found by the integration review, each of which would have made the README or the release notes untrue at the moment of tagging. RETENTION_SWEEP_INTERVAL was absent from the README env table while two other passages referred to it as documented. Enumerated every variable read by internal/config from the source (12 in total) rather than by eye; that was the only one missing. TODO.md omitted five of the units landed in this milestone (#64, #79, #90, #113, #118), two of them credential-exposure fixes, which are precisely the entries a reader of the release notes wants to find. The list is now derived from git log origin/main..origin/next. The README sold Replay in the present tense as a core capability while no redelivery code exists anywhere in the tree, and the roadmap entry for it had been dropped without it being implemented. The README now says planned, and the roadmap entry is back. The production JS asset shipped a console.log on load. The rest of the file and every other shipped asset were checked; that was the only one (alpine.min.js is vendored and untouched). No behavioural change: the only non-documentation edit is the deleted console.log. --- README.md | 9 ++++++--- TODO.md | 31 +++++++++++++++++++++++++++++++ static/js/app.js | 1 - 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d089ea3..7b8a11f 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ TTY detection, and security headers are always applied. | `METRICS_USERNAME` | Basic auth username for `/metrics` | `""` | | `METRICS_PASSWORD` | Basic auth password for `/metrics` | `""` | | `SENTRY_DSN` | Sentry error reporting DSN | `""` | +| `RETENTION_SWEEP_INTERVAL` | How often the retention reaper and archive sweeper run (Go duration) | `1h` | | `SESSION_IDLE_TIMEOUT` | Idle session timeout (Go duration) | `24h` | | `RECEIVER_RATE_LIMIT` | Receiver requests/minute per IP per entrypoint | `120` | | `TRUSTED_PROXIES` | CIDRs whose forwarded headers are trusted | `""` (none) | @@ -260,9 +261,10 @@ webhooker solves this by acting as a durable intermediary: targets simultaneously. This enables patterns like forwarding a GitHub webhook to both a deployment service and a Slack channel. -5. **Replay** — Stored events can be manually redelivered for debugging - or testing, without requiring the original sender to fire the webhook - again. +5. **Replay** (not yet implemented) — Every received event is stored in + full, which is what manual redelivery for debugging or testing will + be built on. No redelivery exists today, in the web UI or the API; + see [TODO.md](TODO.md). ### Use Cases @@ -272,6 +274,7 @@ webhooker solves this by acting as a durable intermediary: size, and delivery performance - **Debugging** and introspection of webhook payloads in the web UI - **Replay** of webhook events for application testing and development + (planned; not yet implemented) - **Fan-out** delivery of a single webhook to multiple downstream targets - **High-availability ingestion** for delivery to less reliable backend diff --git a/TODO.md b/TODO.md index 8b1ee6d..2b341dc 100644 --- a/TODO.md +++ b/TODO.md @@ -61,6 +61,34 @@ rests on. Profile settings placeholder removed, a progressive-enhancement copy button for the entrypoint URL, and retention form copy that states the actual policy (deletion by the reaper, 0 retains forever) (#57) +- 2026-08-11 Mask the webhook credential in delivery errors and logs: + Go embeds the request URL in `*url.Error`, so every transport failure + persisted the full Slack webhook URL into the per-webhook event + database via `DeliveryResult.Error`, a field a future REST API would + have served. `maskURLError` drops path, query and userinfo while + preserving the wrapped cause, so `errors.Is`/`As` and `Timeout()` + still work and DNS, TLS and timeout failures still read differently + (#118) +- 2026-08-11 Rate-limit the public webhook receiver endpoint + (`RECEIVER_RATE_LIMIT`, default 120/min), keyed on client IP plus + entrypoint path so one entrypoint cannot exhaust another's budget; + over-limit requests get 429 with `Retry-After`. It was the one + unauthenticated, internet-facing endpoint with no limit at all (#64) +- 2026-08-11 Enforce the body size limit before CSRF parses the form: + `MaxBodySize` is now first in all four form-parsing route groups, so + an oversized request is rejected with 413 instead of being read in + full by the CSRF middleware before any cap applied (#90) +- 2026-08-11 Mask target config on the source detail page, which + rendered the stored blob verbatim and so exposed the Slack + incoming-webhook URL — a bearer credential that cannot be revoked + per-holder. Config reaches the template only as a `TargetView` of + labelled fields, and header values are rendered as a count (#113) +- 2026-08-11 Allow `retention_days` of 0 to mean retain forever, via a + sentinel written in `BeforeSave` so the GORM column default cannot + win the race. Also bounds the reaper's cutoff arithmetic: day counts + above 106751 overflowed `time.Duration` and wrapped the cutoff into + the future, where every row matched and the sweep deleted everything + (#79) - 2026-08-09 Inactivity-based session timeout: sliding idle expiry (`SESSION_IDLE_TIMEOUT`, default `24h`) refreshed on authenticated requests, with the 7-day absolute cap kept as an independent @@ -115,6 +143,9 @@ rests on. # Future Steps +- Manual event redelivery from the web UI — the "Replay" capability the + README describes as planned. No redelivery code exists anywhere in the + tree; events are stored in full, which is all it would be built on - Delivery status and retry management UI - Per-webhook rate limiting in the receiver handler (per-webhook config plus handler enforcement; global limits must not apply to receiver diff --git a/static/js/app.js b/static/js/app.js index a4d9fc8..ee4f7fd 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1,5 +1,4 @@ // Webhooker client-side JavaScript -console.log("Webhooker loaded"); // Copy-to-clipboard, as progressive enhancement. //