mirror of
https://github.com/fosrl/pangolin.git
synced 2025-05-12 13:20:35 +01:00
95 lines
2.9 KiB
TypeScript
95 lines
2.9 KiB
TypeScript
import z from "zod";
|
|
|
|
export function isValidCIDR(cidr: string): boolean {
|
|
return z.string().cidr().safeParse(cidr).success;
|
|
}
|
|
|
|
export function isValidIP(ip: string): boolean {
|
|
return z.string().ip().safeParse(ip).success;
|
|
}
|
|
|
|
export function isValidUrlGlobPattern(pattern: string): boolean {
|
|
if (pattern === "/") {
|
|
return true;
|
|
}
|
|
|
|
// Remove leading slash if present
|
|
pattern = pattern.startsWith("/") ? pattern.slice(1) : pattern;
|
|
|
|
// Empty string is not valid
|
|
if (!pattern) {
|
|
return false;
|
|
}
|
|
|
|
// Split path into segments
|
|
const segments = pattern.split("/");
|
|
|
|
// Check each segment
|
|
for (let i = 0; i < segments.length; i++) {
|
|
const segment = segments[i];
|
|
|
|
// Empty segments are not allowed (double slashes), except at the end
|
|
if (!segment && i !== segments.length - 1) {
|
|
return false;
|
|
}
|
|
|
|
// Check each character in the segment
|
|
for (let j = 0; j < segment.length; j++) {
|
|
const char = segment[j];
|
|
|
|
// Check for percent-encoded sequences
|
|
if (char === "%" && j + 2 < segment.length) {
|
|
const hex1 = segment[j + 1];
|
|
const hex2 = segment[j + 2];
|
|
if (
|
|
!/^[0-9A-Fa-f]$/.test(hex1) ||
|
|
!/^[0-9A-Fa-f]$/.test(hex2)
|
|
) {
|
|
return false;
|
|
}
|
|
j += 2; // Skip the next two characters
|
|
continue;
|
|
}
|
|
|
|
// Allow:
|
|
// - unreserved (A-Z a-z 0-9 - . _ ~)
|
|
// - sub-delims (! $ & ' ( ) * + , ; =)
|
|
// - @ : for compatibility with some systems
|
|
if (!/^[A-Za-z0-9\-._~!$&'()*+,;#=@:]$/.test(char)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export function isUrlValid(url: string | undefined) {
|
|
if (!url) return true; // the link is optional in the schema so if it's empty it's valid
|
|
var pattern = new RegExp(
|
|
"^(https?:\\/\\/)?" + // protocol
|
|
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name
|
|
"((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address
|
|
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
|
|
"(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
|
|
"(\\#[-a-z\\d_]*)?$",
|
|
"i"
|
|
);
|
|
return !!pattern.test(url);
|
|
}
|
|
|
|
export function isTargetValid(value: string | undefined) {
|
|
if (!value) return true;
|
|
|
|
const DOMAIN_REGEX =
|
|
/^[a-zA-Z0-9_](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9_])?(?:\.[a-zA-Z0-9_](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9_])?)*$/;
|
|
const IPV4_REGEX =
|
|
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
const IPV6_REGEX = /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/i;
|
|
|
|
if (IPV4_REGEX.test(value) || IPV6_REGEX.test(value)) {
|
|
return true;
|
|
}
|
|
|
|
return DOMAIN_REGEX.test(value);
|
|
}
|