Secure Outbound Transport Threat Model
This document outlines the security posture, control-status mapping, and STRIDE-style threat matrix for the planned built-in outbound transport in MultiModel Dev OS.
1. Gateway Security & Outbound Transport Control Posture
Every security control across contract, validator, gate, executor, transport, and runtime layers is classified below using canonical status terms:
| Control | Layer | Current Status | Description |
|---|---|---|---|
| URL Syntax Validation | Validator Layer | validator-enforced | Validates strict HTTPS scheme, parses valid URL format, and rejects invalid structures in validateProviderEndpoint(). |
| Trusted Endpoint Binding | Gate Layer | gate-enforced | Asserts that execution_request.endpoint matches the provider's registered endpoint metadata before execution. |
| Hostname Allowlisting | Gate / Executor Layer | not implemented | Hostname allowlisting per provider is planned; currently endpoints rely on URL syntax and endpoint binding. |
| DNS Resolution | Transport Layer | planned | Asynchronous resolution of hostnames to IPv4/IPv6 addresses via node:dns/promises. |
| IP Classification (DNS-Level) | Transport Layer | not implemented | Pre-connection classification and fail-closed rejection of private, loopback, link-local, multicast, CGNAT, and reserved IP ranges. |
| Literal Private IP Check | Validator Layer | validator-enforced | String-based parsing rejects literal IP strings (e.g. 127.0.0.1, 10.0.0.1, 169.254.169.254); does not protect against domain-name DNS resolution to private IPs. |
| Connection-Time Address Pinning | Transport Layer | planned | Connecting directly to pre-resolved and validated IP address using custom lookup in node:https or direct socket creation in node:net / node:tls. |
| TLS Certificate Verification | Transport Layer | not implemented | Built-in native transport with rejectUnauthorized: true and SNI preservation is planned for Sprint F2. Injected test transports handle TLS independently. |
| Redirect Handling (Contract) | Gate / Validator | validator-enforced | Validator enforces follow_redirects: false; gate denies policy with follow_redirects: true. |
| Redirect Rejection (Transport) | Transport Layer | planned | Built-in transport strictly rejects HTTP 3xx responses without following redirect locations. |
| Request/Response Byte Limits | Executor Layer | executor-enforced | Enforces max_request_bytes and max_response_bytes at payload normalization and streaming levels. |
| Credential Header Boundary | Transport Layer | not implemented | Constructing Authorization: Bearer <secret> header exclusively inside transport using ResolvedCredential.withSecret() is planned for Sprint F2. |
| Timeout & Cancellation | Executor / Runtime | executor-enforced | Lifecycle timeouts (request_timeout_ms, response_timeout_ms) and AbortSignal propagation managed by executor. |
| Observability Redaction | Observability Layer | runtime-enforced | Strips prompts, completions, credentials, and sensitive headers from event metrics and trace logs. |
| Injected Transport Trust | Runtime Layer | runtime-enforced | Governed execution in Sprint E1/E2 functions strictly via explicitly provided in-process transport implementations. |
2. STRIDE Threat Matrix
The expanded STRIDE threat matrix documents key security assets, threat vectors, entry points, preconditions, impacts, mitigations, residual risks, responsible sprints, and current implementation status:
| Asset | Attacker | Entry Point | Precondition | Impact | Prevention Strategy | Detection | Residual Risk | Responsible Sprint / Test | Current Status |
|---|---|---|---|---|---|---|---|---|---|
| Local Services | Malicious Config | Local Policy / Endpoint | Config specifies private IP literal (e.g. 127.0.0.1) | SSRF to local service | String-based private IP rejection in validateProviderEndpoint() | Validator error logs | Hostname resolving to loopback bypasses string check | Sprint A (tests/unit/execution-security.test.js) | validator-enforced |
| Local Services / Cloud Metadata | Untrusted DNS / Attacker | Hostname Resolution | Hostname resolves to private/loopback/link-local IP | SSRF to internal network / metadata API | Pre-connection IP range classification and fail-closed rejection | Transport audit logs & IP classifier errors | Compromised local DNS server prior to lookup | Sprint F1 (tests/unit/transport-destination-policy.test.js) | planned |
| DNS Resolver | Network MitM | Upstream DNS | Attacker tampers with DNS response | Traffic sent to malicious IP | Validate all returned DNS IPs; pin socket; require TLS cert hostname match | TLS handshake failure on host mismatch | Malicious server presents valid CA cert for target domain | Sprint F1 / F2 (tests/unit/transport-dns.test.js) | planned |
| IP Boundaries | Malicious DNS Server | Hostname Resolution | DNS returns public IP first, then private IP on connect | TOCTOU / DNS rebinding SSRF | Pin socket connection to pre-validated IP address via custom lookup | Pinned IP mismatch logging | Attacker controls public IP pointing to malicious server | Sprint F1 / F2 (tests/unit/transport-pinning.test.js) | planned |
| IP Classification | Malicious DNS Server | Hostname Resolution | DNS returns array containing both public and private IP addresses | SSRF if unvalidated IP chosen | Require 100% of returned addresses to pass public IP classification; fail-closed on any private answer | Resolution failure log | Resolver returns single public IP that rebinds | Sprint F1 (tests/unit/transport-destination-policy.test.js) | planned |
| Private Network | Upstream Server | HTTP 3xx Redirect | Server returns 302 Found pointing to internal IP | SSRF via redirect | Hardcode follow_redirects: false; reject 3xx responses in transport | Response status logging | Application logic interprets 3xx as success | Sprint F2 (tests/unit/transport-redirect.test.js) | planned |
| API Credentials | Network MitM | TLS Handshake | Server presents invalid or mismatched cert | Credential exfiltration | rejectUnauthorized: true; strict hostname SNI matching; no custom CAs | TLS verification errors | Compromised root CA on OS host | Sprint F2 (tests/unit/transport-tls.test.js) | planned |
| API Credentials | Cross-Provider Attacker | Execution Request | Reusing OpenAI key for Anthropic | Credential leakage across providers | Scope credential references strictly per provider ID | Credential resolver checks | Misconfigured provider ID in request | Sprint A / C (tests/unit/gateway-credential-resolution.test.js) | contract-defined |
| Transport Headers | Malicious Header Injector | Request Input | Header input contains \r\n | Header injection / request smuggling / token leakage | Strict CR/LF rejection; allowlist limited to Authorization, Content-Type, Accept, User-Agent | Header validation errors | Process memory inspection by local privileged user | Sprint A / F2 (tests/unit/transport-headers.test.js) | validator-enforced (contract); planned (transport) |
| HTTP Parser | Malicious Upstream | HTTP Response | Upstream sends malformed HTTP headers/chunks | Response splitting / smuggling | Strict standard-library HTTP parsing; validate response status and headers | Transport parse errors | Node standard-library HTTP parser bugs | Sprint F2 (tests/unit/transport-response.test.js) | planned |
| Gateway Memory | Upstream Server | Response Body | Server sends compressed / bomb payload | Out-of-memory crash | Enforce max_response_bytes; accept-encoding: identity; no transparent decompression | Byte budget exceeded errors | Memory pressure prior to limit breach | Sprint A / F2 (tests/unit/transport-limits.test.js) | executor-enforced (size); planned (transport encoding) |
| Gateway Memory | Upstream Server / User | Request / Response Payload | Massive JSON payload submitted or returned | RAM exhaustion | Bounded max_request_bytes (<= 52MB) and max_response_bytes (<= 52MB) | Payload limit errors | High memory usage near 52MB limit | Sprint A / B (tests/unit/gateway-execution.test.js) | executor-enforced |
| Gateway Sockets | Malicious Upstream | SSE Stream | Upstream stream stalls without closing | Socket / handle leak | Separate DNS, connect, TLS, header, idle, and total execution timeout timers | Timeout execution logs | Timers depend on Node event loop precision | Sprint E2 / F2 (tests/integration/gateway-governed-runtime-stream.test.js) | executor-enforced (executor timer); planned (transport phase timers) |
| Gateway Sockets | Malicious Upstream | Response Headers / Body | Upstream delays response headers or body chunks | Slowloris resource exhaustion | response_timeout_ms and idle response timeouts | Timeout execution logs | Slowloris sending 1 byte every 29s | Sprint E1 / F2 (tests/unit/gateway-execution.test.js) | executor-enforced |
| Gateway Sockets | Transport Connection | Abort Signal | Socket ignores AbortSignal trigger | Unkillable socket leak | Immediate socket.destroy() on abort or timeout | Cancellation log | Async socket teardown delay | Sprint E2 / F3 (tests/integration/gateway-governed-runtime-stream.test.js) | executor-enforced (executor signal); planned (transport socket destruction) |
| Network Endpoints | Environment / Proxy | Environment Variables | Ambient HTTP_PROXY or HTTPS_PROXY set | Request redirected through untrusted proxy | Explicitly ignore ambient proxy environment variables in built-in transport | Transport initialization check | System-level OS network proxy redirection | Sprint F2 (tests/unit/transport-proxy.test.js) | planned |
| Provider Execution | Malicious In-Process Code | Transport Parameter | Injected transport contains malicious socket code | Arbitrary execution / network access | Injected transport is trusted in-process code; validated via validateTransport() | Transport contract validation | Compromised node_modules or main thread code | Sprint D (tests/unit/gateway-execution.test.js) | executor-enforced |
| Credentials / Prompts | Observability Collector | Event / Trace Logs | Logger records request/response bodies or auth headers | Secret / prompt leakage in logs | Mandatory redaction pass; redact_prompts: true; authorization headers always stripped | Observability audit assertions | Memory inspection of trace buffer | Sprint E1 / E2 (tests/integration/gateway-governed-runtime-stream.test.js) | runtime-enforced |
| Connection Pool | Upstream Connection | HTTP Keep-Alive Pool | Reusing connection across different origins/providers | Credential exfiltration / cross-origin context leak | Disable connection reuse across distinct providers/origins in initial transport | Connection pool audit | Performance overhead of fresh TCP/TLS handshakes | Sprint F2 (tests/unit/transport-pooling.test.js) | planned |
| Trust Store | Local Attacker | Local Workspace File | Attacker modifies .ai/registries/trusted-keys.yaml | Acceptance of untrusted registry manifests | Cryptographic Ed25519 signature checks; file access restricted | Signature validation errors | Privileged local user overwriting trust store | Sprint A (tests/unit/registry-trust-store.test.js) | validator-enforced |
| Local Hostname | Local Attacker | OS /etc/hosts File | Attacker maps provider domain to 127.0.0.1 | Local MitM / credential theft | Pre-connection IP range classification detects 127.0.0.1 after resolution and fails closed | IP classification failure log | Attacker maps domain to a public IP under their control | Sprint F1 (tests/unit/transport-destination-policy.test.js) | planned |
| IP Boundaries | Malicious Endpoint | IPv6 Address Format | Endpoint uses IPv4-mapped IPv6 (e.g. ::ffff:127.0.0.1) | SSRF bypass of IPv4 checks | Normalize IPv4-mapped IPv6 addresses to IPv4 and classify against IPv4 rules | IP classification failure log | Obscure IPv6 translation mechanisms | Sprint F1 (tests/unit/transport-destination-policy.test.js) | planned |
| IP Boundaries | Malicious Endpoint | Alternate Numeric IP | Endpoint uses octal (0177.0.0.1), hex (0x7f000001), or dword (2130706433) | SSRF bypass of standard decimal parser | Reject non-canonical IPv4 representations; enforce standard 4-octet decimal dotted notation | URL / IP validation errors | Ambiguous IP parser implementations | Sprint F1 (tests/unit/transport-destination-policy.test.js) | planned |
| Hostname Parser | Malicious Endpoint | IDN / Unicode Hostname | Homograph attack / Punycode confusion | Hostname spoofing / SNI mismatch | Reject non-ASCII raw host input in first implementation; enforce strict ASCII labels | Endpoint validation errors | Future IDN expansion | Sprint F1 (tests/unit/transport-destination-policy.test.js) | planned |
3. Honest Security Boundaries & Non-Claims
- No Built-in Provider HTTP Transport: Current v4.3 Sprint E2 runtime executes governed requests only when an explicitly constructed transport object is injected. No built-in
node:httpstransport exists in production code today. - DNS-Level SSRF Protection: Current validator checks string syntax of endpoints (rejecting literal IP strings like
127.0.0.1), but does not perform DNS resolution or IP pinning prior to connection. DNS-level SSRF protection is planned for Sprint F1/F2. - Physical Secret Zeroization: In JavaScript and Node.js runtime environments, physical memory zeroization after HTTP/TLS execution cannot be guaranteed due to internal V8 string copies and garbage collection behavior. Ephemeral credential objects utilize
#secretprivate fields and.destroy()zeroing of internal references, but V8 engine memory strings may persist until garbage collected. - Ambient Environment Proxy Isolation: Built-in transport will explicitly ignore ambient process environment variables (
HTTP_PROXY,HTTPS_PROXY,ALL_PROXY,NO_PROXY) to prevent unintended request redirection.
