fix(invoices): CSV-Formel-Injection im Export verhindern
Beim Nightbot-Nachtriage-Durchlauf gefunden: Firmenname und Notiz im Rechnungsjournal-CSV (aus Kunden-/Personal-Eingaben) landeten ungeprüft in der Zelle. Beginnt ein solcher Wert mit =, +, - oder @, interpretieren Excel/Sheets ihn beim Öffnen als Formel statt als Text (CSV-Injection). Zellen mit einem dieser Startzeichen werden jetzt mit einem führenden Apostroph entschärft (Standard-Gegenmaßnahme), sowohl im generischen CSV-Export als auch im DATEV-Buchungstext (dort fließt der Firmenname ebenfalls mit ein). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
12fec5c822
commit
1bda3ac1aa
1 changed files with 5 additions and 2 deletions
|
|
@ -93,7 +93,10 @@ function exportFilter(q: z.infer<typeof exportQuery>): { sql: string; params: un
|
|||
return { sql: conds.join(' AND '), params };
|
||||
}
|
||||
const csvNum = (cents: number) => (cents / 100).toFixed(2).replace('.', ',');
|
||||
const csvCell = (s: string): string => (/[;"\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s);
|
||||
// Formel-Injection-Schutz (CSV): Zellen, die mit =, +, -, @ oder Tab beginnen, werden von Excel/Sheets sonst als Formel
|
||||
// ausgeführt. Firmenname/Notiz stammen aus vom Kunden bzw. Personal eingegebenen Feldern – daher immer entschärfen.
|
||||
const csvSafe = (s: string): string => (/^[=+\-@\t]/.test(s) ? `'${s}` : s);
|
||||
const csvCell = (s: string): string => { const v = csvSafe(s); return /[;"\n]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v; };
|
||||
const csvDate = (d: unknown) => (d ? new Date(d as string).toISOString().slice(0, 10) : '');
|
||||
const STATUS_LABEL: Record<string, string> = { open: 'Offen', paid: 'Bezahlt', cancelled: 'Storniert' };
|
||||
|
||||
|
|
@ -123,7 +126,7 @@ const DATEV_BOOKING_HEADER = [
|
|||
const pad2 = (n: number) => String(n).padStart(2, '0');
|
||||
const datevYmd = (d: Date) => `${d.getUTCFullYear()}${pad2(d.getUTCMonth() + 1)}${pad2(d.getUTCDate())}`;
|
||||
const datevDm = (d: Date) => `${pad2(d.getUTCDate())}${pad2(d.getUTCMonth() + 1)}`;
|
||||
const datevQ = (s: string) => `"${s.replace(/"/g, '""')}"`;
|
||||
const datevQ = (s: string) => `"${csvSafe(s).replace(/"/g, '""')}"`;
|
||||
const datevNum = (cents: number) => (cents / 100).toFixed(2).replace('.', ',');
|
||||
/** Eine Buchungszeile mit genau 125 Feldern; nur die übergebenen (1-basierten) Spaltennummern werden gesetzt, der Rest bleibt leer. */
|
||||
function datevRow(vals: Record<number, string>): string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue