mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
fix(ssrf): unify ipv6 special-use blocking
This commit is contained in:
@@ -18,6 +18,7 @@ describe("fetchWithSsrFGuard hardening", () => {
|
||||
it("blocks private and legacy loopback literals before fetch", async () => {
|
||||
const blockedUrls = [
|
||||
"http://127.0.0.1:8080/internal",
|
||||
"http://[ff02::1]/internal",
|
||||
"http://0177.0.0.1:8080/internal",
|
||||
"http://0x7f000001/internal",
|
||||
];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { blockedIpv6MulticastLiterals } from "../../shared/net/ip-test-fixtures.js";
|
||||
import { normalizeFingerprint } from "../tls/fingerprint.js";
|
||||
import { isBlockedHostnameOrIp, isPrivateIpAddress } from "./ssrf.js";
|
||||
|
||||
@@ -38,9 +39,7 @@ const privateIpCases = [
|
||||
"fe80::1%lo0",
|
||||
"fd00::1",
|
||||
"fec0::1",
|
||||
"ff02::1",
|
||||
"ff05::1:3",
|
||||
"[ff02::1]",
|
||||
...blockedIpv6MulticastLiterals,
|
||||
"2001:db8:1234::5efe:127.0.0.1",
|
||||
"2001:db8:1234:1:200:5efe:7f00:1",
|
||||
];
|
||||
|
||||
@@ -4,11 +4,11 @@ import { Agent, type Dispatcher } from "undici";
|
||||
import {
|
||||
extractEmbeddedIpv4FromIpv6,
|
||||
isBlockedSpecialUseIpv4Address,
|
||||
isBlockedSpecialUseIpv6Address,
|
||||
isCanonicalDottedDecimalIPv4,
|
||||
type Ipv4SpecialUseBlockOptions,
|
||||
isIpv4Address,
|
||||
isLegacyIpv4Literal,
|
||||
isPrivateOrLoopbackIpAddress,
|
||||
parseCanonicalIpAddress,
|
||||
parseLooseIpAddress,
|
||||
} from "../../shared/net/ip.js";
|
||||
@@ -120,7 +120,7 @@ export function isPrivateIpAddress(address: string, policy?: SsrFPolicy): boolea
|
||||
if (isIpv4Address(strictIp)) {
|
||||
return isBlockedSpecialUseIpv4Address(strictIp, blockOptions);
|
||||
}
|
||||
if (isPrivateOrLoopbackIpAddress(strictIp.toString())) {
|
||||
if (isBlockedSpecialUseIpv6Address(strictIp)) {
|
||||
return true;
|
||||
}
|
||||
const embeddedIpv4 = extractEmbeddedIpv4FromIpv6(strictIp);
|
||||
|
||||
1
src/shared/net/ip-test-fixtures.ts
Normal file
1
src/shared/net/ip-test-fixtures.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const blockedIpv6MulticastLiterals = ["ff02::1", "ff05::1:3", "[ff02::1]"] as const;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { blockedIpv6MulticastLiterals } from "./ip-test-fixtures.js";
|
||||
import {
|
||||
extractEmbeddedIpv4FromIpv6,
|
||||
isCanonicalDottedDecimalIPv4,
|
||||
@@ -47,8 +48,9 @@ describe("shared ip helpers", () => {
|
||||
|
||||
it("treats blocked IPv6 classes as private/internal", () => {
|
||||
expect(isPrivateOrLoopbackIpAddress("fec0::1")).toBe(true);
|
||||
expect(isPrivateOrLoopbackIpAddress("ff02::1")).toBe(true);
|
||||
expect(isPrivateOrLoopbackIpAddress("[ff05::1:3]")).toBe(true);
|
||||
for (const literal of blockedIpv6MulticastLiterals) {
|
||||
expect(isPrivateOrLoopbackIpAddress(literal)).toBe(true);
|
||||
}
|
||||
expect(isPrivateOrLoopbackIpAddress("2001:4860:4860::8888")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ const PRIVATE_OR_LOOPBACK_IPV4_RANGES = new Set<Ipv4Range>([
|
||||
"carrierGradeNat",
|
||||
]);
|
||||
|
||||
const PRIVATE_OR_LOOPBACK_IPV6_RANGES = new Set<Ipv6Range>([
|
||||
const BLOCKED_IPV6_SPECIAL_USE_RANGES = new Set<Ipv6Range>([
|
||||
"unspecified",
|
||||
"loopback",
|
||||
"linkLocal",
|
||||
@@ -228,11 +228,15 @@ export function isPrivateOrLoopbackIpAddress(raw: string | undefined): boolean {
|
||||
if (isIpv4Address(normalized)) {
|
||||
return PRIVATE_OR_LOOPBACK_IPV4_RANGES.has(normalized.range());
|
||||
}
|
||||
if (PRIVATE_OR_LOOPBACK_IPV6_RANGES.has(normalized.range())) {
|
||||
return isBlockedSpecialUseIpv6Address(normalized);
|
||||
}
|
||||
|
||||
export function isBlockedSpecialUseIpv6Address(address: ipaddr.IPv6): boolean {
|
||||
if (BLOCKED_IPV6_SPECIAL_USE_RANGES.has(address.range())) {
|
||||
return true;
|
||||
}
|
||||
// ipaddr.js does not classify deprecated site-local fec0::/10 as private.
|
||||
return (normalized.parts[0] & 0xffc0) === 0xfec0;
|
||||
return (address.parts[0] & 0xffc0) === 0xfec0;
|
||||
}
|
||||
|
||||
export function isRfc1918Ipv4Address(raw: string | undefined): boolean {
|
||||
|
||||
Reference in New Issue
Block a user