收紧桌面文件选择器过滤门禁

锁定 Tauri 文本、文档、图片和音频选择器扩展名边界

要求导入与导出文件对话框继续匹配 HostBridge 文件契约

同步桌面壳文件选择边界记录
This commit is contained in:
2026-06-19 17:12:40 +08:00
parent db99448baa
commit 83be6c02d9
2 changed files with 98 additions and 0 deletions
@@ -879,6 +879,60 @@ function extractDesktopHandledMethods(source) {
];
}
function extractDesktopHostBridgeMethodBody(source, method) {
const methodStart = source.indexOf(`"${method}" => {`);
if (methodStart < 0) {
throw new Error(`desktop shell HostBridge missing method ${method}`);
}
const nextMethodStart = source.indexOf('\n "', methodStart + method.length);
return source.slice(
methodStart,
nextMethodStart > methodStart ? nextMethodStart : undefined,
);
}
function extractDesktopDialogFilter(source, method, filterLabel) {
const methodBody = extractDesktopHostBridgeMethodBody(source, method);
const match = methodBody.match(
new RegExp(`\\.add_filter\\("${filterLabel}",\\s*&\\[([^\\]]*)\\]\\)`),
);
if (!match) {
throw new Error(
`desktop shell ${method} must use a ${filterLabel} system dialog filter`,
);
}
return [...match[1].matchAll(/"([^"]+)"/g)].map((entry) => entry[1]);
}
function assertDesktopDialogBoundary(method, filterLabel, expected, action) {
const methodBody = extractDesktopHostBridgeMethodBody(
desktopHostBridgeDispatchSource,
method,
);
assertSameList(
extractDesktopDialogFilter(
desktopHostBridgeDispatchSource,
method,
filterLabel,
),
expected,
`desktop shell ${method} ${filterLabel} dialog filter`,
);
const requiredDialogAction =
action === 'save' ? '.blocking_save_file()' : '.blocking_pick_file()';
const blockedDialogAction =
action === 'save' ? '.blocking_pick_file()' : '.blocking_save_file()';
if (!methodBody.includes(requiredDialogAction)) {
throw new Error(`desktop shell ${method} must use ${requiredDialogAction}`);
}
if (methodBody.includes(blockedDialogAction)) {
throw new Error(`desktop shell ${method} must not use ${blockedDialogAction}`);
}
}
function assertNoRawHostContextUrl(urlValue, label) {
const rawUrl = String(urlValue ?? '');
const blockedQueryKeys = [
@@ -1965,6 +2019,49 @@ for (const staleTimeoutBoundary of [
}
}
assertDesktopDialogBoundary(
'file.exportText',
'Text',
['txt', 'json', 'md', 'csv'],
'save',
);
assertDesktopDialogBoundary(
'file.importText',
'Text',
['txt', 'md', 'markdown', 'csv', 'json'],
'pick',
);
assertDesktopDialogBoundary(
'file.importDocument',
'Document',
['txt', 'md', 'markdown', 'csv', 'json', 'docx'],
'pick',
);
assertDesktopDialogBoundary(
'file.exportImage',
'Image',
['png', 'jpg', 'jpeg', 'webp'],
'save',
);
assertDesktopDialogBoundary(
'file.importImage',
'Image',
['png', 'jpg', 'jpeg', 'webp'],
'pick',
);
assertDesktopDialogBoundary(
'file.importAudio',
'Audio',
['mp3', 'm4a', 'mp4', 'wav', 'ogg', 'webm'],
'pick',
);
assertDesktopDialogBoundary(
'file.exportAudio',
'Audio',
['mp3', 'm4a', 'wav', 'ogg', 'webm'],
'save',
);
assertSameList(
capability.windows ?? [],
['main'],
File diff suppressed because one or more lines are too long