统一宿主壳目录边界
移动端壳拆分为 host-bridge 与 shell 目录 桌面端 Tauri 拆分为 host_bridge 与 shell Rust 模块 更新三端桥接层结构门禁与配置检查 同步宿主壳方案文档和项目共享决策
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import {
|
||||
resolveMobileShellExternalUrl,
|
||||
resolveMobileShellWebViewUrl,
|
||||
shouldAcceptMobileShellHostBridgeMessage,
|
||||
shouldOpenInMobileShellWebView,
|
||||
} from './mobileShellNavigation';
|
||||
|
||||
describe('shouldOpenInMobileShellWebView', () => {
|
||||
test('只允许主站同源页面留在移动壳 WebView 内', () => {
|
||||
const allowedOrigin = 'https://app.genarrative.world';
|
||||
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView(
|
||||
'https://app.genarrative.world/works/detail?work=PZ-1',
|
||||
allowedOrigin,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView('/creation/puzzle', allowedOrigin),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView(
|
||||
'http://app.genarrative.world/works/detail?work=PZ-1',
|
||||
allowedOrigin,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView('about:blank', allowedOrigin),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('外链和非网页协议必须离开带 HostBridge 的 WebView', () => {
|
||||
const allowedOrigin = 'https://app.genarrative.world';
|
||||
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView('https://example.com/', allowedOrigin),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView('mailto:hi@example.com', allowedOrigin),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView('//example.com/evil', allowedOrigin),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldOpenInMobileShellWebView('javascript:alert(1)', allowedOrigin),
|
||||
).toBe(false);
|
||||
expect(shouldOpenInMobileShellWebView('not a url', allowedOrigin)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('只有允许协议能交给系统外部应用打开', () => {
|
||||
expect(resolveMobileShellExternalUrl(' https://example.com/path ')).toBe(
|
||||
'https://example.com/path',
|
||||
);
|
||||
expect(resolveMobileShellExternalUrl('mailto:hi@example.com')).toBe(
|
||||
'mailto:hi@example.com',
|
||||
);
|
||||
expect(resolveMobileShellExternalUrl('tel:+12345678')).toBe(
|
||||
'tel:+12345678',
|
||||
);
|
||||
expect(resolveMobileShellExternalUrl('javascript:alert(1)')).toBeNull();
|
||||
expect(resolveMobileShellExternalUrl('file:///etc/passwd')).toBeNull();
|
||||
expect(resolveMobileShellExternalUrl('/relative/path')).toBeNull();
|
||||
});
|
||||
|
||||
test('HostBridge 主动导航只解析同源网页目标', () => {
|
||||
const allowedOrigin = 'https://app.genarrative.world';
|
||||
|
||||
expect(
|
||||
resolveMobileShellWebViewUrl('/works/detail?work=PZ-1', allowedOrigin),
|
||||
).toBe('https://app.genarrative.world/works/detail?work=PZ-1');
|
||||
expect(
|
||||
resolveMobileShellWebViewUrl(
|
||||
'https://app.genarrative.world/creation/puzzle#draft',
|
||||
allowedOrigin,
|
||||
),
|
||||
).toBe('https://app.genarrative.world/creation/puzzle#draft');
|
||||
expect(
|
||||
resolveMobileShellWebViewUrl('https://example.com/', allowedOrigin),
|
||||
).toBeNull();
|
||||
expect(
|
||||
resolveMobileShellWebViewUrl('about:blank', allowedOrigin),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
test('HostBridge 消息只接受同源主站页面', () => {
|
||||
const allowedOrigin = 'https://app.genarrative.world';
|
||||
|
||||
expect(
|
||||
shouldAcceptMobileShellHostBridgeMessage(
|
||||
'https://app.genarrative.world/creation/puzzle',
|
||||
allowedOrigin,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldAcceptMobileShellHostBridgeMessage('about:blank', allowedOrigin),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldAcceptMobileShellHostBridgeMessage(
|
||||
'https://example.com/evil',
|
||||
allowedOrigin,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldAcceptMobileShellHostBridgeMessage(
|
||||
'javascript:alert(1)',
|
||||
allowedOrigin,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user