mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-05 16:37:46 +00:00
fix(backend): use URL parser for registry scheme + template host check (#808)
Two small hardenings flagged by CodeQL:
- routes/registries.ts: drop the redundant startsWith block-list and rely
solely on URL parsing + protocol allow-list. The startsWith pass was
unreachable defense (any non-http/https scheme already fails the
protocol check below it) and was tripping js/incomplete-url-scheme-check.
- services/TemplateService.ts: replace the .includes('api.linuxserver.io')
substring match with new URL(registryUrl).hostname comparison. The
substring form would mis-classify a malicious admin-set URL like
https://evil.example/api.linuxserver.io/... as the LSIO registry and
apply the LSIO response parser to its payload. Hostname compare closes
that.
Behavioral parity for the happy path: every previously-accepted URL still
parses; the LSIO branch still triggers when the hostname is exactly
api.linuxserver.io.
This commit is contained in:
@@ -11,17 +11,14 @@ function isValidRegistryUrl(url: string, type: string): boolean {
|
||||
if (type === 'dockerhub') return true;
|
||||
const trimmed = url.trim();
|
||||
if (!trimmed) return false;
|
||||
const lower = trimmed.toLowerCase();
|
||||
if (lower.startsWith('javascript:') || lower.startsWith('data:') || lower.startsWith('file:') || lower.startsWith('ftp:')) {
|
||||
return false;
|
||||
}
|
||||
let parsed: URL;
|
||||
try {
|
||||
const parsed = new URL(trimmed.includes('://') ? trimmed : `https://${trimmed}`);
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return false;
|
||||
if (!parsed.hostname) return false;
|
||||
parsed = new URL(trimmed.includes('://') ? trimmed : `https://${trimmed}`);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return false;
|
||||
if (!parsed.hostname) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,9 @@ export class TemplateService {
|
||||
console.log(`[Templates] Fetching from registry: ${registryUrl}`);
|
||||
const debug = isDebugEnabled();
|
||||
|
||||
if (registryUrl.includes('api.linuxserver.io')) {
|
||||
let registryHost = '';
|
||||
try { registryHost = new URL(registryUrl).hostname.toLowerCase(); } catch { /* invalid URL, treated as non-LSIO */ }
|
||||
if (registryHost === 'api.linuxserver.io') {
|
||||
const response = await axios.get<LsioApiResponse>(registryUrl, { timeout: 20_000 });
|
||||
// Official LSIO API Schema Mapping
|
||||
const lsioApps = response.data?.data?.repositories?.linuxserver ?? {};
|
||||
|
||||
Reference in New Issue
Block a user