Skip to main content
Glama
network.ts4.08 kB
/** * Network Validation Utilities * * Validation functions for network-related data including URLs, email addresses, * and username validation with security considerations. */ import { WordPressAPIError } from "@/types/client.js"; import { config } from "@/config/Config.js"; /** * Validates and sanitizes URLs with enhanced edge case handling */ export function validateUrl(url: string, fieldName: string = "url"): string { // Check for empty or whitespace-only URLs const trimmedUrl = url.trim(); if (!trimmedUrl) { throw new WordPressAPIError(`${fieldName} cannot be empty`, 400, "INVALID_PARAMETER"); } // Remove trailing slashes for consistency const cleanUrl = trimmedUrl.replace(/\/+$/, ""); // Check for common URL mistakes if (!cleanUrl.match(/^https?:\/\//i)) { throw new WordPressAPIError( `Invalid ${fieldName}: must start with http:// or https:// (got "${cleanUrl}")`, 400, "INVALID_PARAMETER", ); } try { const urlObj = new URL(cleanUrl); // Only allow http and https protocols if (!["http:", "https:"].includes(urlObj.protocol)) { throw new WordPressAPIError( `Invalid ${fieldName}: only HTTP and HTTPS protocols are allowed`, 400, "INVALID_PARAMETER", ); } // Validate hostname if (!urlObj.hostname || urlObj.hostname.length < 3) { throw new WordPressAPIError(`Invalid ${fieldName}: hostname is missing or too short`, 400, "INVALID_PARAMETER"); } // Check for localhost in production if (config().app.isProduction && (urlObj.hostname === "localhost" || urlObj.hostname === "127.0.0.1")) { throw new WordPressAPIError( `Invalid ${fieldName}: localhost URLs are not allowed in production`, 400, "INVALID_PARAMETER", ); } // Validate port if present if (urlObj.port) { const port = parseInt(urlObj.port); if (port < 1 || port > 65535) { throw new WordPressAPIError( `Invalid ${fieldName}: port number must be between 1 and 65535`, 400, "INVALID_PARAMETER", ); } } return cleanUrl; } catch (_error) { if (_error instanceof WordPressAPIError) { throw _error; } throw new WordPressAPIError(`Invalid ${fieldName}: malformed URL "${cleanUrl}"`, 400, "INVALID_PARAMETER"); } } /** * Validates email addresses */ export function validateEmail(email: string): string { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { throw new WordPressAPIError("Invalid email address format", 400, "INVALID_PARAMETER"); } return email.toLowerCase(); } /** * Validates username format with enhanced security checks */ export function validateUsername(username: string): string { // Trim and check for empty const trimmed = username.trim(); if (!trimmed) { throw new WordPressAPIError("Username cannot be empty", 400, "INVALID_PARAMETER"); } // WordPress username rules: alphanumeric, space, underscore, hyphen, period, @ symbol const usernameRegex = /^[a-zA-Z0-9 _.\-@]+$/; if (!usernameRegex.test(trimmed)) { throw new WordPressAPIError( "Invalid username: can only contain letters, numbers, spaces, and _.-@ symbols", 400, "INVALID_PARAMETER", ); } // Length validation if (trimmed.length < 3 || trimmed.length > 60) { throw new WordPressAPIError( `Invalid username: must be between 3 and 60 characters (got ${trimmed.length})`, 400, "INVALID_PARAMETER", ); } // Check for consecutive spaces if (/\s{2,}/.test(trimmed)) { throw new WordPressAPIError("Invalid username: cannot contain consecutive spaces", 400, "INVALID_PARAMETER"); } // Security: Prevent common problematic usernames const blacklist = ["admin", "root", "wordpress", "wp-admin", "administrator"]; if (blacklist.includes(trimmed.toLowerCase())) { throw new WordPressAPIError(`Username "${trimmed}" is reserved and cannot be used`, 400, "RESERVED_USERNAME"); } return trimmed; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/docdyhr/mcp-wordpress'

If you have feedback or need assistance with the MCP directory API, please join our Discord server