@payloadcms/next 3.32.0-internal.30bda70 → 3.32.0-internal.7b8361c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSafeRedirect.d.ts","sourceRoot":"","sources":["../../src/utilities/getSafeRedirect.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,kBACX,MAAM,GAAG,MAAM,EAAE,aACtB,MAAM,KACf,
|
|
1
|
+
{"version":3,"file":"getSafeRedirect.d.ts","sourceRoot":"","sources":["../../src/utilities/getSafeRedirect.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,kBACX,MAAM,GAAG,MAAM,EAAE,aACtB,MAAM,KACf,MA8BF,CAAA"}
|
|
@@ -2,13 +2,23 @@ export const getSafeRedirect = (redirectParam, fallback = '/') => {
|
|
|
2
2
|
if (typeof redirectParam !== 'string') {
|
|
3
3
|
return fallback;
|
|
4
4
|
}
|
|
5
|
-
//
|
|
6
|
-
|
|
5
|
+
// Normalize and decode the path
|
|
6
|
+
let redirectPath;
|
|
7
|
+
try {
|
|
8
|
+
redirectPath = decodeURIComponent(redirectParam.trim());
|
|
9
|
+
} catch {
|
|
10
|
+
return fallback // invalid encoding
|
|
11
|
+
;
|
|
12
|
+
}
|
|
7
13
|
const isSafeRedirect =
|
|
8
14
|
// Must start with a single forward slash (e.g., "/admin")
|
|
9
15
|
redirectPath.startsWith('/') &&
|
|
10
|
-
// Prevent protocol-relative URLs (e.g., "//
|
|
16
|
+
// Prevent protocol-relative URLs (e.g., "//example.com")
|
|
11
17
|
!redirectPath.startsWith('//') &&
|
|
18
|
+
// Prevent encoded slashes that could resolve to protocol-relative
|
|
19
|
+
!redirectPath.startsWith('/%2F') &&
|
|
20
|
+
// Prevent backslash-based escape attempts (e.g., "/\\/example.com", "/\\\\example.com", "/\\example.com")
|
|
21
|
+
!redirectPath.startsWith('/\\/') && !redirectPath.startsWith('/\\\\') && !redirectPath.startsWith('/\\') &&
|
|
12
22
|
// Prevent javascript-based schemes (e.g., "/javascript:alert(1)")
|
|
13
23
|
!redirectPath.toLowerCase().startsWith('/javascript:') &&
|
|
14
24
|
// Prevent attempts to redirect to full URLs using "/http:" or "/https:"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSafeRedirect.js","names":["getSafeRedirect","redirectParam","fallback","redirectPath","trim","isSafeRedirect","startsWith","toLowerCase"],"sources":["../../src/utilities/getSafeRedirect.ts"],"sourcesContent":["export const getSafeRedirect = (\n redirectParam: string | string[],\n fallback: string = '/',\n): string => {\n if (typeof redirectParam !== 'string') {\n return fallback\n }\n\n //
|
|
1
|
+
{"version":3,"file":"getSafeRedirect.js","names":["getSafeRedirect","redirectParam","fallback","redirectPath","decodeURIComponent","trim","isSafeRedirect","startsWith","toLowerCase"],"sources":["../../src/utilities/getSafeRedirect.ts"],"sourcesContent":["export const getSafeRedirect = (\n redirectParam: string | string[],\n fallback: string = '/',\n): string => {\n if (typeof redirectParam !== 'string') {\n return fallback\n }\n\n // Normalize and decode the path\n let redirectPath: string\n try {\n redirectPath = decodeURIComponent(redirectParam.trim())\n } catch {\n return fallback // invalid encoding\n }\n\n const isSafeRedirect =\n // Must start with a single forward slash (e.g., \"/admin\")\n redirectPath.startsWith('/') &&\n // Prevent protocol-relative URLs (e.g., \"//example.com\")\n !redirectPath.startsWith('//') &&\n // Prevent encoded slashes that could resolve to protocol-relative\n !redirectPath.startsWith('/%2F') &&\n // Prevent backslash-based escape attempts (e.g., \"/\\\\/example.com\", \"/\\\\\\\\example.com\", \"/\\\\example.com\")\n !redirectPath.startsWith('/\\\\/') &&\n !redirectPath.startsWith('/\\\\\\\\') &&\n !redirectPath.startsWith('/\\\\') &&\n // Prevent javascript-based schemes (e.g., \"/javascript:alert(1)\")\n !redirectPath.toLowerCase().startsWith('/javascript:') &&\n // Prevent attempts to redirect to full URLs using \"/http:\" or \"/https:\"\n !redirectPath.toLowerCase().startsWith('/http')\n\n return isSafeRedirect ? redirectPath : fallback\n}\n"],"mappings":"AAAA,OAAO,MAAMA,eAAA,GAAkBA,CAC7BC,aAAA,EACAC,QAAA,GAAmB,GAAG;EAEtB,IAAI,OAAOD,aAAA,KAAkB,UAAU;IACrC,OAAOC,QAAA;EACT;EAEA;EACA,IAAIC,YAAA;EACJ,IAAI;IACFA,YAAA,GAAeC,kBAAA,CAAmBH,aAAA,CAAcI,IAAI;EACtD,EAAE,MAAM;IACN,OAAOH,QAAA,CAAS;IAAA;EAClB;EAEA,MAAMI,cAAA;EACJ;EACAH,YAAA,CAAaI,UAAU,CAAC;EACxB;EACA,CAACJ,YAAA,CAAaI,UAAU,CAAC;EACzB;EACA,CAACJ,YAAA,CAAaI,UAAU,CAAC;EACzB;EACA,CAACJ,YAAA,CAAaI,UAAU,CAAC,WACzB,CAACJ,YAAA,CAAaI,UAAU,CAAC,YACzB,CAACJ,YAAA,CAAaI,UAAU,CAAC;EACzB;EACA,CAACJ,YAAA,CAAaK,WAAW,GAAGD,UAAU,CAAC;EACvC;EACA,CAACJ,YAAA,CAAaK,WAAW,GAAGD,UAAU,CAAC;EAEzC,OAAOD,cAAA,GAAiBH,YAAA,GAAeD,QAAA;AACzC","ignoreList":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getSafeRedirect } from './getSafeRedirect';
|
|
2
|
+
const fallback = '/admin' // default fallback if the input is unsafe or invalid
|
|
3
|
+
;
|
|
4
|
+
describe('getSafeRedirect', () => {
|
|
5
|
+
// Valid - safe redirect paths
|
|
6
|
+
it.each([['/dashboard'], ['/admin/settings'], ['/projects?id=123'], ['/hello-world']])('should allow safe relative path: %s', input => {
|
|
7
|
+
// If the input is a clean relative path, it should be returned as-is
|
|
8
|
+
expect(getSafeRedirect(input, fallback)).toBe(input);
|
|
9
|
+
});
|
|
10
|
+
// Invalid types or empty inputs
|
|
11
|
+
it.each(['', null, undefined, 123, {}, []])('should fallback on invalid or non-string input: %s', input => {
|
|
12
|
+
// If the input is not a valid string, it should return the fallback
|
|
13
|
+
expect(getSafeRedirect(input, fallback)).toBe(fallback);
|
|
14
|
+
});
|
|
15
|
+
// Unsafe redirect patterns
|
|
16
|
+
it.each(['//example.com', '/javascript:alert(1)', '/JavaScript:alert(1)', '/http://unknown.com', '/https://unknown.com', '/%2Funknown.com', '/\\/unknown.com', '/\\\\unknown.com', '/\\unknown.com', '%2F%2Funknown.com', '%2Fjavascript:alert(1)'])('should block unsafe redirect: %s', input => {
|
|
17
|
+
// All of these should return the fallback because they’re unsafe
|
|
18
|
+
expect(getSafeRedirect(input, fallback)).toBe(fallback);
|
|
19
|
+
});
|
|
20
|
+
// Input with extra spaces should still be properly handled
|
|
21
|
+
it('should trim whitespace before evaluating', () => {
|
|
22
|
+
// A valid path with surrounding spaces should still be accepted
|
|
23
|
+
expect(getSafeRedirect(' /dashboard ', fallback)).toBe('/dashboard');
|
|
24
|
+
// An unsafe path with spaces should still be rejected
|
|
25
|
+
expect(getSafeRedirect(' //example.com ', fallback)).toBe(fallback);
|
|
26
|
+
});
|
|
27
|
+
// If decoding the input fails (e.g., invalid percent encoding), it should not crash
|
|
28
|
+
it('should return fallback on invalid encoding', () => {
|
|
29
|
+
expect(getSafeRedirect('%E0%A4%A', fallback)).toBe(fallback);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=getSafeRedirect.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getSafeRedirect.spec.js","names":["getSafeRedirect","fallback","describe","it","each","input","expect","toBe","undefined"],"sources":["../../src/utilities/getSafeRedirect.spec.ts"],"sourcesContent":["import { getSafeRedirect } from './getSafeRedirect'\n\nconst fallback = '/admin' // default fallback if the input is unsafe or invalid\n\ndescribe('getSafeRedirect', () => {\n // Valid - safe redirect paths\n it.each([['/dashboard'], ['/admin/settings'], ['/projects?id=123'], ['/hello-world']])(\n 'should allow safe relative path: %s',\n (input) => {\n // If the input is a clean relative path, it should be returned as-is\n expect(getSafeRedirect(input, fallback)).toBe(input)\n },\n )\n\n // Invalid types or empty inputs\n it.each(['', null, undefined, 123, {}, []])(\n 'should fallback on invalid or non-string input: %s',\n (input) => {\n // If the input is not a valid string, it should return the fallback\n expect(getSafeRedirect(input as any, fallback)).toBe(fallback)\n },\n )\n\n // Unsafe redirect patterns\n it.each([\n '//example.com', // protocol-relative URL\n '/javascript:alert(1)', // JavaScript scheme\n '/JavaScript:alert(1)', // case-insensitive JavaScript\n '/http://unknown.com', // disguised external redirect\n '/https://unknown.com', // disguised external redirect\n '/%2Funknown.com', // encoded slash — could resolve to //\n '/\\\\/unknown.com', // escaped slash\n '/\\\\\\\\unknown.com', // double escaped slashes\n '/\\\\unknown.com', // single escaped slash\n '%2F%2Funknown.com', // fully encoded protocol-relative path\n '%2Fjavascript:alert(1)', // encoded JavaScript scheme\n ])('should block unsafe redirect: %s', (input) => {\n // All of these should return the fallback because they’re unsafe\n expect(getSafeRedirect(input, fallback)).toBe(fallback)\n })\n\n // Input with extra spaces should still be properly handled\n it('should trim whitespace before evaluating', () => {\n // A valid path with surrounding spaces should still be accepted\n expect(getSafeRedirect(' /dashboard ', fallback)).toBe('/dashboard')\n\n // An unsafe path with spaces should still be rejected\n expect(getSafeRedirect(' //example.com ', fallback)).toBe(fallback)\n })\n\n // If decoding the input fails (e.g., invalid percent encoding), it should not crash\n it('should return fallback on invalid encoding', () => {\n expect(getSafeRedirect('%E0%A4%A', fallback)).toBe(fallback)\n })\n})\n"],"mappings":"AAAA,SAASA,eAAe,QAAQ;AAEhC,MAAMC,QAAA,GAAW,SAAS;AAAA;AAE1BC,QAAA,CAAS,mBAAmB;EAC1B;EACAC,EAAA,CAAGC,IAAI,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,kBAAkB,EAAE,CAAC,mBAAmB,EAAE,CAAC,eAAe,CAAC,EACnF,uCACCC,KAAA;IACC;IACAC,MAAA,CAAON,eAAA,CAAgBK,KAAA,EAAOJ,QAAA,GAAWM,IAAI,CAACF,KAAA;EAChD;EAGF;EACAF,EAAA,CAAGC,IAAI,CAAC,CAAC,IAAI,MAAMI,SAAA,EAAW,KAAK,CAAC,GAAG,EAAE,CAAC,EACxC,sDACCH,KAAA;IACC;IACAC,MAAA,CAAON,eAAA,CAAgBK,KAAA,EAAcJ,QAAA,GAAWM,IAAI,CAACN,QAAA;EACvD;EAGF;EACAE,EAAA,CAAGC,IAAI,CAAC,CACN,iBACA,wBACA,wBACA,uBACA,wBACA,mBACA,mBACA,oBACA,kBACA,qBACA,yBACD,EAAE,oCAAqCC,KAAA;IACtC;IACAC,MAAA,CAAON,eAAA,CAAgBK,KAAA,EAAOJ,QAAA,GAAWM,IAAI,CAACN,QAAA;EAChD;EAEA;EACAE,EAAA,CAAG,4CAA4C;IAC7C;IACAG,MAAA,CAAON,eAAA,CAAgB,oBAAoBC,QAAA,GAAWM,IAAI,CAAC;IAE3D;IACAD,MAAA,CAAON,eAAA,CAAgB,uBAAuBC,QAAA,GAAWM,IAAI,CAACN,QAAA;EAChE;EAEA;EACAE,EAAA,CAAG,8CAA8C;IAC/CG,MAAA,CAAON,eAAA,CAAgB,YAAYC,QAAA,GAAWM,IAAI,CAACN,QAAA;EACrD;AACF","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/next",
|
|
3
|
-
"version": "3.32.0-internal.
|
|
3
|
+
"version": "3.32.0-internal.7b8361c",
|
|
4
4
|
"homepage": "https://payloadcms.com",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -79,9 +79,9 @@
|
|
|
79
79
|
"react-diff-viewer-continued": "4.0.5",
|
|
80
80
|
"sass": "1.77.4",
|
|
81
81
|
"uuid": "10.0.0",
|
|
82
|
-
"@payloadcms/graphql": "3.32.0-internal.
|
|
83
|
-
"@payloadcms/ui": "3.32.0-internal.
|
|
84
|
-
"@payloadcms/translations": "3.32.0-internal.
|
|
82
|
+
"@payloadcms/graphql": "3.32.0-internal.7b8361c",
|
|
83
|
+
"@payloadcms/ui": "3.32.0-internal.7b8361c",
|
|
84
|
+
"@payloadcms/translations": "3.32.0-internal.7b8361c"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@babel/cli": "7.26.4",
|
|
@@ -100,12 +100,12 @@
|
|
|
100
100
|
"eslint-plugin-react-compiler": "19.0.0-beta-714736e-20250131",
|
|
101
101
|
"swc-plugin-transform-remove-imports": "3.1.0",
|
|
102
102
|
"@payloadcms/eslint-config": "3.28.0",
|
|
103
|
-
"payload": "3.32.0-internal.
|
|
103
|
+
"payload": "3.32.0-internal.7b8361c"
|
|
104
104
|
},
|
|
105
105
|
"peerDependencies": {
|
|
106
106
|
"graphql": "^16.8.1",
|
|
107
107
|
"next": "^15.2.3",
|
|
108
|
-
"payload": "3.32.0-internal.
|
|
108
|
+
"payload": "3.32.0-internal.7b8361c"
|
|
109
109
|
},
|
|
110
110
|
"engines": {
|
|
111
111
|
"node": "^18.20.2 || >=20.9.0"
|