mindspace: close authority boundaries

This commit is contained in:
john
2026-07-27 15:34:35 +08:00
parent dfab78c75a
commit e94052ff24
78 changed files with 11962 additions and 2162 deletions
+79 -1
View File
@@ -1,7 +1,21 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
mintMindSpaceMcpScopedToken,
} from './mindspace-mcp-scoped-token.mjs';
import { resolveAgentGooseMode } from './policies.mjs';
const SCOPED_WORKSPACE_MCP_TOOLS =
new Set([
'create_dir',
'edit_file',
'generate_long_image',
'list_dir',
'publish_page',
'read_file',
'write_file',
]);
function resolveBundledMcpServerPath(filename, overridePath, runtimeRoot) {
const normalized = String(overridePath ?? '').trim();
if (normalized) return normalized;
@@ -425,6 +439,7 @@ export function sandboxMcpTools(capabilities) {
'write_file',
'edit_file',
'create_dir',
'publish_page',
'generate_image',
'generate_docx',
'generate_long_image',
@@ -483,6 +498,57 @@ function sandboxMcpEnvs(sandboxMcp, mcpTools) {
if (compatRoot) envs.SANDBOX_ROOT = compatRoot;
if (sandboxMcp.workspaceRoot || localRoot) envs.MINDSPACE_WORKSPACE_ROOT = sandboxMcp.workspaceRoot || localRoot;
if (sandboxMcp.workspaceRef) envs.MINDSPACE_WORKSPACE_REF = sandboxMcp.workspaceRef;
const scopedTools = [
...new Set([
...mcpTools.filter(
(tool) =>
SCOPED_WORKSPACE_MCP_TOOLS.has(
tool,
),
),
...(mcpTools.includes('generate_docx')
? ['write_binary_file']
: []),
]),
];
if (
sandboxMcp.mcpBaseUrl &&
sandboxMcp.mcpTokenSecret &&
sandboxMcp.workspaceRef &&
sandboxMcp.sessionId &&
sandboxMcp.packageId &&
scopedTools.length > 0
) {
const tokenBucketMs = 30 * 60 * 1000;
const tokenNow =
Math.floor(Date.now() / tokenBucketMs) *
tokenBucketMs;
envs.MINDSPACE_MCP_BASE_URL =
String(sandboxMcp.mcpBaseUrl)
.trim()
.replace(/\/+$/, '');
envs.MINDSPACE_MCP_SCOPED_TOKEN =
mintMindSpaceMcpScopedToken({
secret:
sandboxMcp.mcpTokenSecret,
userId: sandboxMcp.userId,
sessionId:
sandboxMcp.sessionId,
packageId:
sandboxMcp.packageId,
workspaceRef:
sandboxMcp.workspaceRef,
tools: scopedTools,
ttlSeconds: 60 * 60,
now: tokenNow,
tokenId:
`${sandboxMcp.sessionId}:${tokenNow}`,
});
envs.MINDSPACE_SESSION_ID =
String(sandboxMcp.sessionId);
envs.MINDSPACE_PACKAGE_ID =
String(sandboxMcp.packageId);
}
if (sandboxMcp.userId) envs.PRIVATE_DATA_USER_ID = sandboxMcp.userId;
for (const [key, value] of [
['H5_PUBLIC_BASE_URL', sandboxMcp.publicBaseUrl],
@@ -566,7 +632,19 @@ export function buildAgentExtensionPolicy(
if (sandboxMcp?.serverPath && localRoot) {
// Sandboxed stdio MCP: enforces SANDBOX_ROOT at the OS level.
// Replaces the built-in developer extension so path traversal is impossible.
const mcpTools = sandboxMcpTools(capabilities);
const scopedWorkspaceReady = Boolean(
sandboxMcp.mcpBaseUrl &&
sandboxMcp.mcpTokenSecret &&
sandboxMcp.workspaceRef &&
sandboxMcp.sessionId &&
sandboxMcp.packageId,
);
const mcpTools =
sandboxMcpTools(capabilities).filter(
(tool) =>
tool !== 'publish_page' ||
scopedWorkspaceReady,
);
if (mcpTools.length > 0) {
extensions.push({
type: 'stdio',