validators.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import z from "zod";
  2. export function isValidCIDR(cidr: string): boolean {
  3. return z.string().cidr().safeParse(cidr).success;
  4. }
  5. export function isValidIP(ip: string): boolean {
  6. return z.string().ip().safeParse(ip).success;
  7. }
  8. export function isValidUrlGlobPattern(pattern: string): boolean {
  9. // Remove leading slash if present
  10. pattern = pattern.startsWith("/") ? pattern.slice(1) : pattern;
  11. // Empty string is not valid
  12. if (!pattern) {
  13. return false;
  14. }
  15. // Split path into segments
  16. const segments = pattern.split("/");
  17. // Check each segment
  18. for (let i = 0; i < segments.length; i++) {
  19. const segment = segments[i];
  20. // Empty segments are not allowed (double slashes), except at the end
  21. if (!segment && i !== segments.length - 1) {
  22. return false;
  23. }
  24. // If segment contains *, it must be exactly *
  25. if (segment.includes("*") && segment !== "*") {
  26. return false;
  27. }
  28. // Check each character in the segment
  29. for (let j = 0; j < segment.length; j++) {
  30. const char = segment[j];
  31. // Check for percent-encoded sequences
  32. if (char === "%" && j + 2 < segment.length) {
  33. const hex1 = segment[j + 1];
  34. const hex2 = segment[j + 2];
  35. if (
  36. !/^[0-9A-Fa-f]$/.test(hex1) ||
  37. !/^[0-9A-Fa-f]$/.test(hex2)
  38. ) {
  39. return false;
  40. }
  41. j += 2; // Skip the next two characters
  42. continue;
  43. }
  44. // Allow:
  45. // - unreserved (A-Z a-z 0-9 - . _ ~)
  46. // - sub-delims (! $ & ' ( ) * + , ; =)
  47. // - @ : for compatibility with some systems
  48. if (!/^[A-Za-z0-9\-._~!$&'()*+,;=@:]$/.test(char)) {
  49. return false;
  50. }
  51. }
  52. }
  53. return true;
  54. }
  55. export function isUrlValid(url: string | undefined) {
  56. if (!url) return true; // the link is optional in the schema so if it's empty it's valid
  57. var pattern = new RegExp(
  58. "^(https?:\\/\\/)?" + // protocol
  59. "((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name
  60. "((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address
  61. "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
  62. "(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
  63. "(\\#[-a-z\\d_]*)?$",
  64. "i"
  65. );
  66. return !!pattern.test(url);
  67. }
  68. export function isTargetValid(value: string | undefined) {
  69. if (!value) return true;
  70. const DOMAIN_REGEX =
  71. /^[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_])?)*$/;
  72. const IPV4_REGEX =
  73. /^(?:(?: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]?)$/;
  74. const IPV6_REGEX = /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/i;
  75. if (IPV4_REGEX.test(value) || IPV6_REGEX.test(value)) {
  76. return true;
  77. }
  78. return DOMAIN_REGEX.test(value);
  79. }