This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
'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 };
|
||||
Reference in New Issue
Block a user