mindspace: close authority boundaries
This commit is contained in:
+79
-1
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user