You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

32 lines
1008 B

export class RssUrlUnsafeError extends Error {
constructor(message: string) {
super(message);
this.name = "RssUrlUnsafeError";
}
}
const PRIVATE_IPV4 = /^(127\.|10\.|172\.(1[6-9]|2\d|3[0-1])\.|192\.168\.|0\.|169\.254\.)/;
/** 仅允许 http(s),主机名为非内网字面量(粗略)。完整 DNS 解析防 SSRF 可在 fetch 前再加强。 */
export function assertSafeRssUrl(raw: string) {
let url: URL;
try {
url = new URL(raw);
} catch {
throw new RssUrlUnsafeError("无效的 URL");
}
if (url.protocol !== "http:" && url.protocol !== "https:") {
throw new RssUrlUnsafeError("仅支持 http/https");
}
if (!url.hostname) {
throw new RssUrlUnsafeError("缺少主机名");
}
const host = url.hostname.toLowerCase();
if (host === "localhost" || host.endsWith(".localhost")) {
throw new RssUrlUnsafeError("禁止 localhost");
}
if (PRIVATE_IPV4.test(host)) {
throw new RssUrlUnsafeError("禁止内网地址");
}
return url.toString();
}