Skip to content

Commit 3d15589

Browse files
authored
Enhance IP checks and hostname parsing (#84)
Added checks for IPv4-mapped and IPv4-compatible addresses in is_ip_internal function. Updated hostname parsing in is_url_safe_debug to remove brackets.
1 parent f40edfc commit 3d15589

1 file changed

Lines changed: 29 additions & 7 deletions

File tree

src/helpers.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import * as ipaddr from "ipaddr.js";
44
import CIDR from "ip-cidr";
5+
import * as net from "node:net"; // Added for net.isIPv6 check
56
const got = require("got").default;
67
const DEBUG = false;
78
const DSSRF_MAKE_REQUEST = process.env.DSSRF_MAKE_REQUEST;
@@ -232,11 +233,32 @@ export function is_ip_internal(ip: string): boolean {
232233

233234
if (parsed.kind() === "ipv6") {
234235
const range = parsed.range();
235-
return (
236-
range === "loopback" ||
237-
range === "linkLocal" ||
238-
range === "uniqueLocal"
239-
);
236+
if (range !== "unicast") {
237+
// Check for IPv4-mapped or IPv4-compatible addresses
238+
if (range === "ipv4Mapped" || range === "rfc6145") {
239+
try {
240+
// @ts-ignore
241+
const ipv4 = parsed.toIPv4Address();
242+
return is_ip_internal(ipv4.toString());
243+
} catch {
244+
return true;
245+
}
246+
}
247+
return true;
248+
}
249+
250+
// Additional manual blocks for IPv6
251+
const extraBadRanges = [
252+
ipaddr.parseCIDR("64:ff9b:1::/48"),
253+
ipaddr.parseCIDR("5f00::/8"),
254+
ipaddr.parseCIDR("3fff::/20"),
255+
ipaddr.parseCIDR("fec0::/10"),
256+
];
257+
258+
for (const [range, bits] of extraBadRanges) {
259+
// @ts-ignore
260+
if (parsed.match(range, bits)) return true;
261+
}
240262
}
241263

242264
return false;
@@ -491,7 +513,7 @@ export async function is_url_safe(url: string): Promise<boolean> {
491513
if (!is_proto_safe(schema)) return false;
492514

493515
const parsed = new URL(u);
494-
const hostname = parsed.hostname;
516+
const hostname = parsed.hostname.replace(/^\[|\]$/g, "");
495517

496518
// IPv4 validation
497519
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) {
@@ -521,6 +543,6 @@ export async function is_url_safe(url: string): Promise<boolean> {
521543

522544

523545
/// FIXME(): The debug version do not match is_url_safe, We'wll fix it later but for now keep it as is.
524-
export async function is_url_safe_debug(url: string): Promise<boolean> { try { console.log("STEP 1 input:", url); let u = normalize_unicode(url); console.log("STEP 2 unicode:", u); u = replace_backslash_with_slash_in_string(u); console.log("STEP 3 slashes:", u); u = replace_two_slashes_url_to_normal_url(u); console.log("STEP 4 normalize slashes:", u); u = remove_at_symbol_in_string(u); console.log("STEP 5 remove @:", u); const schema = normalize_schema(u); if (!is_proto_safe(schema)) return false; console.log("STEP 6 schema:", schema); if (!is_proto_safe(u)) { console.log("STEP 7 proto unsafe"); return false; } console.log("STEP 7 proto safe"); const parsed = new URL(u); const hostname = parsed.hostname; console.log("STEP 8 hostname:", hostname); if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) { try { normalize_ipv4(hostname); } catch { console.log("STEP 9 ipv4 invalid"); return false; } } if (is_ipv6(hostname)) { if (is_ip_internal(hostname)) { console.log("STEP 10 ipv6 internal"); return false; } } const isInternal = await is_hostname_resolve_to_internal_ip(hostname); console.log("STEP 11 internal?", isInternal); if (isInternal) { return false; } const redirectSafe = await is_redirect_safe(u); console.log("STEP 12 redirect safe?", redirectSafe); if (!redirectSafe) { return false; } console.log("STEP 13 final: true"); return true; } catch (e) { console.log("ERROR:", e); return false; } }
546+
export async function is_url_safe_debug(url: string): Promise<boolean> { try { console.log("STEP 1 input:", url); let u = normalize_unicode(url); console.log("STEP 2 unicode:", u); u = replace_backslash_with_slash_in_string(u); console.log("STEP 3 slashes:", u); u = replace_two_slashes_url_to_normal_url(u); console.log("STEP 4 normalize slashes:", u); u = remove_at_symbol_in_string(u); console.log("STEP 5 remove @:", u); const schema = normalize_schema(u); if (!is_proto_safe(schema)) return false; console.log("STEP 6 schema:", schema); if (!is_proto_safe(u)) { console.log("STEP 7 proto unsafe"); return false; } console.log("STEP 7 proto safe"); const parsed = new URL(u); const hostname = parsed.hostname.replace(/^\[|\]$/g, ""); console.log("STEP 8 hostname:", hostname); if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) { try { normalize_ipv4(hostname); } catch { console.log("STEP 9 ipv4 invalid"); return false; } } if (is_ipv6(hostname)) { if (is_ip_internal(hostname)) { console.log("STEP 10 ipv6 internal"); return false; } } const isInternal = await is_hostname_resolve_to_internal_ip(hostname); console.log("STEP 11 internal?", isInternal); if (isInternal) { return false; } const redirectSafe = await is_redirect_safe(u); console.log("STEP 12 redirect safe?", redirectSafe); if (!redirectSafe) { return false; } console.log("STEP 13 final: true"); return true; } catch (e) { console.log("ERROR:", e); return false; } }
525547

526548

0 commit comments

Comments
 (0)