feat(tun): validate route-exclude-address as CIDR (#6440)

* feat(tun): validate route-exclude-address as CIDR

* refactor(network): replace ipaddr.js helpers with cidr-block and validator

* docs: Changelog
This commit is contained in:
Slinetrac
2026-03-07 09:18:35 +00:00
committed by GitHub
parent c429632d80
commit 0bbf9407d8
24 changed files with 204 additions and 169 deletions
@@ -21,7 +21,7 @@ import {
getWebdavStatus,
setWebdavStatus,
} from "@/services/webdav-status";
import { isValidUrl } from "@/utils/helper";
import { isValidUrl } from "@/utils/network";
interface BackupConfigViewerProps {
onBackupSuccess: () => Promise<void>;
+23 -3
View File
@@ -23,6 +23,7 @@ import { useClash } from "@/hooks/use-clash";
import { enhanceProfiles } from "@/services/cmds";
import { showNotice } from "@/services/notice-service";
import getSystem from "@/utils/get-system";
import { areValidIpCidrs } from "@/utils/network";
import { StackModeSwitch } from "./stack-mode-switch";
@@ -52,6 +53,17 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
mtu: 1500,
});
const routeExcludeAddressItems = splitRouteExcludeAddress(
values.routeExcludeAddress,
);
const routeExcludeAddressError =
values.autoRoute &&
routeExcludeAddressItems.length > 0 &&
!areValidIpCidrs(routeExcludeAddressItems);
const routeExcludeAddressHelperText = routeExcludeAddressError
? t("settings.modals.tun.messages.invalidRouteExcludeAddress")
: t("settings.modals.tun.messages.routeExcludeAddressHint");
useImperativeHandle(ref, () => ({
open: () => {
setOpen(true);
@@ -78,9 +90,15 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
const onSave = useLockFn(async () => {
try {
const routeExcludeAddress = splitRouteExcludeAddress(
values.routeExcludeAddress,
);
const routeExcludeAddress = routeExcludeAddressItems;
if (routeExcludeAddressError) {
showNotice.error(
"settings.modals.tun.messages.invalidRouteExcludeAddress",
);
return;
}
const tun: IConfigData["tun"] = {
stack: values.stack,
device:
@@ -312,6 +330,8 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
placeholder="192.168.0.0/16"
ariaLabel={t("settings.modals.tun.fields.routeExcludeAddress")}
disabled={!values.autoRoute}
error={routeExcludeAddressError}
helperText={routeExcludeAddressHelperText}
onChange={(nextValue) =>
setValues((v) => ({ ...v, routeExcludeAddress: nextValue }))
}
+12 -13
View File
@@ -19,7 +19,12 @@ import { useClash } from "@/hooks/use-clash";
import { useAppData } from "@/providers/app-data-context";
import { isPortInUse } from "@/services/cmds";
import { showNotice } from "@/services/notice-service";
import { parseHost, parsedLocalhost, isValidPort } from "@/utils/helper";
import {
formatHostPort,
isValidPort,
normalizeHost,
normalizeListenHost,
} from "@/utils/network";
interface TunnelsViewerRef {
open: () => void;
@@ -127,8 +132,8 @@ export const TunnelsViewer = forwardRef<TunnelsViewerRef>((_, ref) => {
}
// 本地地址校验(host)
const parsedLocal = parsedLocalhost(localAddr);
if (!parsedLocal) {
const localHost = normalizeListenHost(localAddr);
if (!localHost) {
showNotice.error(
"settings.sections.clash.form.fields.tunnels.messages.invalidLocalAddr",
);
@@ -151,8 +156,8 @@ export const TunnelsViewer = forwardRef<TunnelsViewerRef>((_, ref) => {
}
// 目标地址校验 (host)
const parsedTarget = parseHost(targetAddr);
if (!parsedTarget) {
const targetHost = normalizeHost(targetAddr);
if (!targetHost) {
showNotice.error(
"settings.sections.clash.form.fields.tunnels.messages.invalidTargetAddr",
);
@@ -170,14 +175,8 @@ export const TunnelsViewer = forwardRef<TunnelsViewerRef>((_, ref) => {
// 构造新 entry
const entry: TunnelEntry = {
network: network === "tcp+udp" ? ["tcp", "udp"] : [network],
address:
parsedLocal.kind === "ipv6"
? `[${parsedLocal.host}]:${localPort}`
: `${parsedLocal.host}:${localPort}`,
target:
parsedTarget.kind === "ipv6"
? `[${parsedTarget.host}]:${targetPort}`
: `${parsedTarget.host}:${targetPort}`,
address: formatHostPort(localHost, localPort),
target: formatHostPort(targetHost, targetPort),
...(proxy ? { proxy } : {}),
};