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
+37 -119
View File
@@ -3,13 +3,6 @@ import fs from 'node:fs';
import path from 'node:path';
import { createAgentRunGateway } from '../agent-run-gateway.mjs';
import { isDirectChatSessionId } from '../direct-chat-service.mjs';
import { scanWorkspaceFilesForProhibitedBrowserStorage } from '../mindspace-browser-storage-policy.mjs';
import { evaluatePageDataHtmlContent } from '../mindspace-page-data-finish-guard.mjs';
import { normalizeWorkspaceRelativePath } from '../mindspace-pages.mjs';
import { resolveMindSpaceUserPublishDir } from '../mindspace-runtime-config.mjs';
import { policyAllowsAction } from '../page-access-policy.mjs';
import { detectPageDataDatasetUsageFromHtml } from '../page-data-html-detect.mjs';
import { readPageAccessPolicy } from '../page-data-policy-store.mjs';
import {
cancelSessionActiveRequest,
quiesceSessionStdioExtensions,
@@ -91,115 +84,35 @@ export function startPortalAgentRunRecoveryLoop(
}
export function createPortalRunDeliverablesValidator({
h5Root,
resolveMindSpaceUserPublishDirFn =
resolveMindSpaceUserPublishDir,
normalizeWorkspaceRelativePathFn =
normalizeWorkspaceRelativePath,
evaluatePageDataHtmlContentFn =
evaluatePageDataHtmlContent,
readPageAccessPolicyFn = readPageAccessPolicy,
detectPageDataDatasetUsageFromHtmlFn =
detectPageDataDatasetUsageFromHtml,
policyAllowsActionFn = policyAllowsAction,
scanWorkspaceFilesForProhibitedBrowserStorageFn =
scanWorkspaceFilesForProhibitedBrowserStorage,
resolvePathFn = path.resolve,
pathSeparator = path.sep,
existsSyncFn = fs.existsSync,
readFileSyncFn = fs.readFileSync,
getWorkspacePublicationDelivery = () =>
null,
} = {}) {
if (!h5Root) {
if (
typeof getWorkspacePublicationDelivery !==
'function'
) {
throw new Error(
'createPortalRunDeliverablesValidator requires h5Root',
'createPortalRunDeliverablesValidator requires MindSpace delivery access',
);
}
return async function validateRunDeliverables({
userId,
deliverables,
}) {
const publishDir =
resolveMindSpaceUserPublishDirFn(h5Root, {
id: userId,
});
const resolvedPublishDir =
resolvePathFn(publishDir);
const pageDataErrors = [];
for (const page of deliverables?.pages ?? []) {
const relativePath =
normalizeWorkspaceRelativePathFn(
page.workspaceRelativePath,
);
if (!relativePath?.startsWith('public/')) continue;
const filePath = resolvePathFn(
publishDir,
relativePath,
return async function validateRunDeliverables(
input,
) {
const service =
getWorkspacePublicationDelivery();
if (
!service ||
typeof service.validateRunDeliverables !==
'function'
) {
throw new Error(
'MindSpace 交付验证服务未启用',
);
if (
!filePath.startsWith(
`${resolvedPublishDir}${pathSeparator}`,
) ||
!existsSyncFn(filePath)
) {
continue;
}
const html = readFileSyncFn(filePath, 'utf8');
const evaluation =
evaluatePageDataHtmlContentFn(html, {
relativePath,
});
if (!evaluation.usesPageDataApi) continue;
for (const issue of evaluation.issues) {
pageDataErrors.push({
code: issue,
message: `${relativePath} Page Data HTML 不可交付:${issue}`,
});
}
const policy = page.pageId
? readPageAccessPolicyFn(
publishDir,
page.pageId,
)
: null;
for (const [dataset, actions] of
detectPageDataDatasetUsageFromHtmlFn(html)) {
for (const action of ['read', 'insert']) {
if (
actions?.[action] &&
!policyAllowsActionFn(
policy,
dataset,
action,
)
) {
pageDataErrors.push({
code: 'page_data_policy_action_missing',
message: `${relativePath}${dataset}.${action} 未获最终 policy 授权或 dataset 已关闭`,
});
}
}
}
}
const violations =
scanWorkspaceFilesForProhibitedBrowserStorageFn({
publishDir,
relativePaths: (deliverables?.pages ?? [])
.map((page) => page.workspaceRelativePath)
.filter(Boolean),
});
return {
errors: [
...pageDataErrors,
...violations.map((violation) => ({
code: 'browser_storage_forbidden',
message: `${violation.relativePath} 使用 ${violation.apis.join(', ')}`,
})),
],
};
return service.validateRunDeliverables(
input,
);
};
}
@@ -220,6 +133,8 @@ export function bootstrapPortalGatewayServices({
memoryV2,
systemDisclosurePolicyService,
mindSpaceAssets,
getWorkspacePublicationDelivery = () =>
null,
directChatService,
chatIntentRouter,
syncUserGeneratedPages,
@@ -235,7 +150,6 @@ export function bootstrapPortalGatewayServices({
createPortalRunDeliverablesValidator,
startAgentRunRecoveryLoopFn =
startPortalAgentRunRecoveryLoop,
readAssetFileFn = fs.promises.readFile,
} = {}) {
if (
!pool ||
@@ -267,15 +181,17 @@ export function bootstrapPortalGatewayServices({
systemDisclosurePolicyService,
localFetchAsset: mindSpaceAssets
? async (userId, assetId) => {
const { asset, path: assetPath } =
await mindSpaceAssets.readAsset(
userId,
assetId,
);
const buffer =
await readAssetFileFn(assetPath);
const { asset, bodyBase64 } =
await mindSpaceAssets
.readAssetContent(
userId,
assetId,
);
return {
buffer,
buffer: Buffer.from(
bodyBase64,
'base64',
),
mimeType: asset.mimeType,
};
}
@@ -285,7 +201,9 @@ export function bootstrapPortalGatewayServices({
llmProviderService,
});
const validateRunDeliverables =
createRunDeliverablesValidatorFn({ h5Root });
createRunDeliverablesValidatorFn({
getWorkspacePublicationDelivery,
});
const workflowShadowEnabled = isEnabledFlag(
env.MEMIND_ORCHESTRATOR_SHADOW_OBSERVATION_ENABLED,
);