Validate the destination of the connection you are about to make.
A safe first destination says nothing about the next one.
A server-side HTTP request spends the application’s network access on behalf of someone else. That makes the destination a security decision. In Twenty, the affected secure HTTP client checked an initial URL before handing the request to Axios. The upstream advisory records a gap when that request followed a redirect: the next destination did not receive the same validation.
The useful review question is about the lifetime of that decision. A URL check can be correct for the input it sees and still be insufficient for the operation it authorizes. When a response supplies a new destination, the application is about to make another network request. The earlier approval needs a defined scope; it cannot establish trust in a server chosen later.
The security check ended before redirect handling did.
The pre-fix adapter assembled the URL, rejected unsupported protocols, resolved the hostname and rejected a private address. It then created a custom lookup function that returned the validated address. This protected the initial lookup from being replaced by a different DNS answer. Finally, it delegated execution to the standard HTTP adapter.
The source also selected only one custom agent, according to the starting protocol. An HTTPS request therefore configured the HTTPS agent alone. A later redirect to HTTP could use a different transport path. The advisory describes both missing redirect validation and this cross-protocol gap. These are related coverage failures: the component that approves the first request does not control every subsequent connection.
- Check the initial URL
Resolve and approve the first destination.
- Follow a redirect
A response supplies a different destination.
- Open the next connection
The initial check is not repeated.
- Receive a destination
Initial URL or redirect target.
- Validate the connection
HTTP and HTTPS agents enforce the policy.
- Allow or stop
A private destination fails validation.
Conceptual control flow reconstructed from the upstream source. It is not a captured request or a TraceMint execution trace.
Move enforcement to the connection boundary.
The upstream patch replaces the adapter with dedicated agents. Each agent checks a literal host address in createConnection and attaches a handler for the socket’s DNS lookup event. A private resolved address causes the socket to be destroyed; an address that cannot be validated also closes the socket. Both HTTP and HTTPS receive this behavior.
The service installs both agents when outbound safe mode is enabled. The checked revision also sets a maximum of five redirects, while respecting a lower caller-supplied limit. That bound limits the length of a chain. The security boundary comes from destination validation on the connection path: one redirect can still cross a boundary even when a redirect budget is small.
This is a change in where the invariant is enforced. Callers no longer need to predict which transport a redirect will use. The agent that creates the new connection applies the destination policy at the point where that connection becomes concrete.
const { address: resolvedIp, family } = await dnsLookup(url.hostname);httpAgent: createSsrfSafeAgent('http'),httpsAgent: createSsrfSafeAgent('https'),validateHost(options.host ?? undefined);return attachLookupValidation(super.createConnection(options, callback));Selected upstream lines; indentation normalized. Links include surrounding code at immutable revisions.
Network reachability determines the practical exposure.
Twenty rates the issue Moderate, with a CVSS 3.1 score of 5.0. The advisory describes access to internal service responses and identifies versions through 1.17 as affected, with 1.18 patched. Its scope is conditional: the attacker must be able to influence an outbound URL, and the application server must be able to reach the destination.
- An authenticated actor
- The published attack requires a logged-in user with control over an outbound request URL.
- A redirected request
- A controllable initial endpoint supplies the next destination.
- A reachable internal service
- Network placement and the target service’s own protections affect what can be exposed.
Test the transition, as well as the destination.
The patch includes unit tests for agent creation, literal private addresses, DNS lookup outcomes and redirect limits. Those tests document the intended controls. Reading a test is evidence of coverage in source; it does not establish that a particular deployed environment passed an integration test.
A regression review should also exercise the boundary through an actual HTTP client in an isolated fixture. Use endpoints owned by the test and a controlled destination classified as private. Observe connection attempts as well as the final response: a request that returns an error may still have contacted a forbidden service. The scenarios below are suggested review checks, not reported TraceMint test results.
| Scenario | Expected boundary |
|---|---|
| Allowed URL → allowed URL | Legitimate redirect behavior continues within the configured limit. |
| Allowed URL → private destination | The redirected connection fails destination validation. |
| HTTPS → HTTP redirect | The destination policy remains active after the protocol change. |
| Hostname → private DNS answer | The resolved address is rejected on the connection path. |
Match a check’s scope to the operation it protects.
For reviewers, the high-value seam is the handoff between an application wrapper and a library that performs more work internally. Redirects, retries and transport changes deserve explicit questions: which inputs can change, which component creates the next connection, and which security policy still applies? A check is useful only for the set of operations it actually governs.
For maintainers, preserve a benign success case beside each rejected case. A fix that prevents all requests would satisfy a narrow negative test while breaking the product. Review safe-mode configuration and the caller’s use of the protected client alongside the patch; the implementation shown here is not a claim that every outbound path in every deployment is covered.
The public advisory credits the TraceMint account as Finder. The patch is upstream remediation. This study analyzes the public record and immutable source revisions; it does not establish a private discovery sequence, a product execution method, or observed compromise of a customer environment.
Follow the evidence.
Public upstream records and immutable code revisions, reviewed 2026-09-19.
- Twenty security advisory
Impact, prerequisites, versions, severity and finder credit.
- Adapter before the fix
Immutable parent revision · e4aad775 · URL validation, DNS pinning and adapter handoff.
- Upstream corrective commit
610c0ebc · the complete change and its parent revision.
- Connection-level agents
Literal-host checks and validation of socket lookup results.
- Client configuration after the fix
Both agents in safe mode; five-redirect cap in the committed source.
- Upstream agent tests
Unit cases for address validation and HTTP/HTTPS agent behavior.
- Upstream client tests
Safe-mode configuration and redirect-budget expectations.
Brief code excerpts from twentyhq/twenty. Upstream license: GNU AGPL v3. All remediation described here is upstream work.
Read the original record
This case study analyzes an already-public advisory and upstream source. For the complete impact, remediation and credit record, see the Twenty security advisory.
Editorial publication is separate from disclosure. No private discovery timeline or method has been inferred.