Harden cover art fetching

This commit is contained in:
TorMatzAndren
2026-05-16 11:11:59 +02:00
parent 38ca4d6c8d
commit 09bbffa6e8
+37 -2
View File
@@ -494,12 +494,47 @@ export class YoutubeService {
return;
}
const res = await fetch(coverUrl);
let parsedCoverUrl: URL;
try {
parsedCoverUrl = new URL(coverUrl);
} catch {
throw new Error('Invalid cover art URL');
}
if (!['http:', 'https:'].includes(parsedCoverUrl.protocol)) {
throw new Error('Rejected non-HTTP cover art URL');
}
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 15_000);
let res: Response;
try {
res = await fetch(parsedCoverUrl.toString(), { signal: controller.signal });
} finally {
clearTimeout(timeout);
}
if (!res.ok) {
throw new Error(`Failed to fetch cover art: ${res.status}`);
}
const contentType = res.headers.get('content-type')?.split(';')[0].toLowerCase() || '';
if (!['image/jpeg', 'image/png', 'image/webp'].includes(contentType)) {
throw new Error(`Rejected unsupported cover art type: ${contentType || 'unknown'}`);
}
const maxCoverBytes = 5 * 1024 * 1024;
const contentLength = Number(res.headers.get('content-length') || 0);
if (contentLength > maxCoverBytes) {
throw new Error(`Rejected oversized cover art: ${contentLength} bytes`);
}
const arrayBuf = await res.arrayBuffer();
if (arrayBuf.byteLength > maxCoverBytes) {
throw new Error(`Rejected oversized cover art: ${arrayBuf.byteLength} bytes`);
}
const imageBuffer = Buffer.from(arrayBuf);
NodeID3.write(
@@ -507,7 +542,7 @@ export class YoutubeService {
title,
artist,
APIC: {
mime: 'image/jpeg',
mime: contentType,
type: { id: 3, name: 'front cover' },
description: 'cover',
imageBuffer,