Skip to content

Commit e6df12e

Browse files
t3dotggclaude
andcommitted
fix(server): never advertise an IPv4-mapped host as a direct route
Co-Authored-By: Claude Opus 5.5 <[email protected]>
1 parent 33551a6 commit e6df12e

2 files changed

Lines changed: 8 additions & 1 deletion

File tree

‎apps/server/src/environment/DirectEndpoints.test.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ describe("resolveBoundEndpoints", () => {
148148
resolveBoundEndpoints({ host: "203.0.113.20", port: 3773, interfaces: INTERFACES }),
149149
).toEqual([]);
150150
});
151+
152+
it("never reports an IPv4-mapped host, which can name the client's own loopback", () => {
153+
expect(
154+
resolveBoundEndpoints({ host: "::ffff:127.0.0.1", port: 3773, interfaces: INTERFACES }),
155+
).toEqual([]);
156+
});
151157
});
152158

153159
const TAILSCALE_STATUS_JSON = JSON.stringify({

‎apps/server/src/environment/DirectEndpoints.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import * as Layer from "effect/Layer";
1111
import * as Option from "effect/Option";
1212
import { HttpClient } from "effect/http";
1313
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
14+
import * as NodeNet from "node:net";
1415
import * as NodeOS from "node:os";
1516

1617
import { isTailnetHost } from "@t3tools/shared/hostClassification";
@@ -45,7 +46,7 @@ export class DirectEndpoints extends Context.Service<
4546
* (`server.local`) can resolve to a different machine on each client's network.
4647
*/
4748
const isAdvertisableAddress = (address: string): boolean =>
48-
!address.startsWith("127.") && isPrivateNetworkAddress(address);
49+
NodeNet.isIPv4(address) && !address.startsWith("127.") && isPrivateNetworkAddress(address);
4950

5051
/**
5152
* Tailscale's own interface: `tailscale0` on Linux, "Tailscale" on Windows.

0 commit comments

Comments
 (0)