mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-07-02 03:16:05 +01:00
Guard against invalid extensions during chat export
This commit is contained in:
@@ -115,14 +115,15 @@ export async function runAttachmentBackupJob(
|
||||
}
|
||||
}
|
||||
|
||||
function getExtension(
|
||||
/** @testexport */
|
||||
export function getExtension(
|
||||
contentType: string | undefined,
|
||||
fileName: string | undefined
|
||||
): string | undefined {
|
||||
if (fileName) {
|
||||
const extension = extname(fileName).replace(/^./, '');
|
||||
|
||||
if (extension) {
|
||||
if (isValidExtension(extension)) {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
@@ -132,31 +133,41 @@ function getExtension(
|
||||
}
|
||||
|
||||
if (contentType.startsWith('application/x-')) {
|
||||
return contentType.replace('application/x-', '');
|
||||
return normalizeExtension(contentType.replace('application/x-', ''));
|
||||
}
|
||||
|
||||
if (contentType.startsWith('application/')) {
|
||||
return contentType.replace('application/', '');
|
||||
return normalizeExtension(contentType.replace('application/', ''));
|
||||
}
|
||||
|
||||
if (contentType.startsWith('audio/')) {
|
||||
return contentType.replace('audio/', '');
|
||||
return normalizeExtension(contentType.replace('audio/', ''));
|
||||
}
|
||||
|
||||
if (contentType.startsWith('image/')) {
|
||||
return contentType.replace('image/', '');
|
||||
return normalizeExtension(contentType.replace('image/', ''));
|
||||
}
|
||||
|
||||
if (contentType === 'text/x-signal-plain') {
|
||||
return 'txt';
|
||||
}
|
||||
if (contentType.startsWith('text/x-')) {
|
||||
return contentType.replace('text/x-', '');
|
||||
return normalizeExtension(contentType.replace('text/x-', ''));
|
||||
}
|
||||
|
||||
if (contentType.startsWith('video/')) {
|
||||
return contentType.replace('video/', '');
|
||||
return normalizeExtension(contentType.replace('video/', ''));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const VALID_EXTENSION_REGEXP = /^[A-Za-z\d](?:[\w+.-]{0,30}[A-Za-z\d])?$/;
|
||||
|
||||
function isValidExtension(extension: string): boolean {
|
||||
return VALID_EXTENSION_REGEXP.test(extension);
|
||||
}
|
||||
|
||||
function normalizeExtension(extension: string): string | undefined {
|
||||
return isValidExtension(extension) ? extension : undefined;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { getExtension } from '../../jobs/AttachmentLocalBackupManager.preload.ts';
|
||||
import { IMAGE_JPEG } from '../../types/MIME.std.ts';
|
||||
|
||||
describe('AttachmentLocalBackupManager', () => {
|
||||
describe('getExtension', () => {
|
||||
it('prefers valid filename extensions', () => {
|
||||
assert.strictEqual(getExtension(IMAGE_JPEG, 'attachment.png'), 'png');
|
||||
assert.strictEqual(getExtension(undefined, 'archive.tar.gz'), 'gz');
|
||||
assert.strictEqual(getExtension(undefined, 'source.c'), 'c');
|
||||
assert.strictEqual(getExtension(undefined, 'image.svg+xml'), 'svg+xml');
|
||||
});
|
||||
|
||||
it('falls back to content type when the filename extension is invalid', () => {
|
||||
assert.strictEqual(getExtension(IMAGE_JPEG, 'attachment.*'), 'jpeg');
|
||||
assert.strictEqual(getExtension(IMAGE_JPEG, 'attachment._png'), 'jpeg');
|
||||
assert.strictEqual(getExtension(IMAGE_JPEG, 'attachment.png_'), 'jpeg');
|
||||
assert.strictEqual(getExtension(IMAGE_JPEG, 'attachment.+png'), 'jpeg');
|
||||
assert.strictEqual(getExtension(IMAGE_JPEG, 'attachment.png+'), 'jpeg');
|
||||
assert.strictEqual(getExtension(IMAGE_JPEG, 'attachment.png-'), 'jpeg');
|
||||
assert.strictEqual(
|
||||
getExtension('application/vnd.ms-excel', 'attachment.'),
|
||||
'vnd.ms-excel'
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores invalid content type extensions', () => {
|
||||
assert.strictEqual(
|
||||
getExtension('application/*', 'attachment.*'),
|
||||
undefined
|
||||
);
|
||||
assert.strictEqual(getExtension('image/jpeg.', undefined), undefined);
|
||||
});
|
||||
|
||||
it('uses txt for signal plain text attachments', () => {
|
||||
assert.strictEqual(getExtension('text/x-signal-plain', undefined), 'txt');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user