收紧桌面文件选择器过滤门禁
锁定 Tauri 文本、文档、图片和音频选择器扩展名边界 要求导入与导出文件对话框继续匹配 HostBridge 文件契约 同步桌面壳文件选择边界记录
This commit is contained in:
@@ -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
Reference in New Issue
Block a user