|
| 1 | +import { getSafeRedirect } from './getSafeRedirect' |
| 2 | + |
| 3 | +const fallback = '/admin' // default fallback if the input is unsafe or invalid |
| 4 | + |
| 5 | +describe('getSafeRedirect', () => { |
| 6 | + // Valid - safe redirect paths |
| 7 | + it.each([['/dashboard'], ['/admin/settings'], ['/projects?id=123'], ['/hello-world']])( |
| 8 | + 'should allow safe relative path: %s', |
| 9 | + (input) => { |
| 10 | + // If the input is a clean relative path, it should be returned as-is |
| 11 | + expect(getSafeRedirect(input, fallback)).toBe(input) |
| 12 | + }, |
| 13 | + ) |
| 14 | + |
| 15 | + // Invalid types or empty inputs |
| 16 | + it.each(['', null, undefined, 123, {}, []])( |
| 17 | + 'should fallback on invalid or non-string input: %s', |
| 18 | + (input) => { |
| 19 | + // If the input is not a valid string, it should return the fallback |
| 20 | + expect(getSafeRedirect(input as any, fallback)).toBe(fallback) |
| 21 | + }, |
| 22 | + ) |
| 23 | + |
| 24 | + // Unsafe redirect patterns |
| 25 | + it.each([ |
| 26 | + '//example.com', // protocol-relative URL |
| 27 | + '/javascript:alert(1)', // JavaScript scheme |
| 28 | + '/JavaScript:alert(1)', // case-insensitive JavaScript |
| 29 | + '/http://unknown.com', // disguised external redirect |
| 30 | + '/https://unknown.com', // disguised external redirect |
| 31 | + '/%2Funknown.com', // encoded slash — could resolve to // |
| 32 | + '/\\/unknown.com', // escaped slash |
| 33 | + '/\\\\unknown.com', // double escaped slashes |
| 34 | + '/\\unknown.com', // single escaped slash |
| 35 | + '%2F%2Funknown.com', // fully encoded protocol-relative path |
| 36 | + '%2Fjavascript:alert(1)', // encoded JavaScript scheme |
| 37 | + ])('should block unsafe redirect: %s', (input) => { |
| 38 | + // All of these should return the fallback because they’re unsafe |
| 39 | + expect(getSafeRedirect(input, fallback)).toBe(fallback) |
| 40 | + }) |
| 41 | + |
| 42 | + // Input with extra spaces should still be properly handled |
| 43 | + it('should trim whitespace before evaluating', () => { |
| 44 | + // A valid path with surrounding spaces should still be accepted |
| 45 | + expect(getSafeRedirect(' /dashboard ', fallback)).toBe('/dashboard') |
| 46 | + |
| 47 | + // An unsafe path with spaces should still be rejected |
| 48 | + expect(getSafeRedirect(' //example.com ', fallback)).toBe(fallback) |
| 49 | + }) |
| 50 | + |
| 51 | + // If decoding the input fails (e.g., invalid percent encoding), it should not crash |
| 52 | + it('should return fallback on invalid encoding', () => { |
| 53 | + expect(getSafeRedirect('%E0%A4%A', fallback)).toBe(fallback) |
| 54 | + }) |
| 55 | +}) |
0 commit comments