fix(security): Symlinks im Scanner, Exportpfad und Belegnummer-URL absichern

- Scanner: Dateien von der Scan-Freigabe werden nur übernommen, wenn sie
  reguläre Dateien in echten Verzeichnissen sind. chokidar folgt keinen
  Symlinks mehr; beim Kopieren über Mount-Grenzen (/mnt/scans → /mnt/data)
  wird mit O_NOFOLLOW geöffnet und nur exklusiv geschrieben. Bisher holte
  ein Symlink wie scan.pdf -> /proc/self/environ die Zieldatei in den
  Posteingang.
- Export: Dateinamen (enthalten u.a. Barcode-Werte) werden auf ein
  Pfadsegment reduziert – „../“ verlässt den FTP-Zielordner nicht mehr.
- Belegnummer: {Jahr}/{Nummer} werden URL-kodiert in BELEGNUMMER_*_URL
  eingesetzt (Nummer stammt aus Request bzw. gedrucktem Barcode);
  belegnummer/release prüft die Typen im Body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bjoernpoettkerandClaude Opus 5.5 committed 2026-11-04 10:46:57 +01:00
1 parent c28475f71f
commit fe338271bf
6 files changed
+260 -28

No files matched your search

@@ -28,6 +28,7 @@ import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs/promises';
import * as crypto from 'crypto';
import { ersetzeUrlPlatzhalter } from '../common/pfad-sicherheit.util';
function sanitizeFilename(name: string): string {
return name
@@ -152,7 +153,18 @@ export class EmailImportService {
const year = (isNaN(dateObj.getTime()) ? new Date() : dateObj)
.getFullYear()
.toString();
return urlTemplate.replace('{Jahr}', year);
return ersetzeUrlPlatzhalter(urlTemplate, { Jahr: year });
}
/** URL mit Jahr und Belegnummer – die Nummer kommt u.a. aus Barcodes. */
private buildNummerUrl(
urlTemplate: string,
dateStr: string,
nummer: string,
): string {
return ersetzeUrlPlatzhalter(this.buildUrl(urlTemplate, dateStr), {
Nummer: nummer,
});
}
async getBelegnummer(emailDate: string): Promise<string> {
@@ -207,8 +219,7 @@ export class EmailImportService {
}
const cleanNumber = number.replace(/^0+/, '') || '0';
let url = this.buildUrl(urlTemplate, emailDate);
url = url.replace('{Nummer}', cleanNumber);
const url = this.buildNummerUrl(urlTemplate, emailDate, cleanNumber);
try {
this.logger.log(
@@ -231,8 +242,7 @@ export class EmailImportService {
);
const cleanNumber = number.replace(/^0+/, '') || '0';
let url = this.buildUrl(urlTemplate, emailDate);
url = url.replace('{Nummer}', cleanNumber);
const url = this.buildNummerUrl(urlTemplate, emailDate, cleanNumber);
try {
this.logger.log(