@modelcontextprotocol/inspector 0.18.0 → 0.20.0
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/LICENSE +196 -1
- package/README.md +6 -1
- package/client/bin/start.js +14 -4
- package/client/dist/assets/{OAuthCallback-CXY8DEIM.js → OAuthCallback-CcWP_GMC.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-zr6INHin.js → OAuthDebugCallback-D0p8RB9e.js} +1 -1
- package/client/dist/assets/{index-Dw52pmVD.js → index-B8iPFz0O.js} +20169 -6820
- package/client/dist/assets/{index-DoQUGvvr.css → index-DdtP67NK.css} +59 -0
- package/client/dist/index.html +2 -2
- package/package.json +7 -7
- package/server/build/index.js +22 -0
- package/server/build/mcpProxy.js +3 -1
- package/server/build/static/sandbox_proxy.html +197 -0
|
@@ -863,10 +863,17 @@ video {
|
|
|
863
863
|
.row-auto {
|
|
864
864
|
grid-row: auto;
|
|
865
865
|
}
|
|
866
|
+
.m-0 {
|
|
867
|
+
margin: 0px;
|
|
868
|
+
}
|
|
866
869
|
.-mx-1 {
|
|
867
870
|
margin-left: -0.25rem;
|
|
868
871
|
margin-right: -0.25rem;
|
|
869
872
|
}
|
|
873
|
+
.mx-auto {
|
|
874
|
+
margin-left: auto;
|
|
875
|
+
margin-right: auto;
|
|
876
|
+
}
|
|
870
877
|
.my-1 {
|
|
871
878
|
margin-top: 0.25rem;
|
|
872
879
|
margin-bottom: 0.25rem;
|
|
@@ -953,6 +960,9 @@ video {
|
|
|
953
960
|
.h-10 {
|
|
954
961
|
height: 2.5rem;
|
|
955
962
|
}
|
|
963
|
+
.h-12 {
|
|
964
|
+
height: 3rem;
|
|
965
|
+
}
|
|
956
966
|
.h-2 {
|
|
957
967
|
height: 0.5rem;
|
|
958
968
|
}
|
|
@@ -980,6 +990,9 @@ video {
|
|
|
980
990
|
.h-96 {
|
|
981
991
|
height: 24rem;
|
|
982
992
|
}
|
|
993
|
+
.h-\[600px\] {
|
|
994
|
+
height: 600px;
|
|
995
|
+
}
|
|
983
996
|
.h-\[var\(--radix-select-trigger-height\)\] {
|
|
984
997
|
height: var(--radix-select-trigger-height);
|
|
985
998
|
}
|
|
@@ -1016,6 +1029,12 @@ video {
|
|
|
1016
1029
|
.min-h-\[60px\] {
|
|
1017
1030
|
min-height: 60px;
|
|
1018
1031
|
}
|
|
1032
|
+
.w-1\/3 {
|
|
1033
|
+
width: 33.333333%;
|
|
1034
|
+
}
|
|
1035
|
+
.w-12 {
|
|
1036
|
+
width: 3rem;
|
|
1037
|
+
}
|
|
1019
1038
|
.w-2 {
|
|
1020
1039
|
width: 0.5rem;
|
|
1021
1040
|
}
|
|
@@ -1061,6 +1080,9 @@ video {
|
|
|
1061
1080
|
.min-w-\[var\(--radix-select-trigger-width\)\] {
|
|
1062
1081
|
min-width: var(--radix-select-trigger-width);
|
|
1063
1082
|
}
|
|
1083
|
+
.max-w-\[200px\] {
|
|
1084
|
+
max-width: 200px;
|
|
1085
|
+
}
|
|
1064
1086
|
.max-w-full {
|
|
1065
1087
|
max-width: 100%;
|
|
1066
1088
|
}
|
|
@@ -1100,6 +1122,15 @@ video {
|
|
|
1100
1122
|
.transform {
|
|
1101
1123
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1102
1124
|
}
|
|
1125
|
+
@keyframes pulse {
|
|
1126
|
+
|
|
1127
|
+
50% {
|
|
1128
|
+
opacity: .5;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
.animate-pulse {
|
|
1132
|
+
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
1133
|
+
}
|
|
1103
1134
|
@keyframes spin {
|
|
1104
1135
|
|
|
1105
1136
|
to {
|
|
@@ -1126,6 +1157,9 @@ video {
|
|
|
1126
1157
|
.resize-none {
|
|
1127
1158
|
resize: none;
|
|
1128
1159
|
}
|
|
1160
|
+
.grid-cols-1 {
|
|
1161
|
+
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
1162
|
+
}
|
|
1129
1163
|
.grid-cols-2 {
|
|
1130
1164
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1131
1165
|
}
|
|
@@ -1375,6 +1409,9 @@ video {
|
|
|
1375
1409
|
.bg-muted {
|
|
1376
1410
|
background-color: hsl(var(--muted));
|
|
1377
1411
|
}
|
|
1412
|
+
.bg-muted\/30 {
|
|
1413
|
+
background-color: hsl(var(--muted) / 0.3);
|
|
1414
|
+
}
|
|
1378
1415
|
.bg-popover {
|
|
1379
1416
|
background-color: hsl(var(--popover));
|
|
1380
1417
|
}
|
|
@@ -1465,6 +1502,10 @@ video {
|
|
|
1465
1502
|
padding-top: 0.375rem;
|
|
1466
1503
|
padding-bottom: 0.375rem;
|
|
1467
1504
|
}
|
|
1505
|
+
.py-12 {
|
|
1506
|
+
padding-top: 3rem;
|
|
1507
|
+
padding-bottom: 3rem;
|
|
1508
|
+
}
|
|
1468
1509
|
.py-2 {
|
|
1469
1510
|
padding-top: 0.5rem;
|
|
1470
1511
|
padding-bottom: 0.5rem;
|
|
@@ -1534,6 +1575,9 @@ video {
|
|
|
1534
1575
|
.font-semibold {
|
|
1535
1576
|
font-weight: 600;
|
|
1536
1577
|
}
|
|
1578
|
+
.capitalize {
|
|
1579
|
+
text-transform: capitalize;
|
|
1580
|
+
}
|
|
1537
1581
|
.italic {
|
|
1538
1582
|
font-style: italic;
|
|
1539
1583
|
}
|
|
@@ -1667,6 +1711,14 @@ video {
|
|
|
1667
1711
|
--tw-text-opacity: 1;
|
|
1668
1712
|
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
|
|
1669
1713
|
}
|
|
1714
|
+
.text-yellow-500 {
|
|
1715
|
+
--tw-text-opacity: 1;
|
|
1716
|
+
color: rgb(234 179 8 / var(--tw-text-opacity, 1));
|
|
1717
|
+
}
|
|
1718
|
+
.text-yellow-600 {
|
|
1719
|
+
--tw-text-opacity: 1;
|
|
1720
|
+
color: rgb(202 138 4 / var(--tw-text-opacity, 1));
|
|
1721
|
+
}
|
|
1670
1722
|
.text-yellow-800 {
|
|
1671
1723
|
--tw-text-opacity: 1;
|
|
1672
1724
|
color: rgb(133 77 14 / var(--tw-text-opacity, 1));
|
|
@@ -1680,6 +1732,9 @@ video {
|
|
|
1680
1732
|
.opacity-100 {
|
|
1681
1733
|
opacity: 1;
|
|
1682
1734
|
}
|
|
1735
|
+
.opacity-20 {
|
|
1736
|
+
opacity: 0.2;
|
|
1737
|
+
}
|
|
1683
1738
|
.opacity-50 {
|
|
1684
1739
|
opacity: 0.5;
|
|
1685
1740
|
}
|
|
@@ -2519,6 +2574,10 @@ h1 {
|
|
|
2519
2574
|
.md\:max-w-\[420px\] {
|
|
2520
2575
|
max-width: 420px;
|
|
2521
2576
|
}
|
|
2577
|
+
|
|
2578
|
+
.md\:grid-cols-2 {
|
|
2579
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
2580
|
+
}
|
|
2522
2581
|
}
|
|
2523
2582
|
|
|
2524
2583
|
.\[\&\+div\]\:text-xs+div {
|
package/client/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/mcp.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Inspector</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-B8iPFz0O.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DdtP67NK.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root" class="w-full"></div>
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelcontextprotocol/inspector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Model Context Protocol inspector",
|
|
5
|
-
"license": "
|
|
6
|
-
"author": "
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"author": "Model Context Protocol a Series of LF Projects, LLC.",
|
|
7
7
|
"homepage": "https://modelcontextprotocol.io",
|
|
8
8
|
"bugs": "https://github.com/modelcontextprotocol/inspector/issues",
|
|
9
9
|
"type": "module",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"check-version": "node scripts/check-version-consistency.js"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@modelcontextprotocol/inspector-cli": "^0.
|
|
51
|
-
"@modelcontextprotocol/inspector-client": "^0.
|
|
52
|
-
"@modelcontextprotocol/inspector-server": "^0.
|
|
53
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
50
|
+
"@modelcontextprotocol/inspector-cli": "^0.20.0",
|
|
51
|
+
"@modelcontextprotocol/inspector-client": "^0.20.0",
|
|
52
|
+
"@modelcontextprotocol/inspector-server": "^0.20.0",
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
54
54
|
"concurrently": "^9.2.0",
|
|
55
55
|
"node-fetch": "^3.3.2",
|
|
56
56
|
"open": "^10.2.0",
|
package/server/build/index.js
CHANGED
|
@@ -12,10 +12,18 @@ import { StreamableHTTPClientTransport, StreamableHTTPError, } from "@modelconte
|
|
|
12
12
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
13
13
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
14
14
|
import express from "express";
|
|
15
|
+
import rateLimit from "express-rate-limit";
|
|
15
16
|
import { findActualExecutable } from "spawn-rx";
|
|
16
17
|
import mcpProxy from "./mcpProxy.js";
|
|
17
18
|
import { randomUUID, randomBytes, timingSafeEqual } from "node:crypto";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
import { dirname, join } from "path";
|
|
21
|
+
import { readFileSync } from "fs";
|
|
18
22
|
const DEFAULT_MCP_PROXY_LISTEN_PORT = "6277";
|
|
23
|
+
const sandboxRateLimiter = rateLimit({
|
|
24
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
25
|
+
max: 100, // limit each IP to 100 /sandbox requests per windowMs
|
|
26
|
+
});
|
|
19
27
|
const defaultEnvironment = {
|
|
20
28
|
...getDefaultEnvironment(),
|
|
21
29
|
...(process.env.MCP_ENV_VARS ? JSON.parse(process.env.MCP_ENV_VARS) : {}),
|
|
@@ -625,6 +633,20 @@ app.get("/config", originValidationMiddleware, authMiddleware, (req, res) => {
|
|
|
625
633
|
res.status(500).json(error);
|
|
626
634
|
}
|
|
627
635
|
});
|
|
636
|
+
app.get("/sandbox", sandboxRateLimiter, (req, res) => {
|
|
637
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
638
|
+
const __dirname = dirname(__filename);
|
|
639
|
+
const filePath = join(__dirname, "..", "static", "sandbox_proxy.html");
|
|
640
|
+
let sandboxHtml;
|
|
641
|
+
try {
|
|
642
|
+
sandboxHtml = readFileSync(filePath, "utf-8");
|
|
643
|
+
}
|
|
644
|
+
catch (e) {
|
|
645
|
+
sandboxHtml = "MCP Apps sandbox not loaded: " + e;
|
|
646
|
+
}
|
|
647
|
+
res.set("Cache-Control", "no-cache, no-store, max-age=0");
|
|
648
|
+
res.send(sandboxHtml);
|
|
649
|
+
});
|
|
628
650
|
const PORT = parseInt(process.env.SERVER_PORT || DEFAULT_MCP_PROXY_LISTEN_PORT, 10);
|
|
629
651
|
const HOST = process.env.HOST || "localhost";
|
|
630
652
|
const server = app.listen(PORT, HOST);
|
package/server/build/mcpProxy.js
CHANGED
|
@@ -26,7 +26,9 @@ export default function mcpProxy({ transportToClient, transportToServer, }) {
|
|
|
26
26
|
id: message.id,
|
|
27
27
|
error: {
|
|
28
28
|
code: -32001,
|
|
29
|
-
message: error.
|
|
29
|
+
message: error.cause
|
|
30
|
+
? `${error.message} (cause: ${error.cause})`
|
|
31
|
+
: error.message,
|
|
30
32
|
data: error,
|
|
31
33
|
},
|
|
32
34
|
};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="color-scheme" content="light dark" />
|
|
6
|
+
<!-- CSP is set via HTTP headers by serve.ts (based on ?csp= query param).
|
|
7
|
+
The inner iframe inherits this CSP since we use document.write(). -->
|
|
8
|
+
<title>MCP-UI Proxy</title>
|
|
9
|
+
<style>
|
|
10
|
+
html,
|
|
11
|
+
body {
|
|
12
|
+
margin: 0;
|
|
13
|
+
height: 100vh;
|
|
14
|
+
width: 100vw;
|
|
15
|
+
/* Transparent background allows parent page to show through */
|
|
16
|
+
background-color: transparent;
|
|
17
|
+
}
|
|
18
|
+
body {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
}
|
|
22
|
+
* {
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
}
|
|
25
|
+
iframe {
|
|
26
|
+
background-color: transparent;
|
|
27
|
+
border: 0px none transparent;
|
|
28
|
+
padding: 0px;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
flex-grow: 1;
|
|
31
|
+
/* Inherit color scheme from parent for consistent transparency */
|
|
32
|
+
color-scheme: inherit;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
</head>
|
|
36
|
+
<body>
|
|
37
|
+
<script>
|
|
38
|
+
function buildAllowAttribute(permissions) {
|
|
39
|
+
if (!permissions) return "";
|
|
40
|
+
|
|
41
|
+
const allowList = [];
|
|
42
|
+
if (permissions["camera"]) allowList.push("camera");
|
|
43
|
+
if (permissions["microphone"]) allowList.push("microphone");
|
|
44
|
+
if (permissions["geolocation"]) allowList.push("geolocation");
|
|
45
|
+
if (permissions["clipboardWrite"]) allowList.push("clipboard-write");
|
|
46
|
+
|
|
47
|
+
return allowList.join("; ");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const ALLOWED_REFERRER_PATTERN =
|
|
51
|
+
/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/;
|
|
52
|
+
|
|
53
|
+
if (window.self === window.top) {
|
|
54
|
+
throw new Error("This file is only to be used in an iframe sandbox.");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!document.referrer) {
|
|
58
|
+
throw new Error("No referrer, cannot validate embedding site.");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!document.referrer.match(ALLOWED_REFERRER_PATTERN)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Embedding domain not allowed in referrer ${document.referrer}. (Consider updating the validation logic to allow your domain.)`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Extract the expected host origin from the referrer for origin validation.
|
|
68
|
+
// This is the origin we expect all parent messages to come from.
|
|
69
|
+
const EXPECTED_HOST_ORIGIN = new URL(document.referrer).origin;
|
|
70
|
+
|
|
71
|
+
const OWN_ORIGIN = new URL(window.location.href).origin;
|
|
72
|
+
|
|
73
|
+
// Security self-test: verify iframe isolation is working correctly.
|
|
74
|
+
// This MUST throw a SecurityError -- if `window.top` is accessible, the sandbox
|
|
75
|
+
// configuration is dangerously broken and untrusted content could escape.
|
|
76
|
+
try {
|
|
77
|
+
window.top?.alert(
|
|
78
|
+
"If you see this, the sandbox is not setup securely.",
|
|
79
|
+
);
|
|
80
|
+
throw "FAIL";
|
|
81
|
+
} catch (e) {
|
|
82
|
+
if (e === "FAIL") {
|
|
83
|
+
throw new Error("The sandbox is not setup securely.");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Expected: SecurityError confirms proper sandboxing.
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Double-iframe sandbox architecture: THIS file is the outer sandbox proxy
|
|
90
|
+
// iframe on a separate origin. It creates an inner iframe for untrusted HTML
|
|
91
|
+
// content. Per the specification, the Host and the Sandbox MUST have different
|
|
92
|
+
// origins.
|
|
93
|
+
const inner = document.createElement("iframe");
|
|
94
|
+
inner.style = "width:100%; height:100%; border:none;";
|
|
95
|
+
inner.setAttribute(
|
|
96
|
+
"sandbox",
|
|
97
|
+
"allow-scripts allow-same-origin allow-forms",
|
|
98
|
+
);
|
|
99
|
+
// Note: allow attribute is set later when receiving sandbox-resource-ready notification
|
|
100
|
+
// based on the permissions requested by the app
|
|
101
|
+
document.body.appendChild(inner);
|
|
102
|
+
|
|
103
|
+
const RESOURCE_READY_NOTIFICATION =
|
|
104
|
+
"ui/notifications/sandbox-resource-ready";
|
|
105
|
+
const PROXY_READY_NOTIFICATION = "ui/notifications/sandbox-proxy-ready";
|
|
106
|
+
|
|
107
|
+
// Message relay: This Sandbox (outer iframe) acts as a bidirectional bridge,
|
|
108
|
+
// forwarding messages between:
|
|
109
|
+
//
|
|
110
|
+
// Host (parent window) ↔ Sandbox (outer frame) ↔ View (inner iframe)
|
|
111
|
+
//
|
|
112
|
+
// Reason: the parent window and inner iframe have different origins and can't
|
|
113
|
+
// communicate directly, so the outer iframe forwards messages in both
|
|
114
|
+
// directions to connect them.
|
|
115
|
+
//
|
|
116
|
+
// Special case: The "ui/notifications/sandbox-proxy-ready" message is
|
|
117
|
+
// intercepted here (not relayed) because the Sandbox uses it to configure and
|
|
118
|
+
// load the inner iframe with the view HTML content.
|
|
119
|
+
//
|
|
120
|
+
// Security: CSP is enforced via HTTP headers on sandbox.html (set by serve.ts
|
|
121
|
+
// based on ?csp= query param). This is tamper-proof unlike meta tags.
|
|
122
|
+
|
|
123
|
+
window.addEventListener("message", async (event) => {
|
|
124
|
+
if (event.source === window.parent) {
|
|
125
|
+
// Validate that messages from parent come from the expected host origin.
|
|
126
|
+
// This prevents malicious pages from sending messages to this sandbox.
|
|
127
|
+
if (event.origin !== EXPECTED_HOST_ORIGIN) {
|
|
128
|
+
console.error(
|
|
129
|
+
"[Sandbox] Rejecting message from unexpected origin:",
|
|
130
|
+
event.origin,
|
|
131
|
+
"expected:",
|
|
132
|
+
EXPECTED_HOST_ORIGIN,
|
|
133
|
+
);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (event.data && event.data.method === RESOURCE_READY_NOTIFICATION) {
|
|
138
|
+
const { html, sandbox, permissions } = event.data.params;
|
|
139
|
+
if (typeof sandbox === "string") {
|
|
140
|
+
inner.setAttribute("sandbox", sandbox);
|
|
141
|
+
}
|
|
142
|
+
// Set Permission Policy allow attribute if permissions are requested
|
|
143
|
+
const allowAttribute = buildAllowAttribute(permissions);
|
|
144
|
+
if (allowAttribute) {
|
|
145
|
+
console.log("[Sandbox] Setting allow attribute:", allowAttribute);
|
|
146
|
+
inner.setAttribute("allow", allowAttribute);
|
|
147
|
+
}
|
|
148
|
+
if (typeof html === "string") {
|
|
149
|
+
// Use document.write instead of srcdoc (which the CesiumJS Map won't work with)
|
|
150
|
+
const doc =
|
|
151
|
+
inner.contentDocument || inner.contentWindow?.document;
|
|
152
|
+
if (doc) {
|
|
153
|
+
doc.open();
|
|
154
|
+
doc.write(html);
|
|
155
|
+
doc.close();
|
|
156
|
+
} else {
|
|
157
|
+
// Fallback to srcdoc if document is not accessible
|
|
158
|
+
console.warn(
|
|
159
|
+
"[Sandbox] document.write not available, falling back to srcdoc",
|
|
160
|
+
);
|
|
161
|
+
inner.srcdoc = html;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
if (inner && inner.contentWindow) {
|
|
166
|
+
inner.contentWindow.postMessage(event.data, "*");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} else if (event.source === inner.contentWindow) {
|
|
170
|
+
if (event.origin !== OWN_ORIGIN) {
|
|
171
|
+
console.error(
|
|
172
|
+
"[Sandbox] Rejecting message from inner iframe with unexpected origin:",
|
|
173
|
+
event.origin,
|
|
174
|
+
"expected:",
|
|
175
|
+
OWN_ORIGIN,
|
|
176
|
+
);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Relay messages from inner frame to parent window.
|
|
180
|
+
// Use specific origin instead of "*" to prevent message interception.
|
|
181
|
+
window.parent.postMessage(event.data, EXPECTED_HOST_ORIGIN);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Notify the Host that the Sandbox is ready to receive view HTML.
|
|
186
|
+
// Use specific origin instead of "*" to ensure only the expected host receives this.
|
|
187
|
+
window.parent.postMessage(
|
|
188
|
+
{
|
|
189
|
+
jsonrpc: "2.0",
|
|
190
|
+
method: PROXY_READY_NOTIFICATION,
|
|
191
|
+
params: {},
|
|
192
|
+
},
|
|
193
|
+
EXPECTED_HOST_ORIGIN,
|
|
194
|
+
);
|
|
195
|
+
</script>
|
|
196
|
+
</body>
|
|
197
|
+
</html>
|