收口原生壳剪贴板读取边界
H5 HostBridge 读取剪贴板时将畸形原生返回统一收口为 false 补充原生 App 剪贴板读取归一化回归测试 原生壳门禁反查 clipboard.readText 返回值必须经过共享剪贴板边界
This commit is contained in:
@@ -1165,6 +1165,21 @@ function assertH5HostBridgePayloadBoundaries() {
|
||||
'H5 HostBridge facade must normalize clipboard.writeText payloads with the shared clipboard boundary',
|
||||
);
|
||||
}
|
||||
if (
|
||||
!h5HostBridgeSource.includes(
|
||||
'const clipboardText = normalizeHostBridgeClipboardText(',
|
||||
) ||
|
||||
!h5HostBridgeSource.includes(
|
||||
"await requestNativeAppHostBridge<ClipboardReadTextResult>(\n 'clipboard.readText',",
|
||||
) ||
|
||||
!h5HostBridgeSource.includes(
|
||||
'return clipboardText ?? false;',
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
'H5 HostBridge facade must normalize clipboard.readText results with the shared clipboard boundary',
|
||||
);
|
||||
}
|
||||
if (
|
||||
!h5HostBridgeSource.includes(
|
||||
'const style = normalizeHostBridgeHapticsImpactStyle(params.style);',
|
||||
|
||||
@@ -4,6 +4,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import type { HostBridgeCapability } from '../../../packages/shared/src/contracts/hostBridge';
|
||||
import {
|
||||
HOST_BRIDGE_CLIPBOARD_TEXT_MAX_LENGTH,
|
||||
HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS,
|
||||
HOST_BRIDGE_SCANNER_TIMEOUT_MS,
|
||||
HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS,
|
||||
@@ -1485,6 +1486,42 @@ describe('hostBridge', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('原生 App HostBridge 读取剪贴板结果按共享清单归一化', async () => {
|
||||
const oversizedText = 'a'.repeat(HOST_BRIDGE_CLIPBOARD_TEXT_MAX_LENGTH + 5);
|
||||
const invoke = vi.fn(
|
||||
async (_command: string, args?: Record<string, unknown>) => {
|
||||
const request = (args as { request: { id: string } }).request;
|
||||
return {
|
||||
bridge: 'GenarrativeHostBridge',
|
||||
version: 1,
|
||||
id: request.id,
|
||||
ok: true,
|
||||
result: {
|
||||
text:
|
||||
invoke.mock.calls.length === 1
|
||||
? 42
|
||||
: oversizedText,
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
window.__TAURI__ = {
|
||||
core: { invoke: asTauriInvoke(invoke) },
|
||||
};
|
||||
window.history.replaceState(
|
||||
null,
|
||||
'',
|
||||
nativeAppPath(['clipboard.readText']),
|
||||
);
|
||||
|
||||
await expect(readHostClipboardText()).resolves.toBe(false);
|
||||
await expect(readHostClipboardText()).resolves.toEqual({
|
||||
text: oversizedText.slice(0, HOST_BRIDGE_CLIPBOARD_TEXT_MAX_LENGTH),
|
||||
});
|
||||
|
||||
expect(invoke).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('原生 App 宿主不支持能力时回退到 H5 路径', async () => {
|
||||
window.history.replaceState(null, '', nativeAppPath());
|
||||
window.__TAURI__ = {
|
||||
|
||||
@@ -889,13 +889,14 @@ export async function readHostClipboardText() {
|
||||
}
|
||||
|
||||
try {
|
||||
return normalizeHostBridgeClipboardText(
|
||||
const clipboardText = normalizeHostBridgeClipboardText(
|
||||
(
|
||||
await requestNativeAppHostBridge<ClipboardReadTextResult>(
|
||||
'clipboard.readText',
|
||||
)
|
||||
)?.text,
|
||||
);
|
||||
return clipboardText ?? false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user