MCPcopy Create free account
hub / github.com/MCSManager/MCSManager / checkSafeUrl

Function checkSafeUrl

panel/src/app/utils/url.ts:5–69  ·  view source on GitHub ↗
(url: string)

Source from the content-addressed store, hash-verified

3 * Prevents SSRF attacks by blocking local/private IP addresses and internal domains
4 */
5export function checkSafeUrl(url: string): boolean {
6 try {
7 const urlObj = new URL(url);
8 const hostname = urlObj.hostname.toLowerCase();
9
10 // Reject IPv6 addresses (IPv6 addresses are wrapped in brackets by URL object)
11 if (hostname.startsWith("[") && hostname.endsWith("]")) {
12 return false;
13 }
14
15 // Reject IPv4 address format
16 const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
17 if (ipv4Regex.test(hostname)) {
18 return false;
19 }
20
21 // Reject local domains and loopback addresses
22 const localDomains = ["localhost", "127.0.0.1", "0.0.0.0", "::1"];
23
24 if (localDomains.includes(hostname)) {
25 return false;
26 }
27
28 // Reject .local domains
29 if (hostname.endsWith(".local")) {
30 return false;
31 }
32
33 // Reject private IP address ranges (additional check in case IP format bypasses above)
34 if (ipv4Regex.test(hostname)) {
35 const parts = hostname.split(".").map(Number);
36 // 10.0.0.0/8
37 if (parts[0] === 10) return false;
38 // 172.16.0.0/12
39 if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return false;
40 // 192.168.0.0/16
41 if (parts[0] === 192 && parts[1] === 168) return false;
42 // 127.0.0.0/8 (loopback)
43 if (parts[0] === 127) return false;
44 // 169.254.0.0/16 (link-local)
45 if (parts[0] === 169 && parts[1] === 254) return false;
46 }
47
48 // Must contain at least one dot (ensure it's a valid domain, not a single word)
49 if (!hostname.includes(".")) {
50 return false;
51 }
52
53 // Domain must have at least a top-level domain (exclude single dot cases)
54 const parts = hostname.split(".");
55 if (parts.length < 2 || parts.some((part) => part.length === 0)) {
56 return false;
57 }
58
59 // Only allow http and https protocols
60 const allowedProtocols = ["http:", "https:"];
61 if (!allowedProtocols.includes(urlObj.protocol)) {
62 return false;

Callers 1

Calls

no outgoing calls

Tested by

no test coverage detected