Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
// @vitest-environment node
|
||
import { describe, expect, it } from 'vitest'
|
||
import sharp from 'sharp'
|
||
import { reencode } from '@/lib/files/reencode'
|
||
import { detectType } from '@/lib/files/magic'
|
||
|
||
/**
|
||
* Die Neukodierung ist die wirksamste Massnahme der Kette: ein Bild, das durch
|
||
* `sharp` gelaufen ist, besteht nur noch aus Pixeln.
|
||
*
|
||
* Diese Tests prüfen genau das - nicht, dass irgendetwas herauskommt, sondern
|
||
* dass das, was nicht Inhalt ist, **weg** ist.
|
||
*/
|
||
|
||
async function testbild(): Promise<Buffer> {
|
||
return sharp({
|
||
create: { width: 24, height: 16, channels: 3, background: { r: 200, g: 40, b: 40 } },
|
||
})
|
||
.jpeg()
|
||
.withMetadata({
|
||
exif: {
|
||
IFD0: { Copyright: 'GEHEIMER FOTOGRAF', Software: 'Kamera 9000' },
|
||
},
|
||
})
|
||
.toBuffer()
|
||
}
|
||
|
||
describe('Bilder neu kodieren', () => {
|
||
it('wirft die Metadaten weg, statt sie zu filtern', async () => {
|
||
const original = await testbild()
|
||
// Gegenprobe: das Original trägt die Daten wirklich.
|
||
expect(original.includes(Buffer.from('GEHEIMER FOTOGRAF'))).toBe(true)
|
||
|
||
const ergebnis = await reencode(new Uint8Array(original), 'image/jpeg')
|
||
expect(ergebnis.ok).toBe(true)
|
||
if (!ergebnis.ok) return
|
||
|
||
const neu = Buffer.from(ergebnis.result.content)
|
||
expect(neu.includes(Buffer.from('GEHEIMER FOTOGRAF'))).toBe(false)
|
||
expect(neu.includes(Buffer.from('Kamera 9000'))).toBe(false)
|
||
|
||
// Und es ist noch dasselbe Bild.
|
||
const info = await sharp(neu).metadata()
|
||
expect(info.width).toBe(24)
|
||
expect(info.height).toBe(16)
|
||
expect(ergebnis.result.width).toBe(24)
|
||
})
|
||
|
||
it('zerstört eine angehängte Nutzlast', async () => {
|
||
// Der klassische Trick: hinter das Bildende Daten kleben. Nach der
|
||
// Neukodierung ist davon nichts mehr da, weil nur die Pixel übernommen
|
||
// werden.
|
||
const angehaengt = Buffer.concat([
|
||
await testbild(),
|
||
Buffer.from('<?php system($_GET["c"]); ?>'),
|
||
])
|
||
const ergebnis = await reencode(new Uint8Array(angehaengt), 'image/jpeg')
|
||
expect(ergebnis.ok).toBe(true)
|
||
if (!ergebnis.ok) return
|
||
expect(Buffer.from(ergebnis.result.content).includes(Buffer.from('<?php'))).toBe(false)
|
||
})
|
||
|
||
it('erzeugt Vorschaubild und Platzhalter', async () => {
|
||
const ergebnis = await reencode(new Uint8Array(await testbild()), 'image/jpeg')
|
||
expect(ergebnis.ok).toBe(true)
|
||
if (!ergebnis.ok) return
|
||
|
||
expect(ergebnis.result.thumb).toBeDefined()
|
||
// Das Vorschaubild ist immer WebP, damit die Auslieferung es gefahrlos
|
||
// inline schicken kann.
|
||
expect(detectType(ergebnis.result.thumb!)).toEqual({ ok: true, type: 'image/webp' })
|
||
expect(ergebnis.result.blurhash).toMatch(/^[\w#$%*+,\-.:;=?@[\]^_{|}~]{6,}$/)
|
||
})
|
||
|
||
it('lehnt ab, was kein Bild ist', async () => {
|
||
const ergebnis = await reencode(new TextEncoder().encode('kein Bild'), 'image/png')
|
||
expect(ergebnis.ok).toBe(false)
|
||
})
|
||
})
|
||
|
||
describe('Text normalisieren', () => {
|
||
it('entfernt die Stückliste am Anfang', async () => {
|
||
const mitBom = new TextEncoder().encode('Hallo Welt')
|
||
const ergebnis = await reencode(mitBom, 'text/plain')
|
||
expect(ergebnis.ok).toBe(true)
|
||
if (!ergebnis.ok) return
|
||
expect(new TextDecoder().decode(ergebnis.result.content)).toBe('Hallo Welt')
|
||
})
|
||
|
||
it('lehnt ungültiges UTF-8 ab', async () => {
|
||
const ergebnis = await reencode(new Uint8Array([0xff, 0xfe, 0x41]), 'text/plain')
|
||
expect(ergebnis.ok).toBe(false)
|
||
})
|
||
})
|