mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
fix: close upload streams and validate cloud auth and ranges
This commit is contained in:
@@ -140,7 +140,9 @@ async function uploadMultipart(options: {
|
||||
callback(null, chunk);
|
||||
},
|
||||
});
|
||||
const body = createReadStream(options.filePath, { start, end }).pipe(progress);
|
||||
const source = createReadStream(options.filePath, { start, end });
|
||||
const body = source.pipe(progress);
|
||||
source.on("error", (error) => body.destroy(error));
|
||||
try {
|
||||
const partResponse = await fetch(
|
||||
new URL(
|
||||
@@ -197,6 +199,7 @@ async function uploadMultipart(options: {
|
||||
}
|
||||
} finally {
|
||||
body.destroy();
|
||||
source.destroy();
|
||||
}
|
||||
|
||||
options.onProgress(confirmedBytes);
|
||||
@@ -358,7 +361,9 @@ export function registerCloudShareHandlers() {
|
||||
callback(null, chunk);
|
||||
},
|
||||
});
|
||||
const body = createReadStream(resolvedPath).pipe(progress);
|
||||
const source = createReadStream(resolvedPath);
|
||||
const body = source.pipe(progress);
|
||||
source.on("error", (error) => body.destroy(error));
|
||||
const uploadHeaders: Record<string, string> = {
|
||||
"content-type": contentType,
|
||||
"content-length": String(stat.size),
|
||||
@@ -383,6 +388,7 @@ export function registerCloudShareHandlers() {
|
||||
}
|
||||
} finally {
|
||||
body.destroy();
|
||||
source.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- Require an authenticated Recordly viewer account before posting comments.
|
||||
-- Optional viewer accounts; posting comments does not require an account.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS comment_users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
@@ -442,6 +442,7 @@ function dashboardPassword(env) {
|
||||
|
||||
async function expectedSessionToken(env) {
|
||||
const password = dashboardPassword(env);
|
||||
if (!password) throw new Error('Dashboard sign-in is not configured');
|
||||
const encoder = new TextEncoder();
|
||||
const key = await crypto.subtle.importKey(
|
||||
'raw', encoder.encode(password), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
|
||||
@@ -455,6 +456,7 @@ async function isDashboardAuthed(request, env) {
|
||||
}
|
||||
|
||||
async function dashboardCookieAuthed(request, env) {
|
||||
if (!dashboardPassword(env)) return false;
|
||||
const cookies = parseCookies(request.headers.get('Cookie') || '');
|
||||
const sessionToken = cookies['voom_session'];
|
||||
if (!sessionToken) return false;
|
||||
@@ -1087,7 +1089,7 @@ async function handleVideoStream(request, env, shareCode) {
|
||||
|
||||
const totalSize = object.size; // R2Object.size is the full stored object size
|
||||
const start = suffix ? Math.max(0, totalSize - r2Range.suffix) : r2Range.offset;
|
||||
const actualEnd = !suffix && r2Range.length !== undefined ? start + r2Range.length - 1 : totalSize - 1;
|
||||
const actualEnd = !suffix && r2Range.length !== undefined ? Math.min(totalSize - 1, start + r2Range.length - 1) : totalSize - 1;
|
||||
|
||||
return new Response(object.body, {
|
||||
status: 206,
|
||||
|
||||
@@ -544,3 +544,21 @@ it('rejects creating protected shares without a signing secret', async () => {
|
||||
await response.arrayBuffer();
|
||||
} finally { lookup.mockRestore(); }
|
||||
});
|
||||
|
||||
it('rejects dashboard cookies without configured secrets instead of crashing', async () => {
|
||||
const config = { ...env, API_SECRET: undefined, DASHBOARD_PASSWORD: undefined };
|
||||
const response = await worker.fetch(new Request(`${BASE}/api/videos`, {
|
||||
headers: { Cookie: 'voom_session=untrusted' },
|
||||
}), config, {});
|
||||
expect(response.status).toBe(401);
|
||||
await response.arrayBuffer();
|
||||
});
|
||||
it('clamps a bounded video range to the actual object size', async () => {
|
||||
const { shareCode } = await createShare();
|
||||
await completeUpload(shareCode);
|
||||
const response = await SELF.fetch(`${BASE}/v/${shareCode}`, { headers: { Range: 'bytes=6-200' } });
|
||||
expect(response.status).toBe(206);
|
||||
expect(response.headers.get('Content-Range')).toBe('bytes 6-7/8');
|
||||
expect(response.headers.get('Content-Length')).toBe('2');
|
||||
expect(Array.from(new Uint8Array(await response.arrayBuffer()))).toEqual([6, 7]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user