Last updated: March 2026
The Bridge Framework spans multiple deployment contexts. We assume four primary actors:
- The External Client (Untrusted): End-users sending HTTP/GraphQL requests to the running Bridge server.
- The Bridge Developer (Semi-Trusted): Internal engineers writing
.bridgefiles and configuring the Node.js deployment. - Downstream APIs (Semi-Trusted): The microservices or third-party APIs that Bridge calls via Tools.
- Playground Users (Untrusted): Anonymous visitors executing Bridge code and sharing playground sessions via the browser-based playground.
(Note: If your platform allows users to dynamically upload .bridge files via a SaaS interface, the "Bridge Developer" becomes "Untrusted", elevating all Internal Risks to Critical.)
Each package has a distinct trust profile. Packages with no executable runtime code (pure types, static docs) are excluded from the threat analysis.
| Package | Risk Tier | Input Source | Key Concern |
|---|---|---|---|
bridge-parser |
Medium | .bridge text (developer/SaaS) |
Parser exploits, ReDoS, identifier injection |
bridge-compiler |
High | Parsed AST | Dynamic code generation via new AsyncFunction() |
bridge-core |
High | AST + tool map + client arguments | Pull-based execution, resource exhaustion, prototype pollution |
bridge-stdlib |
High | Wired tool inputs (may originate from clients) | SSRF via httpCall, cache poisoning |
bridge-graphql |
Medium | GraphQL queries + schema | Context exposure, query depth |
bridge-syntax-highlight |
Low | Local .bridge files via IPC |
Parser CPU exhaustion in VS Code |
playground |
High | Untrusted user input in browser + share API | CSRF-like fetch abuse, share enumeration |
bridge-types |
None | — | Pure type definitions, no runtime code |
docs-site |
None | — | Static HTML/CSS/JS |
These are threats initiated by end-users interacting with the compiled GraphQL or REST endpoints.
- The Threat: An external client manipulates input variables to force the Bridge server to make HTTP requests to internal, non-public IP addresses (e.g., AWS metadata endpoint
169.254.169.254or internal admin panels). - Attack Vector: A
.bridgefile wires user input directly into a tool's URL:tool callApi { .baseUrl <- input.targetUrl }. ThehttpCallimplementation constructs URLs via plain string concatenation (new URL(baseUrl + path)), permitting path traversal (e.g.,path = "/../admin") with no allowlist or blocklist for private IP ranges. - Mitigation (Framework Level): The
std.httpCalltool should strictly validate or sanitizebaseUrlinputs if they are dynamically wired. Developers should never wire raw client input to URL paths. All headers from theheadersinput are forwarded verbatim to the upstream — if user-controlled input is wired toheaders, arbitrary HTTP headers can be injected. - Mitigation (Infrastructure Level): Run the Bridge container in an isolated network segment (egress filtering) that blocks access to internal metadata IP addresses.
- The Threat: User A receives User B's private data due to aggressive caching.
- Attack Vector: The built-in
createHttpCallcaches upstream responses. The cache key is constructed asmethod + " " + url + body(headers are not included in the cache key). If two users with differentAuthorizationheaders make the same GET request, User B will receive User A's cached response. - Current Status: The cache key does not incorporate
Authorizationor tenant-specific headers. Caching is only safe for public/unauthenticated endpoints. To disable caching, setcache = "0"on the tool. - Recommendation: Include sorted security-relevant headers (at minimum
Authorization) in the cache key, or clearly document that caching must be disabled for authenticated endpoints.
- The Threat: A client sends a heavily nested GraphQL query, forcing the engine to allocate massive arrays and deeply resolve thousands of tools.
- Attack Vector:
query { users { friends { friends { friends { id } } } } } - Mitigation: The
@stackables/bridge-graphqladapter relies on the underlying GraphQL server (Yoga/Apollo). Adopters must configure Query Depth Limiting and Query Complexity rules at the GraphQL adapter layer before requests ever reach the Bridge engine. The engine itself enforcesMAX_EXECUTION_DEPTH = 30for shadow-tree nesting as a secondary guard.
- The Threat: Internal system details (stack traces, connection strings, file paths) leak to external clients via GraphQL error responses.
- Attack Vector: Tools that throw errors containing internal details propagate through the engine and appear in the
errors[]array of the GraphQL response. When tracing is set to"full"mode, complete tool inputs and outputs are exposed via theextensions.tracesresponse field — including potentially sensitive upstream data. - Mitigation: Adopters should configure error masking in their GraphQL server (e.g., Yoga's
maskedErrors). Tracing should never be set to"full"in production environments exposed to untrusted clients.
These are threats derived from the .bridge files themselves. Even if written by trusted internal developers, malicious or malformed schemas can exploit the Node.js runtime.
- The Threat: A malformed
.bridgefile or programmatically constructed AST injects raw JavaScript into the@stackables/bridge-compilercode generator. - Attack Vector: The AOT compiler (
bridge-compiler) generates a JavaScript function body as a string and evaluates it vianew AsyncFunction(). A developer names a tool or field with malicious string terminators:field "\"); process.exit(1); //". - Mitigation (multi-layered):
- Identifier validation: The Chevrotain lexer restricts identifiers to
/[a-zA-Z_][\w-]*/— only alphanumeric characters, underscores, and hyphens. - Synthetic variable names: The codegen generates internal variable names (
_t1,_d1,_a1) — user-provided identifiers are never used directly as JS variable names. - JSON.stringify for all dynamic values: Tool names use
tools[${JSON.stringify(toolName)}], property paths use bracket notation withJSON.stringify, and object keys useJSON.stringify(key). - Constant coercion:
emitCoerced()produces only primitives orJSON.stringify-escaped values — no raw string interpolation. - Reserved keyword guard:
assertNotReserved()blocksbridge,with,as,from,throw,panic, etc. as identifiers.
- Identifier validation: The Chevrotain lexer restricts identifiers to
- Residual Risk: If consumers construct
BridgeDocumentobjects programmatically (bypassing the parser), they bypass identifier validation. TheJSON.stringify-based codegen provides defense-in-depth but edge cases inemitCoerced()for non-string primitive values should be reviewed. - CSP Note:
new AsyncFunction()is equivalent toeval()from a Content Security Policy perspective. Environments with strict CSP (script-src 'self') will block AOT execution.
- The Threat: The Bridge language constructs deep objects based on paths defined in the schema.
- Attack Vector: A wire is defined as
o.__proto__.isAdmin <- trueoro.constructor.prototype.isAdmin <- true. Both the interpreter (setNested) and the compiler (nested object literals) will attempt to construct this path. - Mitigation: The
UNSAFE_KEYSblocklist (__proto__,constructor,prototype) is enforced at three points:setNested()intree-utils.ts— blocks unsafe assignment keys during tool input assembly.applyPath()inExecutionTree.ts— blocks unsafe property traversal on source refs.lookupToolFn()intoolLookup.ts— blocks unsafe keys in dotted tool name resolution.
- Test Coverage:
prototype-pollution.test.tsexplicitly validates all three enforcement points.
- The Threat: The engine enters an infinite loop trying to resolve tools.
- Attack Vector: Tool A depends on Tool B, which depends on Tool A.
- Mitigation:
- Compiler: Kahn's Algorithm in
@stackables/bridge-compilertopological sort mathematically guarantees that circular dependencies throw a compile-time error. - Interpreter: The
pullSinglerecursive loop maintains apullChainSet. If a tool key is already in the set during traversal, it throws aBridgePanicError, preventing stack overflows.
- Compiler: Kahn's Algorithm in
- The Threat: A bridge file with many independent tool calls or deeply nested structures exhausts server memory or CPU.
- Attack Vector: A
.bridgefile declares hundreds of independent tools, or deep array-mapping creates unbounded shadow trees. - Mitigation (implemented):
MAX_EXECUTION_DEPTH = 30— limits shadow-tree nesting depth.toolTimeoutMs = 15_000—raceTimeout()wraps every tool call with a deadline, throwingBridgeTimeoutErroron expiry.AbortSignalpropagation — external abort signals are checked before tool calls, during wire resolution, and during shadow array creation.BridgeAbortErrorbypasses all error boundaries.constantCachehard cap — clears at 10,000 entries to prevent unbounded growth.boundedClone()— truncates arrays (100 items), strings (1,024 chars), and depth (5 levels) in trace data.
- Gaps: There is no limit on the total number of tool calls per request, no per-request memory budget, and no rate limiting on tool invocations. A bridge with many independent tools will execute all of them without throttling.
- The Threat:
JSON.parse()is called on developer-provided values from the AST inonErrorwire handling andconstblock definitions. - Attack Vector: Programmatically constructed ASTs (bypassing the parser) could supply arbitrarily large or malformed JSON, causing CPU/memory exhaustion during parsing.
- Mitigation: In normal usage, these values originate from the parser which validates string literals. The impact is limited to data within the execution context (no code execution). Adopters accepting user-supplied ASTs should validate
onErrorandconstvalues before passing them to the engine.
The browser-based playground (packages/playground) has a unique threat profile because it executes untrusted Bridge code client-side and provides a public share API.
- The Threat: An attacker crafts a playground share that, when opened by a victim, makes authenticated HTTP requests to internal APIs using the victim's browser cookies.
- Attack Vector: A crafted
.bridgefile usinghttpCallwith abaseUrlpointing to a victim's internal service. The playground usesglobalThis.fetchfor HTTP calls — the browser will attach cookies for the target domain. The attacker shares the playground link; the victim opens it, and the bridge auto-executes. - Mitigation: The browser's CORS policy prevents reading responses from cross-origin requests (the attacker cannot exfiltrate data). However, side-effect requests (POST/PUT/DELETE) may still succeed if the target API does not enforce CSRF tokens. Adopters of internal APIs should implement CSRF protection and
SameSitecookie attributes.
- The Threat: Anyone who knows or guesses a 12-character share ID can read the share data. There is no authentication or access control.
- Attack Vector: Share IDs are 12-char alphanumeric strings derived from UUIDs. While brute-force enumeration is impractical (36¹² ≈ 4.7 × 10¹⁸ possibilities), share URLs may be leaked via browser history, referrer headers, or shared chat logs.
- Mitigation: Shares expire after 90 days. Share IDs have sufficient entropy to resist brute-force. Adopters should be aware that share URLs are effectively "anyone with the link" access — do not share playground links containing sensitive data (API keys, credentials, internal URLs).
- The Threat: An attacker floods the share API to exhaust Cloudflare KV storage.
- Attack Vector: Repeated
POST /api/sharerequests with 128 KiB payloads. - Mitigation: Payload size is capped at 128 KiB. Shares have 90-day TTL (auto-expiry). Cloudflare KV has built-in storage limits. There is no rate limiting — Cloudflare Workers rate limiting or a WAF rule should be applied in production.
The VS Code extension (packages/bridge-syntax-highlight) provides syntax highlighting, diagnostics, hover info, and autocomplete for .bridge files via LSP.
- Transport: IPC (inter-process communication) between the extension host and language server — no network exposure.
- Document scope: Limited to
{ scheme: "file", language: "bridge" }— only local.bridgefiles. - No code execution: The language server only parses and validates — it never executes bridge files or tools.
- Risk: A maliciously crafted
.bridgefile in a workspace could trigger high CPU usage during parsing (Chevrotain's lexer uses simple regexes without backtracking, making ReDoS unlikely). The language server runs with VS Code's privilege level.
- The Threat: Sensitive downstream data (PII, passwords, API keys) is logged to Datadog/NewRelic via OpenTelemetry spans.
- Attack Vector: The
@stackables/bridge-coreengine automatically traces tool inputs and outputs. If an HTTP tool returns a payload containing raw credit card data, the span attributes might log it in plain text. - Mitigation (implemented):
boundedClone()truncates traced data (arrays to 100 items, strings to 1,024 chars, depth to 5 levels) before storing in trace spans, reducing the blast radius. - Mitigation (not yet implemented): A
redacthook orsensitive: truefield flag that prevents specific fields from being serialized into telemetry spans. Adopters should configure OpenTelemetry exporters to filter spans in the meantime.
- The Threat: An upstream API fails synchronously, crashing the Node.js process.
- Attack Vector: A custom tool written by an adopter throws a synchronous
new Error()instead of returning a rejected Promise. - Mitigation: The execution engine wraps all tool invocations in exception handlers, coercing errors into Bridge-managed failure states (
BridgePanicErrorandBridgeAbortErrorare treated as fatal viaisFatalError(); all other errors enter the fallback/catch chain). The server remains available.
- The Threat: Sensitive data in the GraphQL context (auth tokens, database connections, session objects) is exposed to all bridge files.
- Attack Vector: When
bridgeTransform()is used without acontextMapper, the full GraphQL context is passed to every bridge execution. Any bridge file can read any context property. - Mitigation: Configure
options.contextMapperto restrict which context fields are available to bridges. This is especially important in multi-tenant deployments where bridge files may be authored by different teams.
- The Threat: Mutable state returned by tools is shared across shadow trees within the same request.
- Attack Vector:
resolveToolDep()delegates to the root tree's cache. If a tool returns a mutable object, shadow trees (e.g., array mapping iterations) may observe each other's mutations, causing nondeterministic behavior. - Mitigation: Tool functions should return immutable data or fresh objects. The framework does not currently enforce immutability on tool return values.
| Package | External Dependency | Risk |
|---|---|---|
bridge-parser |
chevrotain@^11 |
Low — mature, deterministic parser framework with no eval |
bridge-stdlib |
lru-cache@^11 |
Low — widely used, actively maintained in-memory cache |
bridge-core |
@opentelemetry/api@^1.9 |
Low — CNCF project, passive by default (no-op without SDK) |
bridge-graphql |
graphql@^16, @graphql-tools/utils@^11 (peer) |
Low — reference implementation |
playground |
React 19, CodeMirror 6, Radix UI, Cloudflare Workers SDK | Medium — large dependency surface area, partially mitigated by browser sandbox |
bridge-syntax-highlight |
vscode-languageclient, vscode-languageserver |
Low — standard LSP libraries, IPC transport |
- Never wire raw client input to
httpCall.baseUrlorhttpCall.headers— use static baseUrl values in bridge files. - Disable caching for authenticated endpoints — set
cache = "0"on anyhttpCallthat includesAuthorizationheaders until the cache key incorporates security-relevant headers. - Configure
contextMapperinbridgeTransform()to restrict which GraphQL context fields are available to bridges. - Enable query depth/complexity limiting in your GraphQL server (Yoga/Apollo) before requests reach Bridge.
- Mask errors in production — configure your GraphQL server to strip internal error details from client responses.
- Never use
"full"tracing in production — trace data may contain sensitive upstream payloads. - Apply egress filtering on the Bridge container to block access to internal metadata endpoints and private IP ranges.
- Review custom tools for synchronous throws, mutable return values, and credential leakage in error messages.
- Do not share playground links containing sensitive data — share URLs are effectively "anyone with the link" access.