mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-27 04:41:16 +00:00
refactor(net-policy): use ipaddr helpers
This commit is contained in:
@@ -147,34 +147,6 @@ function isNumericIpv4LiteralPart(value: string): boolean {
|
||||
return /^[0-9]+$/.test(value) || /^0x[0-9a-f]+$/i.test(value);
|
||||
}
|
||||
|
||||
function parseIpv6WithEmbeddedIpv4(raw: string): ipaddr.IPv6 | undefined {
|
||||
if (!raw.includes(":") || !raw.includes(".")) {
|
||||
return undefined;
|
||||
}
|
||||
const match = /^(.*:)([^:%]+(?:\.[^:%]+){3})(%[0-9A-Za-z]+)?$/i.exec(raw);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
const [, prefix, embeddedIpv4, zoneSuffix = ""] = match;
|
||||
if (prefix === undefined || embeddedIpv4 === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (!ipaddr.IPv4.isValidFourPartDecimal(embeddedIpv4)) {
|
||||
return undefined;
|
||||
}
|
||||
const [a, b, c, d] = embeddedIpv4.split(".").map((part) => Number.parseInt(part, 10));
|
||||
if (a === undefined || b === undefined || c === undefined || d === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const high = ((a << 8) | b).toString(16);
|
||||
const low = ((c << 8) | d).toString(16);
|
||||
const normalizedIpv6 = `${prefix}${high}:${low}${zoneSuffix}`;
|
||||
if (!ipaddr.IPv6.isValid(normalizedIpv6)) {
|
||||
return undefined;
|
||||
}
|
||||
return ipaddr.IPv6.parse(normalizedIpv6);
|
||||
}
|
||||
|
||||
/** Type guard for parsed IPv4 addresses. */
|
||||
export function isIpv4Address(address: ParsedIpAddress): address is ipaddr.IPv4 {
|
||||
return address.kind() === "ipv4";
|
||||
@@ -209,16 +181,9 @@ export function parseCanonicalIpAddress(raw: string | undefined): ParsedIpAddres
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
if (ipaddr.IPv4.isValid(normalized)) {
|
||||
if (!ipaddr.IPv4.isValidFourPartDecimal(normalized)) {
|
||||
return undefined;
|
||||
}
|
||||
return ipaddr.IPv4.parse(normalized);
|
||||
}
|
||||
if (ipaddr.IPv6.isValid(normalized)) {
|
||||
return ipaddr.IPv6.parse(normalized);
|
||||
}
|
||||
return parseIpv6WithEmbeddedIpv4(normalized);
|
||||
const isCanonical =
|
||||
ipaddr.IPv4.isValidFourPartDecimal(normalized) || ipaddr.IPv6.isValid(normalized);
|
||||
return isCanonical ? ipaddr.parse(normalized) : undefined;
|
||||
}
|
||||
|
||||
/** Parses canonical IP literals plus legacy IPv4 forms needed for SSRF checks. */
|
||||
@@ -227,10 +192,7 @@ export function parseLooseIpAddress(raw: string | undefined): ParsedIpAddress |
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
if (ipaddr.isValid(normalized)) {
|
||||
return ipaddr.parse(normalized);
|
||||
}
|
||||
return parseIpv6WithEmbeddedIpv4(normalized);
|
||||
return ipaddr.isValid(normalized) ? ipaddr.parse(normalized) : undefined;
|
||||
}
|
||||
|
||||
/** Normalizes canonical IP literals and maps IPv4-mapped IPv6 addresses to IPv4 text. */
|
||||
@@ -245,15 +207,8 @@ export function normalizeIpAddress(raw: string | undefined): string | undefined
|
||||
|
||||
/** True only for canonical four-part dotted-decimal IPv4 literals. */
|
||||
export function isCanonicalDottedDecimalIPv4(raw: string | undefined): boolean {
|
||||
const trimmed = normalizeOptionalString(raw);
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
const normalized = stripIpv6Brackets(trimmed);
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
return ipaddr.IPv4.isValidFourPartDecimal(normalized);
|
||||
const normalized = normalizeIpParseInput(raw);
|
||||
return normalized !== undefined && ipaddr.IPv4.isValidFourPartDecimal(normalized);
|
||||
}
|
||||
|
||||
/** Detects legacy numeric IPv4 forms that canonical parsing deliberately rejects. */
|
||||
@@ -362,20 +317,12 @@ export function isBlockedSpecialUseIpv6Address(
|
||||
|
||||
/** True for canonical IPv4 literals in RFC 1918 private ranges. */
|
||||
export function isRfc1918Ipv4Address(raw: string | undefined): boolean {
|
||||
const parsed = parseCanonicalIpAddress(raw);
|
||||
if (!parsed || !isIpv4Address(parsed)) {
|
||||
return false;
|
||||
}
|
||||
return parsed.range() === "private";
|
||||
return parseCanonicalIpAddress(raw)?.range() === "private";
|
||||
}
|
||||
|
||||
/** True for canonical IPv4 literals in the carrier-grade NAT range. */
|
||||
export function isCarrierGradeNatIpv4Address(raw: string | undefined): boolean {
|
||||
const parsed = parseCanonicalIpAddress(raw);
|
||||
if (!parsed || !isIpv4Address(parsed)) {
|
||||
return false;
|
||||
}
|
||||
return parsed.range() === "carrierGradeNat";
|
||||
return parseCanonicalIpAddress(raw)?.range() === "carrierGradeNat";
|
||||
}
|
||||
|
||||
/** Applies the SSRF block policy for parsed IPv4 special-use ranges. */
|
||||
@@ -445,26 +392,13 @@ export function isIpInCidr(ip: string, cidr: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
let parsedCidr: [ParsedIpAddress, number];
|
||||
try {
|
||||
parsedCidr = ipaddr.parseCIDR(candidate);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [baseAddress, prefixLength] = parsedCidr;
|
||||
const comparableBase = normalizeIpv4MappedAddress(baseAddress);
|
||||
if (comparableIp.kind() !== comparableBase.kind()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (isIpv4Address(comparableIp) && isIpv4Address(comparableBase)) {
|
||||
return comparableIp.match([comparableBase, prefixLength]);
|
||||
}
|
||||
if (isIpv6Address(comparableIp) && isIpv6Address(comparableBase)) {
|
||||
return comparableIp.match([comparableBase, prefixLength]);
|
||||
}
|
||||
return false;
|
||||
const [baseAddress, prefixLength] = ipaddr.parseCIDR(candidate);
|
||||
const comparableBase = normalizeIpv4MappedAddress(baseAddress);
|
||||
return (
|
||||
comparableIp.kind() === comparableBase.kind() &&
|
||||
comparableIp.match([comparableBase, prefixLength])
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user