'use strict'; /** * Validate a backend/UI URL without accepting credential-bearing or opaque URLs. * Query strings and fragments are rejected so secrets cannot be persisted in the URL. */ function validateBackendUrl(value) { if (typeof value !== 'string') return { valid: false, error: 'URL must be text.' }; const input = value.trim(); if (!input || input.length > 2048) return { valid: false, error: 'Enter a URL up to 2048 characters.' }; let parsed; try { parsed = new URL(input); } catch { return { valid: false, error: 'Enter a complete http:// or https:// URL.' }; } if (!['http:', 'https:'].includes(parsed.protocol)) { return { valid: false, error: 'Only http:// and https:// URLs are supported.' }; } if (!parsed.hostname || parsed.username || parsed.password || parsed.search || parsed.hash) { return { valid: false, error: 'URL must not contain credentials, query parameters, or fragments.' }; } if (/\s/.test(parsed.hostname) || parsed.hostname.includes('..')) { return { valid: false, error: 'Enter a valid hostname.' }; } return { valid: true, value: parsed.toString().replace(/\/$/, '') }; } module.exports = { validateBackendUrl };