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
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();
|
|
}
|
|
|