@tinacms/app 2.5.12 → 2.5.14
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinacms/app",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.14",
|
|
4
4
|
"main": "src/main.tsx",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@graphiql/toolkit": "^0.11.3",
|
|
29
29
|
"@monaco-editor/loader": "^1.7.0",
|
|
30
30
|
"@radix-ui/react-popover": "^1.1.18",
|
|
31
|
-
"final-form": "
|
|
31
|
+
"final-form": "4.20.10",
|
|
32
32
|
"graphiql": "^4.1.2",
|
|
33
33
|
"graphql": "15.8.0",
|
|
34
34
|
"lucide-react": "^0.424.0",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
37
|
"react-router-dom": "^6.30.3",
|
|
38
38
|
"zod": "^3.24.2",
|
|
39
|
-
"@tinacms/mdx": "^2.2.
|
|
40
|
-
"tinacms": "^3.
|
|
39
|
+
"@tinacms/mdx": "^2.2.3",
|
|
40
|
+
"tinacms": "^3.14.0"
|
|
41
41
|
},
|
|
42
42
|
"repository": {
|
|
43
43
|
"url": "https://github.com/tinacms/tinacms.git",
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
TinaField,
|
|
17
17
|
TinaSchema,
|
|
18
18
|
TinaState,
|
|
19
|
+
isSessionExpiredError,
|
|
19
20
|
resolveField,
|
|
20
21
|
useCMS,
|
|
21
22
|
} from 'tinacms';
|
|
@@ -24,7 +25,7 @@ import { FormifyCallback, createForm, createGlobalForm } from './build-form';
|
|
|
24
25
|
import { showErrorModal } from './errors';
|
|
25
26
|
import { expandQuery, isConnectionType, isNodeType } from './expand-query';
|
|
26
27
|
import {
|
|
27
|
-
|
|
28
|
+
getPreviewOrigin,
|
|
28
29
|
isFromTrustedPreviewOrigin,
|
|
29
30
|
postMessageToPreview,
|
|
30
31
|
} from './preview-origin';
|
|
@@ -204,12 +205,9 @@ export const useGraphQLReducer = (
|
|
|
204
205
|
|
|
205
206
|
const activeField = searchParams.get('active-field');
|
|
206
207
|
|
|
207
|
-
//
|
|
208
|
+
// The preview is always loaded from the admin's own origin. Used to validate
|
|
208
209
|
// inbound messages and as the explicit `targetOrigin` for outbound ones.
|
|
209
|
-
const expectedOrigin =
|
|
210
|
-
() => getExpectedPreviewOrigin(url),
|
|
211
|
-
[url]
|
|
212
|
-
);
|
|
210
|
+
const expectedOrigin = getPreviewOrigin();
|
|
213
211
|
|
|
214
212
|
React.useEffect(() => {
|
|
215
213
|
const run = async () => {
|
|
@@ -722,6 +720,8 @@ const onSubmit = async (
|
|
|
722
720
|
});
|
|
723
721
|
cms.alerts.success('Document saved!');
|
|
724
722
|
} catch (e) {
|
|
723
|
+
// request() already sent the user back to the login modal; no alert over it
|
|
724
|
+
if (isSessionExpiredError(e)) throw e;
|
|
725
725
|
cms.alerts.error(() =>
|
|
726
726
|
ErrorDialog({
|
|
727
727
|
title: 'There was a problem saving your document',
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
getPreviewOrigin,
|
|
4
4
|
isFromTrustedPreviewOrigin,
|
|
5
5
|
postMessageToPreview,
|
|
6
6
|
} from './preview-origin';
|
|
7
7
|
|
|
8
|
-
const ADMIN = 'https://admin.example';
|
|
9
8
|
const PREVIEW = 'https://preview.example';
|
|
10
9
|
|
|
11
10
|
// A stand-in for the iframe's content window; we only need an identity to
|
|
@@ -15,15 +14,23 @@ const makePeerWindow = () =>
|
|
|
15
14
|
postMessage: ReturnType<typeof vi.fn>;
|
|
16
15
|
};
|
|
17
16
|
|
|
18
|
-
describe('
|
|
19
|
-
it('
|
|
20
|
-
expect(
|
|
17
|
+
describe('getPreviewOrigin', () => {
|
|
18
|
+
it('is the origin the admin itself is served from', () => {
|
|
19
|
+
expect(getPreviewOrigin()).toBe(window.location.origin);
|
|
21
20
|
});
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The origin used to be resolved from the preview URL, so a URL naming
|
|
24
|
+
* another origin moved the trust anchor to it. The preview is always loaded
|
|
25
|
+
* from the admin's own origin, so nothing it carries is consulted here.
|
|
26
|
+
*/
|
|
27
|
+
it('takes no argument that could name another origin', () => {
|
|
28
|
+
expect(getPreviewOrigin.length).toBe(0);
|
|
29
|
+
expect(
|
|
30
|
+
(getPreviewOrigin as (...args: unknown[]) => string)(
|
|
31
|
+
`${PREVIEW}/posts/hello`
|
|
32
|
+
)
|
|
33
|
+
).toBe(window.location.origin);
|
|
27
34
|
});
|
|
28
35
|
});
|
|
29
36
|
|
|
@@ -7,23 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Returns the origin
|
|
10
|
+
* Returns the origin the preview iframe is allowed to be on.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* origin.
|
|
12
|
+
* The admin only ever loads the preview from its own origin, so this is that
|
|
13
|
+
* origin and nothing else. It is deliberately not derived from the preview URL:
|
|
14
|
+
* reading the trust anchor out of a value the URL supplies is what allowed a
|
|
15
|
+
* crafted link to nominate the origin it would then be trusted from.
|
|
14
16
|
*/
|
|
15
|
-
export const
|
|
16
|
-
|
|
17
|
-
baseOrigin: string = typeof window !== 'undefined'
|
|
18
|
-
? window.location.origin
|
|
19
|
-
: ''
|
|
20
|
-
): string => {
|
|
21
|
-
try {
|
|
22
|
-
return new URL(url, baseOrigin || undefined).origin;
|
|
23
|
-
} catch {
|
|
24
|
-
return baseOrigin;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
17
|
+
export const getPreviewOrigin = (): string =>
|
|
18
|
+
typeof window !== 'undefined' ? window.location.origin : '';
|
|
27
19
|
|
|
28
20
|
/**
|
|
29
21
|
* Checks whether a MessageEvent came from the trusted preview iframe.
|