收紧移动壳文本导出校验顺序

移动壳文本导出先校验 MIME 再探测原生分享能力

移动壳文件测试覆盖非法文本 MIME 不触碰原生分享

移动壳配置检查锁定文本导出本地校验顺序
This commit is contained in:
2026-06-20 15:05:23 +08:00
parent 3788d0e02c
commit cbad531182
3 changed files with 35 additions and 2 deletions
@@ -2104,6 +2104,20 @@ if (
) {
throw new Error('mobile shell image export must reject empty and oversized bytes');
}
const exportTextFileBody = extractFunctionBody(filesSource, 'exportTextFile');
const exportTextMimeValidationIndex = exportTextFileBody.indexOf(
'HOST_BRIDGE_TEXT_MIME_TYPE_SET.has(mimeType as HostBridgeTextMimeType)',
);
const exportTextSharingAvailabilityIndex = exportTextFileBody.indexOf(
'await assertMobileFileSharingAvailable()',
);
if (
exportTextMimeValidationIndex < 0 ||
exportTextSharingAvailabilityIndex < 0 ||
exportTextMimeValidationIndex > exportTextSharingAvailabilityIndex
) {
throw new Error('mobile shell text export must validate MIME before native sharing checks');
}
for (const [wrapperName, fileCall] of [
['exportMobileHostBridgeTextFile', 'ok(request, await exportTextFile(request.payload))'],
@@ -2126,6 +2140,7 @@ for (const snippet of [
'exportTextFile({',
'exportImageFile({',
'rejects every export before cache writes when system sharing is unavailable',
'rejects invalid text export MIME before touching native sharing',
'const exportCases = [',
'expect(writtenFiles).toHaveLength(0)',
'importTextFile()',
@@ -185,6 +185,23 @@ describe('mobile HostBridge file actions', () => {
expect(shareAsyncMock).not.toHaveBeenCalled();
});
test('rejects invalid text export MIME before touching native sharing', async () => {
await expect(
exportTextFile({
content: '泥巴AI',
fileName: '创作记录',
mimeType: 'application/octet-stream',
}),
).rejects.toMatchObject({
code: 'invalid_request',
message: 'mimeType must be an allowed text type',
});
expect(shareAvailableMock).not.toHaveBeenCalled();
expect(writtenFiles).toHaveLength(0);
expect(shareAsyncMock).not.toHaveBeenCalled();
});
test('maps native sharing availability failures to stable export errors', async () => {
shareAvailableMock.mockRejectedValueOnce(new Error('expo sharing crashed'));
+3 -2
View File
@@ -109,8 +109,6 @@ export async function exportTextFile(
throw invalidRequest('content exceeds file export size limit');
}
await assertMobileFileSharingAvailable();
const fileName = normalizeHostBridgeExportFileName(exportPayload?.fileName);
const rawMimeType = exportPayload?.mimeType;
const mimeType =
@@ -120,6 +118,9 @@ export async function exportTextFile(
if (!HOST_BRIDGE_TEXT_MIME_TYPE_SET.has(mimeType as HostBridgeTextMimeType)) {
throw invalidRequest('mimeType must be an allowed text type');
}
await assertMobileFileSharingAvailable();
try {
const file = new File(Paths.cache, fileName);
file.write(content);