bump version to 2.9.5; make webview tests async (#1501)

This commit is contained in:
Justin Hernandez
2025-12-14 12:16:48 -08:00
committed by GitHub
parent 529b15f382
commit c66a1b17f9
5 changed files with 52 additions and 52 deletions

View File

@@ -135,7 +135,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 121
versionName "2.9.4"
versionName "2.9.5"
manifestPlaceholders = [appAuthRedirectScheme: 'com.proofofpassportapp']
externalNativeBuild {
cmake {

View File

@@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.9.4</string>
<string>2.9.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@@ -546,7 +546,7 @@
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/MoproKit/Libs",
);
MARKETING_VERSION = 2.9.4;
MARKETING_VERSION = 2.9.5;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -686,7 +686,7 @@
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/MoproKit/Libs",
);
MARKETING_VERSION = 2.9.4;
MARKETING_VERSION = 2.9.5;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",

View File

@@ -1,6 +1,6 @@
{
"name": "@selfxyz/mobile-app",
"version": "2.9.4",
"version": "2.9.5",
"private": true,
"type": "module",
"scripts": {

View File

@@ -598,58 +598,58 @@ describe('WebViewScreen same-origin security', () => {
mockPlatform.OS = 'ios';
});
it('allows initial URL to load', () => {
it('allows initial URL to load', async () => {
const initialUrl = 'https://apps.self.xyz';
render(<WebViewScreen {...createProps(initialUrl)} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: initialUrl,
});
expect(result).toBe(true);
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('allows navigation within trusted self.xyz domains', () => {
it('allows navigation within trusted self.xyz domains', async () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
// Different self.xyz subdomain - allowed because self.xyz is trusted
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://docs.self.xyz/guide',
});
expect(result).toBe(true);
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('allows navigation to whitelisted partner domains', () => {
it('allows navigation to whitelisted partner domains', async () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
// Whitelisted Figma game site
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://amity-lock-11401309.figma.site',
});
expect(result).toBe(true);
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('allows HTTPS navigation to untrusted domains after trusted entry (parent-trusted session)', () => {
it('allows HTTPS navigation to untrusted domains after trusted entry (parent-trusted session)', async () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://external-site.com',
});
expect(result).toBe(true);
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('allows HTTPS JS redirects after trusted entry (parent-trusted session)', () => {
it('allows HTTPS JS redirects after trusted entry (parent-trusted session)', async () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://malicious-phishing.com',
navigationType: 'other', // JS redirect, not a click
});
@@ -657,16 +657,16 @@ describe('WebViewScreen same-origin security', () => {
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('allows about:blank/srcdoc during trusted session (wallet bootstrap)', () => {
it('allows about:blank/srcdoc during trusted session (wallet bootstrap)', async () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
const resultBlank = webview.props.onShouldStartLoadWithRequest?.({
const resultBlank = await webview.props.onShouldStartLoadWithRequest?.({
url: 'about:blank',
});
expect(resultBlank).toBe(true);
const resultSrcdoc = webview.props.onShouldStartLoadWithRequest?.({
const resultSrcdoc = await webview.props.onShouldStartLoadWithRequest?.({
url: 'about:srcdoc',
});
expect(resultSrcdoc).toBe(true);
@@ -674,12 +674,12 @@ describe('WebViewScreen same-origin security', () => {
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('detects WalletConnect from Aave and shows wallet confirmation on iOS', () => {
it('detects WalletConnect from Aave and shows wallet confirmation on iOS', async () => {
// iOS-specific: WalletConnect attestation from Aave should trigger Safari kickout
render(<WebViewScreen {...createProps('https://app.aave.com')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://verify.walletconnect.org/v3/attestation?projectId=test',
mainDocumentURL: 'https://app.aave.com/',
isTopFrame: false,
@@ -714,7 +714,7 @@ describe('WebViewScreen same-origin security', () => {
render(<WebViewScreen {...createProps('https://app.aave.com')} />);
const webview = screen.getByTestId('webview');
webview.props.onShouldStartLoadWithRequest?.({
await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://verify.walletconnect.org/v3/attestation?projectId=test',
mainDocumentURL: 'https://app.aave.com/',
isTopFrame: false,
@@ -729,7 +729,7 @@ describe('WebViewScreen same-origin security', () => {
});
});
it('allows WalletConnect from Aave on Android (no kickout)', () => {
it('allows WalletConnect from Aave on Android (no kickout)', async () => {
// Android: WalletConnect should work in WebView normally
mockPlatform.OS = 'android';
@@ -737,7 +737,7 @@ describe('WebViewScreen same-origin security', () => {
render(<WebViewScreen {...createProps('https://app.aave.com')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://verify.walletconnect.org/v3/attestation?projectId=test',
mainDocumentURL: 'https://app.aave.com/',
isTopFrame: false,
@@ -753,12 +753,12 @@ describe('WebViewScreen same-origin security', () => {
}
});
it('allows WalletConnect from non-Aave domains on iOS (no kickout)', () => {
it('allows WalletConnect from non-Aave domains on iOS (no kickout)', async () => {
// iOS: WalletConnect from other domains should not trigger kickout
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://verify.walletconnect.org/v3/attestation?projectId=test',
mainDocumentURL: 'https://apps.self.xyz/',
isTopFrame: false,
@@ -771,12 +771,12 @@ describe('WebViewScreen same-origin security', () => {
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('blocks spoofed WalletConnect URL with domain in query params on iOS', () => {
it('blocks spoofed WalletConnect URL with domain in query params on iOS', async () => {
// Security: verify.walletconnect.org in query param should NOT trigger kickout
render(<WebViewScreen {...createProps('https://app.aave.com')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://evil.com/?next=verify.walletconnect.org',
mainDocumentURL: 'https://app.aave.com/',
isTopFrame: false,
@@ -791,7 +791,7 @@ describe('WebViewScreen same-origin security', () => {
expect(result).toBe(true);
});
it('blocks spoofed Aave URL with domain in query params on iOS', () => {
it('blocks spoofed Aave URL with domain in query params on iOS', async () => {
// Security: app.aave.com in query param should NOT trigger kickout
render(
<WebViewScreen
@@ -800,7 +800,7 @@ describe('WebViewScreen same-origin security', () => {
);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://verify.walletconnect.org/v3/attestation?projectId=test',
mainDocumentURL: 'https://evil.com/?page=app.aave.com',
isTopFrame: false,
@@ -813,12 +813,12 @@ describe('WebViewScreen same-origin security', () => {
expect(mockLinking.openURL).not.toHaveBeenCalled();
});
it('blocks spoofed WalletConnect URL with domain in path on iOS', () => {
it('blocks spoofed WalletConnect URL with domain in path on iOS', async () => {
// Security: verify.walletconnect.org in path should NOT trigger kickout
render(<WebViewScreen {...createProps('https://app.aave.com')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://evil.com/verify.walletconnect.org/fake-path',
mainDocumentURL: 'https://app.aave.com/',
isTopFrame: false,
@@ -832,12 +832,12 @@ describe('WebViewScreen same-origin security', () => {
expect(result).toBe(true);
});
it('blocks spoofed WalletConnect URL with domain as subdomain prefix on iOS', () => {
it('blocks spoofed WalletConnect URL with domain as subdomain prefix on iOS', async () => {
// Security: verify.walletconnect.org.evil.com should NOT match
render(<WebViewScreen {...createProps('https://app.aave.com')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://verify.walletconnect.org.evil.com/attestation',
mainDocumentURL: 'https://app.aave.com/',
isTopFrame: false,
@@ -851,12 +851,12 @@ describe('WebViewScreen same-origin security', () => {
expect(result).toBe(true);
});
it('enforces "always open externally" policy in onShouldStartLoadWithRequest', () => {
it('enforces "always open externally" policy in onShouldStartLoadWithRequest', async () => {
// keys.coinbase.com is in ALWAYS_OPEN_EXTERNALLY list
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://keys.coinbase.com/connect',
isTopFrame: true,
navigationType: 'click',
@@ -891,7 +891,7 @@ describe('WebViewScreen same-origin security', () => {
render(<WebViewScreen {...createProps('https://app.aave.com')} />);
const webview = screen.getByTestId('webview');
webview.props.onShouldStartLoadWithRequest?.({
await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://keys.coinbase.com/connect',
isTopFrame: true,
navigationType: 'click',
@@ -905,13 +905,13 @@ describe('WebViewScreen same-origin security', () => {
});
});
it('enforces "always open externally" policy for keys.coinbase.com', () => {
it('enforces "always open externally" policy for keys.coinbase.com', async () => {
// keys.coinbase.com is in ALWAYS_OPEN_EXTERNALLY (not TRUSTED_DOMAINS)
// It should always trigger external open, never load in WebView
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://keys.coinbase.com',
isTopFrame: true,
navigationType: 'click',
@@ -926,7 +926,7 @@ describe('WebViewScreen same-origin security', () => {
);
});
it('prevents keys.coinbase.com from being treated as trusted entrypoint', () => {
it('prevents keys.coinbase.com from being treated as trusted entrypoint', async () => {
// Verify keys.coinbase.com cannot start a trusted session
render(<WebViewScreen {...createProps('https://keys.coinbase.com')} />);
const webview = screen.getByTestId('webview');
@@ -934,7 +934,7 @@ describe('WebViewScreen same-origin security', () => {
// The initial URL should redirect externally, not load in WebView
// Since keys.coinbase.com is not trusted, the fallback URL will be used
// We can verify this by checking that a navigation to keys.coinbase.com blocks
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://keys.coinbase.com/wallet',
isTopFrame: true,
navigationType: 'click',
@@ -948,14 +948,14 @@ describe('WebViewScreen same-origin security', () => {
);
});
it('prevents keys.coinbase.com from loading in WebView despite being subdomain of coinbase.com', () => {
it('prevents keys.coinbase.com from loading in WebView despite being subdomain of coinbase.com', async () => {
// keys.coinbase.com is a subdomain of coinbase.com (which IS trusted)
// But keys.coinbase.com should be blocked because it's in always-external list
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
// Try to navigate to keys.coinbase.com
const keysResult = webview.props.onShouldStartLoadWithRequest?.({
const keysResult = await webview.props.onShouldStartLoadWithRequest?.({
url: 'https://keys.coinbase.com',
isTopFrame: true,
navigationType: 'click',
@@ -978,7 +978,7 @@ describe('WebViewScreen same-origin security', () => {
const webview = screen.getByTestId('webview');
// Simulate iframe navigation (isTopFrame=false) trying to open deep link
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'sms:+1234567890',
isTopFrame: false,
navigationType: 'click',
@@ -1010,7 +1010,7 @@ describe('WebViewScreen same-origin security', () => {
const webview = screen.getByTestId('webview');
// Simulate top-frame user click navigation
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'mailto:test@example.com',
isTopFrame: true,
navigationType: 'click',
@@ -1033,7 +1033,7 @@ describe('WebViewScreen same-origin security', () => {
const webview = screen.getByTestId('webview');
// Simulate top-frame but non-click navigation (e.g., 'other' from script)
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'tel:+1234567890',
isTopFrame: true,
navigationType: 'other',
@@ -1068,7 +1068,7 @@ describe('WebViewScreen same-origin security', () => {
const webview = screen.getByTestId('webview');
// Android request doesn't include iOS-specific fields
const result = webview.props.onShouldStartLoadWithRequest?.({
const result = await webview.props.onShouldStartLoadWithRequest?.({
url: 'sms:+1234567890',
});
@@ -1316,12 +1316,12 @@ describe('WebViewScreen same-origin security', () => {
);
});
it('shows deep-link confirmation when opening mailto/tel schemes', () => {
it('shows deep-link confirmation when opening mailto/tel schemes', async () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
// Trigger deep-link via onShouldStartLoadWithRequest
webview.props.onShouldStartLoadWithRequest?.({
await webview.props.onShouldStartLoadWithRequest?.({
url: 'mailto:test@example.com',
isTopFrame: true,
navigationType: 'click',
@@ -1354,7 +1354,7 @@ describe('WebViewScreen same-origin security', () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
webview.props.onShouldStartLoadWithRequest?.({
await webview.props.onShouldStartLoadWithRequest?.({
url: 'mailto:test@example.com',
isTopFrame: true,
navigationType: 'click',
@@ -1384,7 +1384,7 @@ describe('WebViewScreen same-origin security', () => {
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
webview.props.onShouldStartLoadWithRequest?.({
await webview.props.onShouldStartLoadWithRequest?.({
url: 'tel:+1234567890',
isTopFrame: true,
navigationType: 'click',
@@ -1429,14 +1429,14 @@ describe('WebViewScreen same-origin security', () => {
});
});
it('shows external-site confirmation for untrusted HTTPS navigation', () => {
it('shows external-site confirmation for untrusted HTTPS navigation', async () => {
// Start from untrusted URL (falls back to default trusted URL, so we need
// to simulate a scenario where untrusted navigation would be blocked)
render(<WebViewScreen {...createProps('https://apps.self.xyz')} />);
const webview = screen.getByTestId('webview');
// Navigate to untrusted HTTP URL (not HTTPS, not trusted)
webview.props.onShouldStartLoadWithRequest?.({
await webview.props.onShouldStartLoadWithRequest?.({
url: 'http://malicious-site.com',
isTopFrame: true,
navigationType: 'click',