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