From 0e787d6c49600713dbd91d1b377bbdaea9499173 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 22:27:45 +0000 Subject: [PATCH 1/2] docs(service-messaging): name the sys_webhook edit check behind the redeliver door The assertHttpRedeliverable doc comment and the parked-record integration test header still said POST /api/v1/webhooks/redeliver is reachable by any authenticated user. The door now admits a caller with edit (update) access to sys_webhook, decided by plugin-webhooks' redeliver-entitlement.ts. Name that check and point to the file; the refusal's own reason (a parked row carries no signature) is unchanged. Comments only. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4cyh2cxBQ2LKn2VVfxq --- .../src/http-outbox-parked-record.integration.test.ts | 4 +++- packages/services/service-messaging/src/http-outbox.ts | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/services/service-messaging/src/http-outbox-parked-record.integration.test.ts b/packages/services/service-messaging/src/http-outbox-parked-record.integration.test.ts index 76cbcb923f2..824a65d2930 100644 --- a/packages/services/service-messaging/src/http-outbox-parked-record.integration.test.ts +++ b/packages/services/service-messaging/src/http-outbox-parked-record.integration.test.ts @@ -11,7 +11,9 @@ * nothing. The obvious fix (write the discarded event as a `dead` row) is * *actively unsafe on its own*: `redeliver()` reset ANY terminal row to * `pending` with no signature check, behind `POST /api/v1/webhooks/redeliver` - * which any authenticated user can reach. A parked row has no signature — + * (admitted to a caller with edit access to `sys_webhook`, as decided by + * `packages/plugins/plugin-webhooks/src/redeliver-entitlement.ts`). A parked + * row has no signature — * the signature is computed at enqueue from the very secret that could not be * resolved — so redelivering one would put the payload on the wire UNSIGNED, * reopening #7799 through a door nobody would think to audit. diff --git a/packages/services/service-messaging/src/http-outbox.ts b/packages/services/service-messaging/src/http-outbox.ts index 7c03a1ea7d0..d0d2aed0939 100644 --- a/packages/services/service-messaging/src/http-outbox.ts +++ b/packages/services/service-messaging/src/http-outbox.ts @@ -487,8 +487,10 @@ export class HttpRedeliverError extends Error { * ## Why this is a security refusal and not a tidiness one * A parked row has no {@link HttpDelivery.signature}, because the signature is * computed at enqueue from the very secret that could not be resolved (#7722, - * #7799). `POST /api/v1/webhooks/redeliver` is reachable by any authenticated - * user. Without this refusal, making the drop durable would hand that user a + * #7799). `POST /api/v1/webhooks/redeliver` admits a caller with edit + * (`update`) access to `sys_webhook`, as decided by + * `packages/plugins/plugin-webhooks/src/redeliver-entitlement.ts`. Without this + * refusal, making the drop durable would hand any such webhook administrator a * button that delivers the webhook UNSIGNED — reopening #7799 through a door * nobody would think to audit. **An unsigned redelivery is strictly worse than * an unrecorded drop**, which is why this guard lands with — and logically From c4bf56b1fa1b6dbc7f02feabe0cb9c7b2fb179a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 22:53:50 +0000 Subject: [PATCH 2/2] docs(service-messaging): name the redeliver door's check in the redeliverHttp tenant note The #10740 paragraph of the redeliverHttp doc comment still said the door is open to any authenticated user. Name the sys_webhook edit check decided by plugin-webhooks' redeliver-entitlement.ts instead. Comment only. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4cyh2cxBQ2LKn2VVfxq --- .../services/service-messaging/src/messaging-service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/services/service-messaging/src/messaging-service.ts b/packages/services/service-messaging/src/messaging-service.ts index 40f99429280..c48be0bdccc 100644 --- a/packages/services/service-messaging/src/messaging-service.ts +++ b/packages/services/service-messaging/src/messaging-service.ts @@ -387,8 +387,10 @@ export class MessagingService { * * [#10740] `options.tenantId` is the REQUESTING caller's organization, and * it is the whole reason this method takes a second argument: the door in - * front of it (`POST /api/v1/webhooks/redeliver`) is open to any - * authenticated user, and `sys_http_delivery` is tenant-scoped. The + * front of it (`POST /api/v1/webhooks/redeliver`) admits any caller with + * edit (`update`) access to `sys_webhook`, as decided by + * `packages/plugins/plugin-webhooks/src/redeliver-entitlement.ts` — a caller + * in any organization — and `sys_http_delivery` is tenant-scoped. The * outbox applies it to the rows it reads and the row it writes, so a * caller can only replay deliveries in its own organization; a row * elsewhere is `RESOURCE_NOT_FOUND`. ⛔ There is no tenant-audit bypass