Feat/connector 6 webhook in - #359
Merged
Merged
Conversation
… 11b.8 ingress plane An installed connector's SyncMapping can now register a shell-minted inbound webhook endpoint: an authenticated POST to /wh/<routeId>/<secret> on the EXISTING 11b.8 loopback listener (one HTTP server, never a second) acts as a doorbell that dispatches connectors.sync(mappingRef) — the payload is discarded, so hostile body bytes can never enter the projection; the provider's delta API stays the source of truth (doc 56 §Sync model). Security posture: - Custody: hash-only at rest. registry.db connector_webhooks stores the SHA-256 of the endpoint secret; the plaintext exists exactly once, inside the webhookRegister response URL. status never returns it; hits never carry it; the ingress plane verifies by hashing the presented value and timingSafeEqual over fixed-length digests. - Fail-closed gates: server-side connectors.webhook cap on every method; registration additionally requires the RESOLVED connector app's runtime network.ingress grant (11b.8 model); envelope.app is pinned to the mapping's owner (a sibling app cannot mint/see/kill another connector's endpoint); routes only register while the grant holds (per-app filter at hydrate); missing ledger/store reads as no routes/Unavailable. - Bounded ingress: rides the 11b.8 listener unchanged (127.0.0.1-only bind, POST-only, 256 KiB cap, oracle-free 404, constant-time auth); dispatch is per-mapping coalesced (one in-flight sync + one trailing run), so sync rate is capped by sync duration, not inbound request rate. - Lifecycle: endpoints survive restart via the typed ConnectorWebhooksRepository (registry.db v12), die with the mapping (webhookRevoke) and with the account (revoke cascade on disconnect); rehydrate() drops/adds routes live and can bind the listener mid-session for the first mint (fixed a latent host gap where a listener set after start() registered routes but never subscribed). Surface: connectors.webhookRegister / webhookStatus / webhookRevoke (SDK proxy + sdk-types); WebhookRoute/WebhookHit generalized with a WebhookTargetKind enum (workflow | connector-sync) across listener, relay port (desktop-side re-verify incl. hash routes), fan-in, and host. Scoped OUT (documented boundary): public reachability stays owner-ops — the relay node route/deploy from 11b.8 is unchanged and dormant until a transport is paired; this PR ships loopback (127.0.0.1) ingress only. Provider-specific HMAC signature schemes (e.g. X-Hub-Signature) are not implemented; the shell-minted per-endpoint secret is the authentication. Endpoints are device-local (registry.db never syncs), matching the designated-automation-host ingress model. Tests: +45 across repo (mint/rotate/replace, hash-only custody, restart persistence, revoke cascades), listener (digest auth, digest-as-secret rejected, no-verifier fail-closed), host dispatch (doorbell, coalescing, revoke race, failure re-arm), deployment wiring (real loopback end-to-end, per-app grant fail-closed, live revoke, mid-session first-mint bind), and service (cap denial, ingress-grant denial, owner pinning, reveal-once, Unavailable without vault). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…heck, oracle-free denial, type resolve The /security-review + /pentester gates on #352 found five issues; all are fixed here and the adversarial probes are kept as regression tests. C1 (Medium) — a revoked `network.ingress` left live endpoints pulling. The ingress route table re-derives only on an automation-entity write or an explicit rehydrate(), and `ledger:revoke` did neither, so a user who revoked the grant in Settings kept an endpoint that answered 202 AND still ran `connectors.sync` (provider egress + entity writes) indefinitely. Closed twice over: `AutomationsHost` now re-checks the grant against the live ledger before every webhook-driven sync (fail-closed on throw, webhook path only — Connector-4's scheduled mapping fires are untouched), and `ledger:revoke` re-derives the route table so the URL actually 404s. Red-checked by reverting the first half. C2 (Low) — `Invalid: unknown mapping` vs `Denied: does not own mapping` gave any `connectors.webhook` holder a cross-app mapping-existence oracle. Now one uniform Denied. C3 (Low) — `resolveMapping` is purely structural, so an endpoint could be minted against any entity carrying `accountRef` + `pull`. `resolveMappingOwner` now asserts SYNC_MAPPING_TYPE_URL server-side. C4 (Low) — `mint`'s DELETE+INSERT was unwrapped; a failed insert destroyed the old endpoint with no replacement. Now in a transaction. C5 (Low, process) — the branch did not typecheck (2x TS2349 in the Connector-6 tests: a `let` assigned only inside a callback narrows to null). CI would have rejected the PR. +19 adversarial probes (connector-webhook-attack.test.ts x2) covering owner-pinning bypass, forged envelope.app, digest-as-secret replay, timingSafeEqual length confusion, 9 path-traversal/injection shapes, byte-identical 404 differentiation, the method oracle, oversized doorbell bodies, WebhookTargetKind confusion in both directions, coalescer starvation and cross-mapping fairness, and hostile argument shapes incl. __proto__. 87 files / 901 tests green; lint + typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.