Files
Webklar-Kundenbereich/test/auftraegeAblage.test.js
T
2026-10-07 14:07:32 +00:00

84 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// test/auftraegeAblage.test.js
import { test, after } from 'node:test'
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
// Die Ablage liest ihren Ordner aus config.auftraege.uploadDir: AUFTRAEGE_UPLOAD_DIR muss vor dem ersten Import von config.js stehen.
// Die Ablage liegt in einem Unterordner der Wurzel, damit „Köder“-Dateien in der Wurzel einen Ausbruch aus der Ablage (../) sichtbar machen.
const PRAEFIX = path.join(os.tmpdir(), 'auftraege-ablage-')
const wurzel = mkdtempSync(PRAEFIX)
const basis = path.join(wurzel, 'ablage')
mkdirSync(basis, { mode: 0o700 })
process.env.AUFTRAEGE_UPLOAD_DIR = basis
after(() => {
if (wurzel.startsWith(PRAEFIX)) rmSync(wurzel, { recursive: true, force: true })
})
const { speicherePdf, lesePdf, entfernePdf } = await import('../server/services/auftraege/ablage.js')
const { AuftragError } = await import('../server/services/auftraege/fehler.js')
const PDF = Buffer.from('%PDF-1.7\n% Auftragsbestätigung\n%%EOF\n')
const sha = (buffer) => createHash('sha256').update(buffer).digest('hex')
const nichtGefunden = (err) => err instanceof AuftragError && err.status === 404 && err.message === 'PDF nicht gefunden.'
test('speicherePdf legt <kundenId>/<auftragId>.pdf an: Prüfsumme, Rechte 0700 und 0600, Inhalt unverändert', async () => {
const ergebnis = await speicherePdf('c1', 'ab1', PDF)
assert.deepEqual(ergebnis, { pdfDatei: 'c1/ab1.pdf', pdfSha256: sha(PDF) })
assert.match(ergebnis.pdfSha256, /^[0-9a-f]{64}$/)
const datei = path.join(basis, 'c1', 'ab1.pdf')
assert.deepEqual(readFileSync(datei), PDF)
assert.equal(statSync(datei).mode & 0o777, 0o600)
assert.equal(statSync(path.join(basis, 'c1')).mode & 0o777, 0o700)
assert.deepEqual(await lesePdf('c1/ab1.pdf'), PDF)
})
test('speicherePdf überschreibt nie: die vorhandene Datei bleibt, der zweite Aufruf ist 409', async () => {
await speicherePdf('c2', 'ab1', PDF)
await assert.rejects(
() => speicherePdf('c2', 'ab1', Buffer.from('%PDF-1.7\nanderer Inhalt\n')),
(err) => err instanceof AuftragError && err.status === 409,
)
assert.deepEqual(readFileSync(path.join(basis, 'c2', 'ab1.pdf')), PDF)
assert.equal((await speicherePdf('c3', 'ab1', PDF)).pdfDatei, 'c3/ab1.pdf', 'dieselbe AB-ID bei einem anderen Kunden ist eine andere Datei')
})
test('speicherePdf: ungültige Kennungen und Puffer sind Programmfehler und legen nichts an', async () => {
const vorher = readdirSync(basis).sort()
const kennungen = [['', 'ab1'], ['c1', ''], ['../x', 'ab1'], ['c1', '../ab1'], ['c1/x', 'ab1'], ['c1', 'a.b'], ['c1', 'a'.repeat(65)], [undefined, 'ab1'], ['c1', 5]]
for (const [kunde, ab] of kennungen) await assert.rejects(() => speicherePdf(kunde, ab, PDF), TypeError, JSON.stringify([kunde, ab]))
for (const buffer of [Buffer.alloc(0), 'text', undefined, null, new Uint8Array(3)]) {
await assert.rejects(() => speicherePdf('c9', 'ab9', buffer), TypeError, String(buffer))
}
assert.deepEqual(readdirSync(basis).sort(), vorher)
})
test('lesePdf: Pfadtricks, fremde Zeichen und falsche Form sind 404; erlaubt sind 64 Zeichen aus A–Z, a–z, 0–9, _ und -', async () => {
await speicherePdf('c4', 'ab1', PDF)
writeFileSync(path.join(wurzel, 'geheim.pdf'), 'GEHEIM') // neben der Ablage: ein Ausbruch per ../ würde sie lesen
const falsch = [
'c4/../geheim.pdf', '../geheim.pdf', 'c4/ab1.pdf/../../geheim.pdf', '/c4/ab1.pdf', `${basis}/c4/ab1.pdf`, 'c4\\ab1.pdf',
'c4/ab1.PDF', 'c4/ab1.pdf\0', 'c4/ab1.pdf.exe', 'c4/ab 1.pdf', 'c4/ab1', 'ab1.pdf', 'c4/x/ab1.pdf', '', ' ',
`${'a'.repeat(65)}/ab1.pdf`, `c4/${'a'.repeat(65)}.pdf`, 'c4/ä.pdf', undefined, null, 42, {},
]
for (const pdfDatei of falsch) await assert.rejects(() => lesePdf(pdfDatei), nichtGefunden, JSON.stringify(pdfDatei))
await assert.rejects(() => lesePdf('c4/gibtsnicht.pdf'), nichtGefunden)
const lang = `${'A'.repeat(32)}_${'b'.repeat(31)}`
await speicherePdf('K_1-x', lang, PDF)
assert.deepEqual(await lesePdf(`K_1-x/${lang}.pdf`), PDF)
})
test('entfernePdf entfernt eine gespeicherte Datei und prüft den Pfad wie lesePdf; danach lässt sich dieselbe AB erneut speichern', async () => {
await speicherePdf('c5', 'ab1', PDF)
await entfernePdf('c5/ab1.pdf')
assert.equal(existsSync(path.join(basis, 'c5', 'ab1.pdf')), false)
await entfernePdf('c5/ab1.pdf')
await assert.rejects(() => lesePdf('c5/ab1.pdf'), nichtGefunden)
writeFileSync(path.join(wurzel, 'geheim2.pdf'), 'x')
for (const pfad of ['../geheim2.pdf', 'c5/../geheim2.pdf']) await assert.rejects(() => entfernePdf(pfad), nichtGefunden, pfad)
assert.ok(existsSync(path.join(wurzel, 'geheim2.pdf')))
assert.equal((await speicherePdf('c5', 'ab1', PDF)).pdfDatei, 'c5/ab1.pdf')
})