Files
ApiCore/ApiCore-Backend/src/common/file-signature.spec.ts
T
Nick SzaunigandClaude Opus 5.5 0b0bc3c6dc feat: Sprachnotizen-API (T7 L2 Task 7)
Upload (Audio nur per Magic Bytes, versiegelt, 2 MiB), Text-Weg,
Liste, Abruf, Wiederholen, Verwerfen, alle verwerfen, Konfiguration und
Zähler unter /voice-captures. Rechte voice-captures:use plus
inspections:create (eigener Guard, API-Schlüssel ausgeschlossen),
Zähler nur mit system:manage; jede Abfrage auf den Besitzer begrenzt.
Grenzen je Benutzer unter Advisory-Lock (10 offen, 30 je Tag, 10 je
60 s), Wiederholen ebenfalls begrenzt und gedrosselt, Audio für QUEUED
nur unter 24 h. Verwerfen löscht Inhalt und Claim sofort. Keine Inhalte
in Log oder Audit. Wächter gegen Sprachinhalte in Suche, Export und
Berichten erweitert.

Refs #27

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-16 10:10:54 +02:00

110 lines
3.7 KiB
TypeScript

import { detectAudioType, matchesFileSignature } from './file-signature';
describe('matchesFileSignature', () => {
it('accepts a buffer starting with the PNG signature', () => {
const png = Buffer.from([
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00,
]);
expect(matchesFileSignature(png, 'image/png')).toBe(true);
});
it('accepts a buffer starting with the JPEG signature', () => {
const jpeg = Buffer.from([0xff, 0xd8, 0xff, 0x00]);
expect(matchesFileSignature(jpeg, 'image/jpeg')).toBe(true);
});
it('accepts a RIFF/WEBP container', () => {
const webp = Buffer.concat([
Buffer.from('RIFF', 'ascii'),
Buffer.from([0, 0, 0, 0]),
Buffer.from('WEBP', 'ascii'),
]);
expect(matchesFileSignature(webp, 'image/webp')).toBe(true);
});
it('accepts a PDF header', () => {
const pdf = Buffer.from('%PDF-1.7\n', 'ascii');
expect(matchesFileSignature(pdf, 'application/pdf')).toBe(true);
});
it('rejects a buffer whose bytes do not match the declared type', () => {
const plainText = Buffer.from('this is not an image', 'ascii');
expect(matchesFileSignature(plainText, 'image/png')).toBe(false);
expect(matchesFileSignature(plainText, 'image/jpeg')).toBe(false);
expect(matchesFileSignature(plainText, 'image/webp')).toBe(false);
expect(matchesFileSignature(plainText, 'application/pdf')).toBe(false);
});
it('rejects a buffer shorter than the signature it claims to have', () => {
expect(matchesFileSignature(Buffer.from([0xff, 0xd8]), 'image/jpeg')).toBe(
false,
);
});
it('allows any bytes through for a mime type with no known signature', () => {
expect(matchesFileSignature(Buffer.from('anything'), 'text/plain')).toBe(
true,
);
});
});
describe('detectAudioType', () => {
it('knows EBML, OggS and ftyp and nothing else', () => {
const webm = Buffer.from([0x1a, 0x45, 0xdf, 0xa3, 0x9f, 0x42]);
const ogg = Buffer.concat([Buffer.from('OggS', 'ascii'), Buffer.alloc(8)]);
const mp4 = Buffer.concat([
Buffer.from([0x00, 0x00, 0x00, 0x20]),
Buffer.from('ftypM4A ', 'ascii'),
]);
expect(detectAudioType(webm)).toBe('audio/webm');
expect(detectAudioType(ogg)).toBe('audio/ogg');
expect(detectAudioType(mp4)).toBe('audio/mp4');
expect(
detectAudioType(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a])),
).toBeNull();
expect(detectAudioType(Buffer.from('RIFF....WAVE', 'ascii'))).toBeNull();
expect(detectAudioType(Buffer.from('%PDF-1.7', 'ascii'))).toBeNull();
});
it.each(['heic', 'avif', 'mif1', 'qt ', '3gp4'])(
'rejects an ftyp file with the brand %s',
(brand) => {
const file = Buffer.concat([
Buffer.from([0, 0, 0, 0x20]),
Buffer.from(`ftyp${brand}`, 'latin1'),
]);
expect(detectAudioType(file)).toBeNull();
},
);
it.each(['M4A ', 'isom', 'iso2', 'iso6', 'mp41', 'mp42', 'dash'])(
'accepts the audio brand %s',
(brand) => {
const file = Buffer.concat([
Buffer.from([0, 0, 0, 0x20]),
Buffer.from(`ftyp${brand}`, 'latin1'),
]);
expect(detectAudioType(file)).toBe('audio/mp4');
},
);
it('rejects buffers that are too short for any signature', () => {
expect(detectAudioType(Buffer.alloc(0))).toBeNull();
expect(detectAudioType(Buffer.from([0x1a, 0x45, 0xdf]))).toBeNull();
expect(
detectAudioType(Buffer.from('OggS', 'ascii').subarray(0, 3)),
).toBeNull();
expect(
detectAudioType(Buffer.from([0, 0, 0, 0x20, 0x66, 0x74])),
).toBeNull();
});
it('does not take a signature from the middle of the buffer', () => {
const late = Buffer.concat([
Buffer.alloc(16),
Buffer.from('OggS', 'ascii'),
]);
expect(detectAudioType(late)).toBeNull();
});
});