feat(quick-260917-gyd): Ruecksprung nach Anmeldung auf urspruenglich angeforderte Seite
- safe-next.ts: buildNextParam()/sanitizeNextPath() als reine, getestete Funktionen (Open-Redirect-Schutz) - middleware.ts: haengt next-Parameter an beide Login-Umleitungen (fehlendes Cookie, ungueltige Signatur) - login/page.tsx: springt nach erfolgreicher Anmeldung auf den bereinigten next-Wert Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { buildNextParam, sanitizeNextPath } from './safe-next';
|
||||
|
||||
/**
|
||||
* safe-next.test — Ruecksprung nach Anmeldung (quick-260917-gyd).
|
||||
*
|
||||
* Reine Funktionen ohne DOM-/Node-Abhaengigkeit: `buildNextParam` baut den
|
||||
* `next`-Wert aus Pfad + Query, `sanitizeNextPath` prueft ihn beim Absenden
|
||||
* der Anmeldeseite gegen Open-Redirect-Versuche.
|
||||
*/
|
||||
|
||||
describe('buildNextParam', () => {
|
||||
it('Test 1: Pfad ohne Query bleibt unveraendert', () => {
|
||||
expect(buildNextParam('/settings/general/desktop', '')).toBe(
|
||||
'/settings/general/desktop',
|
||||
);
|
||||
});
|
||||
|
||||
it('Test 2: _rsc wird entfernt, andere Parameter bleiben', () => {
|
||||
expect(buildNextParam('/modules/tender-radar', '?tab=alerts&_rsc=1abc')).toBe(
|
||||
'/modules/tender-radar?tab=alerts',
|
||||
);
|
||||
});
|
||||
|
||||
it('Test 3: leere Query nach Entfernen von _rsc bleibt ohne "?"', () => {
|
||||
expect(buildNextParam('/modules/tender-radar', '?_rsc=1abc')).toBe(
|
||||
'/modules/tender-radar',
|
||||
);
|
||||
});
|
||||
|
||||
it('Test 4: Startseite und Anmeldeseite liefern null (kein Ruecksprung auf sich selbst)', () => {
|
||||
expect(buildNextParam('/', '')).toBeNull();
|
||||
expect(buildNextParam('/login', '?next=%2Fx')).toBeNull();
|
||||
expect(buildNextParam('/login/', '')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeNextPath', () => {
|
||||
it('Test 5: gueltige relative Pfade bleiben unveraendert', () => {
|
||||
expect(sanitizeNextPath('/settings/general/desktop')).toBe(
|
||||
'/settings/general/desktop',
|
||||
);
|
||||
expect(sanitizeNextPath('/modules/x?tab=1')).toBe('/modules/x?tab=1');
|
||||
});
|
||||
|
||||
it('Test 6: fehlende oder falsch typisierte Werte fallen auf "/" zurueck', () => {
|
||||
expect(sanitizeNextPath(null)).toBe('/');
|
||||
expect(sanitizeNextPath(undefined)).toBe('/');
|
||||
expect(sanitizeNextPath('')).toBe('/');
|
||||
expect(sanitizeNextPath(42)).toBe('/');
|
||||
});
|
||||
|
||||
it('Test 7: protokoll-relative und Schema-Adressen fallen auf "/" zurueck', () => {
|
||||
expect(sanitizeNextPath('//evil.example')).toBe('/');
|
||||
expect(sanitizeNextPath('/\\evil.example')).toBe('/');
|
||||
expect(sanitizeNextPath('https://evil.example/x')).toBe('/');
|
||||
expect(sanitizeNextPath('javascript:alert(1)')).toBe('/');
|
||||
});
|
||||
|
||||
it('Test 8: fehlender Slash, Steuerzeichen und Ueberlaenge fallen auf "/" zurueck', () => {
|
||||
expect(sanitizeNextPath('settings')).toBe('/');
|
||||
expect(sanitizeNextPath('/foo\nbar')).toBe('/');
|
||||
expect(sanitizeNextPath('/a b')).toBe('/');
|
||||
expect(sanitizeNextPath('/x'.padEnd(3000, 'y'))).toBe('/');
|
||||
});
|
||||
|
||||
it('Test 9: die Anmeldeseite selbst ist kein gueltiges Ziel, "/loginhistory" schon', () => {
|
||||
expect(sanitizeNextPath('/login')).toBe('/');
|
||||
expect(sanitizeNextPath('/login?next=/x')).toBe('/');
|
||||
expect(sanitizeNextPath('/login/')).toBe('/');
|
||||
expect(sanitizeNextPath('/loginhistory')).toBe('/loginhistory');
|
||||
});
|
||||
});
|
||||
Binary file not shown.
Reference in New Issue
Block a user