Skip to content

fix(bridge): harden transports, privacy gates, and envelope guard - #409

Merged
vamgan merged 2 commits into
mainfrom
fix/bridge-review-findings
Aug 9, 2026
Merged

vamgan merged 2 commits into
mainfrom
fix/bridge-review-findings

Conversation

@vamgan

@vamgan vamgan commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Full review of @askable-ui/bridge (#406) and its interaction with @askable-ui/context, @askable-ui/core, and @askable-ui/mcp. Every finding is addressed here.

Security and correctness

  • postMessage no longer defaults to targetOrigin: '*'. It falls back to the sending page's origin and throws when none can be determined — the same invariant @askable-ui/mcp's page bridge already documents in code ("Never fall back to '*': context packets must not be broadcast to arbitrary origins"), which fix(security): audit fixes — storage masking defaults, page-bridge target origin, CI permissions #362 hardened and this package had reintroduced. Broadcasting is now an explicit allowAnyOrigin opt-in.
  • HTTP headers are normalized through Headers before merging. HeadersInit covers Headers instances and entry arrays, and object spread silently drops both — an Authorization header supplied in either form never reached the request.
  • Duplicate transport ids throw instead of silently replacing the earlier transport (every built-in defaults to a per-kind id, so two createHttpTransport() calls collided on 'http'). Unregister handles now delete by identity rather than by id.
  • Sends use allSettled. A throwing transport becomes an ok: false ack instead of rejecting the batch and discarding the acks of transports that succeeded.

Privacy

  • requireRedacted, allowedConsent, and maxEnvelopeBytes gate every send before any transport runs. requireRedacted mirrors the option in @askable-ui/mcp and defaults to false for the same reason.

Previously the envelope copied packet.privacy.consent and sent unconditionally — and since toContextPacket() stamps consent: 'implicit' and redacted: false unless sanitizers are registered, that field was decorative on the one component that actually crosses the trust boundary.

API

  • sendAgentRequest() accepts a ctx.toAgentRequest() payload directly, so apps no longer resolve context twice to reach the bridge. Duck-typed, so this package keeps zero dependency on core. payload.metadata added to carry the request metadata.
  • isAskableBridgeEnvelope() validates consent (the field receivers gate on) and rejects arrays; its local isRecord had diverged from the one in @askable-ui/context.
  • Version compatibility. Envelopes from the same major version are accepted, so a minor protocol bump no longer breaks every deployed extension and webhook at once. Exposed as isAskableBridgeVersionSupported().
  • Abort signals are honored by every transport and by dispose(); failed HTTP requests report the response body, truncated at maxErrorBodyChars.

Docs

  • Provider examples use toContextPacketAsync() — the only form that resolves sources registered with ctx.registerSource() into surrounding.sources. Every example used the sync method, so bridge consumers were silently sending thinner packets than the same app exposes over MCP.
  • Root README gains a Bridge section; the Bridge-vs-MCP table is promoted out of the docs site. The package previously had two table rows and no prose.
  • Guide and API pages cover target origins, privacy gates, ack semantics, and agent-request interop.

Also here

chore(examples): consume Askable 0.17.1 — examples were pinned ^0.17.0 against published 0.17.1, failing check-example-askable-versions on main and turning static / quality red on every open PR. npm and pnpm lockfiles refreshed; the pnpm lockfile had drifted to 0.16.0.

Verification

  • npm run build --workspaces → 0
  • npm test → 0, 1216 passed across 13 workspaces
  • vitepress build → 0
  • check-example-askable-versions, check-publish-package-coverage, check-mcp-server-manifest → all OK
  • Bridge package: 36 tests, up from 5. New coverage for the default target origin, all three HeadersInit forms, duplicate ids, partial transport failure, privacy gates, abort/dispose, agent-request interop, and guard rejection cases — each must-fix finding has a test that fails without its fix.

Rebased onto main after #405 and #398 merged, then reinstalled and re-verified against vitest 4.1.10 and @types/node 26.

vamgan added 2 commits August 8, 2026 19:48
Addresses a full review of the provider-neutral bridge.

Security and correctness:
- postMessage no longer defaults to targetOrigin '*'. It falls back to the
  sending page's origin and throws when none can be determined, matching the
  invariant already enforced in @askable-ui/mcp's page bridge. Broadcasting is
  now an explicit allowAnyOrigin opt-in.
- HTTP headers are normalized through Headers before merging. Object spread
  silently dropped Headers instances and entry arrays, so an Authorization
  header supplied in either form never reached the request.
- Duplicate transport ids throw instead of silently replacing the earlier
  transport, and unregister handles delete by identity rather than by id.
- Sends use allSettled: a throwing transport becomes an ok:false ack instead of
  discarding the acks of transports that succeeded.

Privacy:
- requireRedacted, allowedConsent, and maxEnvelopeBytes gate every send before
  any transport runs. requireRedacted mirrors @askable-ui/mcp.

API:
- sendAgentRequest() accepts a ctx.toAgentRequest() payload directly, so apps
  no longer resolve context twice to reach the bridge. payload.metadata added.
- isAskableBridgeEnvelope validates consent and rejects arrays; envelopes from
  the same major version are accepted so a minor bump does not break every
  deployed receiver. Exposed as isAskableBridgeVersionSupported().
- Abort signals are honored by every transport and by dispose(); failed HTTP
  requests report the response body.

Docs:
- Provider examples use toContextPacketAsync(), the only form that resolves
  registered context sources into surrounding.sources.
- README gains a Bridge section; guide and API pages cover origins, privacy
  gates, ack semantics, and agent-request interop.
Examples were still pinned to ^0.17.0 while packages shipped 0.17.1, so
check-example-askable-versions failed on main and turned static/quality red on
every open pull request. Refreshes the npm and pnpm lockfiles alongside the
manifests; the pnpm lockfile had drifted two releases behind at 0.16.0.
@vercel

vercel Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
askable Building Building Preview Aug 8, 2026 11:56pm

@pkg-pr-new

pkg-pr-new Bot commented Aug 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

@askable-ui/angular

npm i https://pkg.pr.new/@askable-ui/angular@409

@askable-ui/bridge

npm i https://pkg.pr.new/@askable-ui/bridge@409

@askable-ui/context

npm i https://pkg.pr.new/@askable-ui/context@409

@askable-ui/core

npm i https://pkg.pr.new/@askable-ui/core@409

@askable-ui/create-app

npm i https://pkg.pr.new/@askable-ui/create-app@409

@askable-ui/mcp

npm i https://pkg.pr.new/@askable-ui/mcp@409

@askable-ui/qwik

npm i https://pkg.pr.new/@askable-ui/qwik@409

@askable-ui/react

npm i https://pkg.pr.new/@askable-ui/react@409

@askable-ui/react-native

npm i https://pkg.pr.new/@askable-ui/react-native@409

@askable-ui/solid

npm i https://pkg.pr.new/@askable-ui/solid@409

@askable-ui/svelte

npm i https://pkg.pr.new/@askable-ui/svelte@409

@askable-ui/vue

npm i https://pkg.pr.new/@askable-ui/vue@409

@askable-ui/web-component

npm i https://pkg.pr.new/@askable-ui/web-component@409

commit: 7056db2

@vamgan
vamgan merged commit 58fb9a7 into main Aug 9, 2026
9 checks passed
@vamgan
vamgan deleted the fix/bridge-review-findings branch August 9, 2026 04:10

This branch was successfully deployed

1 active deployment
Preview — 7056db28 Deployed Aug 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant