@xxxyz/dsh-mcp-manager 2.0.5 → 2.0.6
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/install.mjs +1 -1
- package/lib/client.js +4 -1
- package/lib/index.js +36 -0
- package/package.json +1 -1
package/install.mjs
CHANGED
|
@@ -273,7 +273,7 @@ async function pollApi(port, timeoutMs) {
|
|
|
273
273
|
try {
|
|
274
274
|
const res = await fetch(url, {
|
|
275
275
|
method: 'POST',
|
|
276
|
-
headers: { 'content-type': 'application/json' },
|
|
276
|
+
headers: { 'content-type': 'application/json', 'x-dsh-plugin': 'dsh-mcp-manager' },
|
|
277
277
|
body: JSON.stringify({ op: 'mcpm-list', args: {} }),
|
|
278
278
|
signal: timeoutSignal(2000),
|
|
279
279
|
})
|
package/lib/client.js
CHANGED
|
@@ -61,9 +61,12 @@ const factory = (require) => {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
function apiCall(op, args) {
|
|
64
|
+
// `x-dsh-plugin` is the cross-site (CSRF) gate header the host half
|
|
65
|
+
// requires on every request; a cross-origin page cannot attach it
|
|
66
|
+
// without a CORS preflight that this route never answers.
|
|
64
67
|
return fetch('/dsh-mcp-manager/api', {
|
|
65
68
|
method: 'POST',
|
|
66
|
-
headers: { 'content-type': 'application/json' },
|
|
69
|
+
headers: { 'content-type': 'application/json', 'x-dsh-plugin': 'dsh-mcp-manager' },
|
|
67
70
|
body: JSON.stringify({ op, args: args || {} }),
|
|
68
71
|
}).then((r) => r.json()).catch((e) => ({ ok: false, error: String((e && e.message) || e) }))
|
|
69
72
|
}
|
package/lib/index.js
CHANGED
|
@@ -936,6 +936,42 @@ export default {
|
|
|
936
936
|
kind: 'exact',
|
|
937
937
|
path: '/dsh-mcp-manager/api',
|
|
938
938
|
handler: async (req, res) => {
|
|
939
|
+
// Cross-site (CSRF) gate. This route mutates config files, so it
|
|
940
|
+
// must only be reachable from the DSH web UI (same origin) or
|
|
941
|
+
// local tooling. A browser cross-site request cannot attach a
|
|
942
|
+
// custom header without a CORS preflight, and this route never
|
|
943
|
+
// answers preflights — requiring `x-dsh-plugin` is the primary
|
|
944
|
+
// gate; POST-only and the Origin check are defense in depth.
|
|
945
|
+
const hdr = (name) => {
|
|
946
|
+
const v = req.headers?.[name];
|
|
947
|
+
return Array.isArray(v) ? v[0] ?? '' : v ?? '';
|
|
948
|
+
};
|
|
949
|
+
if (String(req.method || 'POST').toUpperCase() !== 'POST') {
|
|
950
|
+
res.writeHead(405, { 'content-type': 'application/json' });
|
|
951
|
+
res.end(JSON.stringify({ ok: false, error: 'method not allowed' }));
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
if (hdr('x-dsh-plugin') !== 'dsh-mcp-manager') {
|
|
955
|
+
res.writeHead(403, { 'content-type': 'application/json' });
|
|
956
|
+
res.end(JSON.stringify({ ok: false, error: 'missing plugin gate header' }));
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const origin = hdr('origin');
|
|
960
|
+
if (origin) {
|
|
961
|
+
let sameOrigin = false;
|
|
962
|
+
try {
|
|
963
|
+
const u = new URL(origin);
|
|
964
|
+
const hostHdr = hdr('host') || '';
|
|
965
|
+
sameOrigin =
|
|
966
|
+
/^(localhost|127\.0\.0\.1|\[::1\])$/i.test(u.hostname) || u.host === hostHdr;
|
|
967
|
+
}
|
|
968
|
+
catch (e) { /* unparseable origin → rejected below */ }
|
|
969
|
+
if (!sameOrigin) {
|
|
970
|
+
res.writeHead(403, { 'content-type': 'application/json' });
|
|
971
|
+
res.end(JSON.stringify({ ok: false, error: 'cross-origin request rejected' }));
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
939
975
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
940
976
|
try {
|
|
941
977
|
let payload = {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xxxyz/dsh-mcp-manager",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"description": "DSH-standard MCP manager plugin: Settings UI + HTTP API + model-facing mcp_manager_* tools. Composed as a loader entry, survives restarts. Install via npx, PowerShell, bash or node.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|