From 29bc14cfcba591145e09587902cb8b2ab3ea711a Mon Sep 17 00:00:00 2001 From: Chris Smith <1979423+chris13524@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:42:01 -0500 Subject: [PATCH] chore: test ethers verify API + AppKit in iframe verify test case (#2831) Co-authored-by: Chris Smith --- .github/workflows/ui_tests.yml | 4 - apps/laboratory/next.config.mjs | 6 +- .../library/ethers-verify-domain-mismatch.tsx | 40 ++++ .../src/pages/library/ethers-verify-evil.tsx | 34 +++ .../src/pages/library/ethers-verify-valid.tsx | 34 +++ ...d.tsx => wagmi-verify-domain-mismatch.tsx} | 22 +- ...{verify-evil.tsx => wagmi-verify-evil.tsx} | 9 - ...in-mismatch.tsx => wagmi-verify-valid.tsx} | 4 +- apps/laboratory/tests/README.md | 2 +- ...m-ethers-verify-domain-mismatch-fixture.ts | 12 + .../w3m-ethers-verify-evil-fixture.ts | 12 + .../w3m-ethers-verify-valid-fixture.ts | 12 + ...m-wagmi-verify-domain-mismatch-fixture.ts} | 4 +- ...re.ts => w3m-wagmi-verify-evil-fixture.ts} | 4 +- ...e.ts => w3m-wagmi-verify-valid-fixture.ts} | 4 +- .../tests/shared/pages/ModalPage.ts | 25 +- apps/laboratory/tests/shared/utils/project.ts | 3 +- apps/laboratory/tests/verify.spec.ts | 217 +++++++++++++++--- 18 files changed, 375 insertions(+), 73 deletions(-) create mode 100644 apps/laboratory/src/pages/library/ethers-verify-domain-mismatch.tsx create mode 100644 apps/laboratory/src/pages/library/ethers-verify-evil.tsx create mode 100644 apps/laboratory/src/pages/library/ethers-verify-valid.tsx rename apps/laboratory/src/pages/library/{verify-valid.tsx => wagmi-verify-domain-mismatch.tsx} (79%) rename apps/laboratory/src/pages/library/{verify-evil.tsx => wagmi-verify-evil.tsx} (86%) rename apps/laboratory/src/pages/library/{verify-domain-mismatch.tsx => wagmi-verify-valid.tsx} (100%) create mode 100644 apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-domain-mismatch-fixture.ts create mode 100644 apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-evil-fixture.ts create mode 100644 apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-valid-fixture.ts rename apps/laboratory/tests/shared/fixtures/{w3m-verify-domain-mismatch-fixture.ts => w3m-wagmi-verify-domain-mismatch-fixture.ts} (63%) rename apps/laboratory/tests/shared/fixtures/{w3m-verify-evil-fixture.ts => w3m-wagmi-verify-evil-fixture.ts} (67%) rename apps/laboratory/tests/shared/fixtures/{w3m-verify-valid-fixture.ts => w3m-wagmi-verify-valid-fixture.ts} (66%) diff --git a/.github/workflows/ui_tests.yml b/.github/workflows/ui_tests.yml index 5715941337..05e51b42ee 100644 --- a/.github/workflows/ui_tests.yml +++ b/.github/workflows/ui_tests.yml @@ -36,10 +36,6 @@ on: secrets: NEXT_PUBLIC_PROJECT_ID: required: true - RELEASE_TOKEN_V2: - required: true - TFC_INFRA_TOKEN: - required: true TESTS_NEXTAUTH_SECRET: required: false TESTS_MAILSAC_API_KEY: diff --git a/apps/laboratory/next.config.mjs b/apps/laboratory/next.config.mjs index f7bc508f99..8121bbeba9 100644 --- a/apps/laboratory/next.config.mjs +++ b/apps/laboratory/next.config.mjs @@ -8,6 +8,8 @@ const SHAKRA_UI = `'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE='` const secureSiteDomain = process.env.NEXT_PUBLIC_SECURE_SITE_SDK_URL ? new URL(process.env.NEXT_PUBLIC_SECURE_SITE_SDK_URL).origin : '' +const verifyApiNestedIframesTestOuterDomain = + 'https://verify-api-nested-iframes-test-outer-domain.com' const cspHeader = ` default-src 'self'; script-src 'self' ${SHAKRA_UI} ${process.env.NODE_ENV === 'production' ? '' : "'unsafe-eval'"}; @@ -15,11 +17,11 @@ const cspHeader = ` img-src * 'self' data: blob: https://walletconnect.org https://walletconnect.com https://secure.walletconnect.com https://secure.walletconnect.org https://tokens-data.1inch.io https://tokens.1inch.io https://ipfs.io https://appkit-lab.reown.org; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://react-wallet.walletconnect.com https://rpc.walletconnect.com https://rpc.walletconnect.org https://relay.walletconnect.com https://relay.walletconnect.org wss://relay.walletconnect.com wss://relay.walletconnect.org https://pulse.walletconnect.com https://pulse.walletconnect.org https://api.web3modal.com https://api.web3modal.org wss://www.walletlink.org https://o1095249.ingest.sentry.io https://quote-api.jup.ag; - frame-src 'self' https://verify.walletconnect.com https://verify.walletconnect.org https://secure.walletconnect.com https://secure.walletconnect.org https://secure.reown.com https://widget.solflare.com/ ${secureSiteDomain}/; + frame-src 'self' https://verify.walletconnect.com https://verify.walletconnect.org https://secure.walletconnect.com https://secure.walletconnect.org https://secure.reown.com https://widget.solflare.com/ ${secureSiteDomain}; object-src 'none'; base-uri 'self'; form-action 'self'; - frame-ancestors 'none'; + frame-ancestors ${verifyApiNestedIframesTestOuterDomain}; report-uri https://o1095249.ingest.sentry.io/api/4505685639364608/security/?sentry_key=36ff1e79c60877fce6c0273e94a8ed69; report-to csp-endpoint ` diff --git a/apps/laboratory/src/pages/library/ethers-verify-domain-mismatch.tsx b/apps/laboratory/src/pages/library/ethers-verify-domain-mismatch.tsx new file mode 100644 index 0000000000..c2695c63f3 --- /dev/null +++ b/apps/laboratory/src/pages/library/ethers-verify-domain-mismatch.tsx @@ -0,0 +1,40 @@ +import { createAppKit } from '@reown/appkit/react' +import { EthersAdapter } from '@reown/appkit-adapter-ethers' +import { EthersTests } from '../../components/Ethers/EthersTests' +import { AppKitButtons } from '../../components/AppKitButtons' +import { ThemeStore } from '../../utils/StoreUtil' +import { ConstantsUtil } from '../../utils/ConstantsUtil' +import { EthersModalInfo } from '../../components/Ethers/EthersModalInfo' +import { mainnet } from '@reown/appkit/networks' + +// Special project ID with verify enabled on localhost +const projectId = 'e4eae1aad4503db9966a04fd045a7e4d' + +const modal = createAppKit({ + adapters: [new EthersAdapter()], + networks: ConstantsUtil.EvmNetworks, + defaultNetwork: mainnet, + projectId, + metadata: { + name: 'AppKit', + description: 'AppKit Laboratory', + url: 'https://example.com', + icons: [] + }, + features: { + analytics: true + }, + customWallets: ConstantsUtil.CustomWallets +}) + +ThemeStore.setModal(modal) + +export default function Ethers() { + return ( + <> + + + + + ) +} diff --git a/apps/laboratory/src/pages/library/ethers-verify-evil.tsx b/apps/laboratory/src/pages/library/ethers-verify-evil.tsx new file mode 100644 index 0000000000..4208b635b7 --- /dev/null +++ b/apps/laboratory/src/pages/library/ethers-verify-evil.tsx @@ -0,0 +1,34 @@ +import { createAppKit } from '@reown/appkit/react' +import { EthersAdapter } from '@reown/appkit-adapter-ethers' +import { EthersTests } from '../../components/Ethers/EthersTests' +import { AppKitButtons } from '../../components/AppKitButtons' +import { ThemeStore } from '../../utils/StoreUtil' +import { ConstantsUtil } from '../../utils/ConstantsUtil' +import { EthersModalInfo } from '../../components/Ethers/EthersModalInfo' +import { mainnet } from '@reown/appkit/networks' + +// Special project ID with https://malicious-app-verify-simulation.vercel.app/ as the verified domain and this domain is marked as a scam +const projectId = '9d176efa3150a1df0a76c8c138b6b657' + +const modal = createAppKit({ + adapters: [new EthersAdapter()], + networks: ConstantsUtil.EvmNetworks, + defaultNetwork: mainnet, + projectId, + features: { + analytics: true + }, + customWallets: ConstantsUtil.CustomWallets +}) + +ThemeStore.setModal(modal) + +export default function Ethers() { + return ( + <> + + + + + ) +} diff --git a/apps/laboratory/src/pages/library/ethers-verify-valid.tsx b/apps/laboratory/src/pages/library/ethers-verify-valid.tsx new file mode 100644 index 0000000000..3dcebd1d99 --- /dev/null +++ b/apps/laboratory/src/pages/library/ethers-verify-valid.tsx @@ -0,0 +1,34 @@ +import { createAppKit } from '@reown/appkit/react' +import { EthersAdapter } from '@reown/appkit-adapter-ethers' +import { EthersTests } from '../../components/Ethers/EthersTests' +import { AppKitButtons } from '../../components/AppKitButtons' +import { ThemeStore } from '../../utils/StoreUtil' +import { ConstantsUtil } from '../../utils/ConstantsUtil' +import { EthersModalInfo } from '../../components/Ethers/EthersModalInfo' +import { mainnet } from '@reown/appkit/networks' + +// Special project ID with verify enabled on localhost +const projectId = 'e4eae1aad4503db9966a04fd045a7e4d' + +const modal = createAppKit({ + adapters: [new EthersAdapter()], + networks: ConstantsUtil.EvmNetworks, + defaultNetwork: mainnet, + projectId, + features: { + analytics: true + }, + customWallets: ConstantsUtil.CustomWallets +}) + +ThemeStore.setModal(modal) + +export default function Ethers() { + return ( + <> + + + + + ) +} diff --git a/apps/laboratory/src/pages/library/verify-valid.tsx b/apps/laboratory/src/pages/library/wagmi-verify-domain-mismatch.tsx similarity index 79% rename from apps/laboratory/src/pages/library/verify-valid.tsx rename to apps/laboratory/src/pages/library/wagmi-verify-domain-mismatch.tsx index 3aae04fe56..74d290851d 100644 --- a/apps/laboratory/src/pages/library/verify-valid.tsx +++ b/apps/laboratory/src/pages/library/wagmi-verify-domain-mismatch.tsx @@ -1,23 +1,14 @@ import { createAppKit } from '@reown/appkit/react' +import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { WagmiProvider } from 'wagmi' import { AppKitButtons } from '../../components/AppKitButtons' import { WagmiTests } from '../../components/Wagmi/WagmiTests' import { ThemeStore } from '../../utils/StoreUtil' import { WagmiModalInfo } from '../../components/Wagmi/WagmiModalInfo' -import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' import { ConstantsUtil } from '../../utils/ConstantsUtil' import { mainnet } from '@reown/appkit/networks' -const metadata = { - name: 'AppKit', - description: 'AppKit Laboratory', - // Allow localhost - url: 'http://localhost:3000', - icons: ['https://avatars.githubusercontent.com/u/37784886'], - verifyUrl: '' -} - // Special project ID with verify enabled on localhost const projectId = 'e4eae1aad4503db9966a04fd045a7e4d' @@ -34,11 +25,14 @@ const wagmiAdapter = new WagmiAdapter({ const modal = createAppKit({ adapters: [wagmiAdapter], networks, - projectId, defaultNetwork: mainnet, - metadata, - termsConditionsUrl: 'https://reown.com/terms-of-service', - privacyPolicyUrl: 'https://reown.com/privacy-policy' + projectId, + metadata: { + name: 'AppKit', + description: 'AppKit Laboratory', + url: 'https://example.com', + icons: [] + } }) ThemeStore.setModal(modal) diff --git a/apps/laboratory/src/pages/library/verify-evil.tsx b/apps/laboratory/src/pages/library/wagmi-verify-evil.tsx similarity index 86% rename from apps/laboratory/src/pages/library/verify-evil.tsx rename to apps/laboratory/src/pages/library/wagmi-verify-evil.tsx index 98ec057022..cd37df538f 100644 --- a/apps/laboratory/src/pages/library/verify-evil.tsx +++ b/apps/laboratory/src/pages/library/wagmi-verify-evil.tsx @@ -9,14 +9,6 @@ import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' import { ConstantsUtil } from '../../utils/ConstantsUtil' import { mainnet } from '@reown/appkit/networks' -const metadata = { - name: 'Evil AppKit', - description: 'Evil AppKit Laboratory', - url: 'https://malicious-app-verify-simulation.vercel.app/', - icons: ['https://avatars.githubusercontent.com/u/37784886'], - verifyUrl: '' -} - // Special project ID with https://malicious-app-verify-simulation.vercel.app/ as the verified domain and this domain is marked as a scam const projectId = '9d176efa3150a1df0a76c8c138b6b657' @@ -35,7 +27,6 @@ const modal = createAppKit({ networks, defaultNetwork: mainnet, projectId, - metadata, termsConditionsUrl: 'https://reown.com/terms-of-service', privacyPolicyUrl: 'https://reown.com/privacy-policy' }) diff --git a/apps/laboratory/src/pages/library/verify-domain-mismatch.tsx b/apps/laboratory/src/pages/library/wagmi-verify-valid.tsx similarity index 100% rename from apps/laboratory/src/pages/library/verify-domain-mismatch.tsx rename to apps/laboratory/src/pages/library/wagmi-verify-valid.tsx index 12756c5c55..3605474cd2 100644 --- a/apps/laboratory/src/pages/library/verify-domain-mismatch.tsx +++ b/apps/laboratory/src/pages/library/wagmi-verify-valid.tsx @@ -1,11 +1,11 @@ import { createAppKit } from '@reown/appkit/react' -import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { WagmiProvider } from 'wagmi' import { AppKitButtons } from '../../components/AppKitButtons' import { WagmiTests } from '../../components/Wagmi/WagmiTests' import { ThemeStore } from '../../utils/StoreUtil' import { WagmiModalInfo } from '../../components/Wagmi/WagmiModalInfo' +import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' import { ConstantsUtil } from '../../utils/ConstantsUtil' import { mainnet } from '@reown/appkit/networks' @@ -25,8 +25,8 @@ const wagmiAdapter = new WagmiAdapter({ const modal = createAppKit({ adapters: [wagmiAdapter], networks, - defaultNetwork: mainnet, projectId, + defaultNetwork: mainnet, termsConditionsUrl: 'https://reown.com/terms-of-service', privacyPolicyUrl: 'https://reown.com/privacy-policy' }) diff --git a/apps/laboratory/tests/README.md b/apps/laboratory/tests/README.md index 1c64835d23..05c2dddae1 100644 --- a/apps/laboratory/tests/README.md +++ b/apps/laboratory/tests/README.md @@ -9,7 +9,7 @@ We use Playwright as our functional test runner. It's configured to try multiple - Make sure your `.env.local` is set up (see `.env.example` for reference) - Run `pnpm playwright:install` to install the browsers required to run the tests -- Build AppKit by running `pnpm build` in the root directory +- Build AppKit & the Lab by running `pnpm build:laboratory` in the root directory ## Running Tests diff --git a/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-domain-mismatch-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-domain-mismatch-fixture.ts new file mode 100644 index 0000000000..f673489a0f --- /dev/null +++ b/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-domain-mismatch-fixture.ts @@ -0,0 +1,12 @@ +import type { ModalFixture } from './w3m-fixture' +import { ModalPage } from '../pages/ModalPage' +import { timingFixture } from './timing-fixture' + +export const testMEthersVerifyDomainMismatch = timingFixture.extend({ + library: ['ethers', { option: true }], + modalPage: async ({ page, library }, use) => { + const modalPage = new ModalPage(page, library, 'ethers-verify-domain-mismatch') + await modalPage.load() + await use(modalPage) + } +}) diff --git a/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-evil-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-evil-fixture.ts new file mode 100644 index 0000000000..44bbd88634 --- /dev/null +++ b/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-evil-fixture.ts @@ -0,0 +1,12 @@ +import type { ModalFixture } from './w3m-fixture' +import { ModalPage } from '../pages/ModalPage' +import { timingFixture } from './timing-fixture' + +export const testMEthersVerifyEvil = timingFixture.extend({ + library: ['ethers', { option: true }], + modalPage: async ({ page, library }, use) => { + const modalPage = new ModalPage(page, library, 'ethers-verify-evil') + await modalPage.load() + await use(modalPage) + } +}) diff --git a/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-valid-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-valid-fixture.ts new file mode 100644 index 0000000000..a141d4a5e9 --- /dev/null +++ b/apps/laboratory/tests/shared/fixtures/w3m-ethers-verify-valid-fixture.ts @@ -0,0 +1,12 @@ +import type { ModalFixture } from './w3m-fixture' +import { ModalPage } from '../pages/ModalPage' +import { timingFixture } from './timing-fixture' + +export const testMEthersVerifyValid = timingFixture.extend({ + library: ['ethers', { option: true }], + modalPage: async ({ page, library }, use) => { + const modalPage = new ModalPage(page, library, 'ethers-verify-valid') + await modalPage.load() + await use(modalPage) + } +}) diff --git a/apps/laboratory/tests/shared/fixtures/w3m-verify-domain-mismatch-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-domain-mismatch-fixture.ts similarity index 63% rename from apps/laboratory/tests/shared/fixtures/w3m-verify-domain-mismatch-fixture.ts rename to apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-domain-mismatch-fixture.ts index e87f4a3707..539e19650d 100644 --- a/apps/laboratory/tests/shared/fixtures/w3m-verify-domain-mismatch-fixture.ts +++ b/apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-domain-mismatch-fixture.ts @@ -2,10 +2,10 @@ import type { ModalFixture } from './w3m-fixture' import { ModalPage } from '../pages/ModalPage' import { timingFixture } from './timing-fixture' -export const testMVerifyDomainMismatch = timingFixture.extend({ +export const testMWagmiVerifyDomainMismatch = timingFixture.extend({ library: ['wagmi', { option: true }], modalPage: async ({ page, library }, use) => { - const modalPage = new ModalPage(page, library, 'verify-domain-mismatch') + const modalPage = new ModalPage(page, library, 'wagmi-verify-domain-mismatch') await modalPage.load() await use(modalPage) } diff --git a/apps/laboratory/tests/shared/fixtures/w3m-verify-evil-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-evil-fixture.ts similarity index 67% rename from apps/laboratory/tests/shared/fixtures/w3m-verify-evil-fixture.ts rename to apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-evil-fixture.ts index 9e65c6e300..db479b3df3 100644 --- a/apps/laboratory/tests/shared/fixtures/w3m-verify-evil-fixture.ts +++ b/apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-evil-fixture.ts @@ -2,10 +2,10 @@ import type { ModalFixture } from './w3m-fixture' import { ModalPage } from '../pages/ModalPage' import { timingFixture } from './timing-fixture' -export const testMVerifyEvil = timingFixture.extend({ +export const testMWagmiVerifyEvil = timingFixture.extend({ library: ['wagmi', { option: true }], modalPage: async ({ page, library }, use) => { - const modalPage = new ModalPage(page, library, 'verify-evil') + const modalPage = new ModalPage(page, library, 'wagmi-verify-evil') await modalPage.load() await use(modalPage) } diff --git a/apps/laboratory/tests/shared/fixtures/w3m-verify-valid-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-valid-fixture.ts similarity index 66% rename from apps/laboratory/tests/shared/fixtures/w3m-verify-valid-fixture.ts rename to apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-valid-fixture.ts index 8b42c63bb0..749573f5ff 100644 --- a/apps/laboratory/tests/shared/fixtures/w3m-verify-valid-fixture.ts +++ b/apps/laboratory/tests/shared/fixtures/w3m-wagmi-verify-valid-fixture.ts @@ -2,10 +2,10 @@ import type { ModalFixture } from './w3m-fixture' import { ModalPage } from '../pages/ModalPage' import { timingFixture } from './timing-fixture' -export const testMVerifyValid = timingFixture.extend({ +export const testMWagmiVerifyValid = timingFixture.extend({ library: ['wagmi', { option: true }], modalPage: async ({ page, library }, use) => { - const modalPage = new ModalPage(page, library, 'verify-valid') + const modalPage = new ModalPage(page, library, 'wagmi-verify-valid') await modalPage.load() await use(modalPage) } diff --git a/apps/laboratory/tests/shared/pages/ModalPage.ts b/apps/laboratory/tests/shared/pages/ModalPage.ts index 08b072c508..d4462fac85 100644 --- a/apps/laboratory/tests/shared/pages/ModalPage.ts +++ b/apps/laboratory/tests/shared/pages/ModalPage.ts @@ -16,9 +16,12 @@ export type ModalFlavor = | 'default' | 'external' | 'debug-mode' - | 'verify-valid' - | 'verify-domain-mismatch' - | 'verify-evil' + | 'wagmi-verify-valid' + | 'wagmi-verify-domain-mismatch' + | 'wagmi-verify-evil' + | 'ethers-verify-valid' + | 'ethers-verify-domain-mismatch' + | 'ethers-verify-evil' | 'no-email' | 'no-socials' | 'siwe' @@ -28,9 +31,12 @@ function getUrlByFlavor(baseUrl: string, library: string, flavor: ModalFlavor) { const urlsByFlavor: Partial> = { default: `${baseUrl}library/${library}/`, external: `${baseUrl}library/external/`, - 'verify-valid': `${baseUrl}library/verify-valid/`, - 'verify-domain-mismatch': `${baseUrl}library/verify-domain-mismatch/`, - 'verify-evil': maliciousUrl + 'wagmi-verify-valid': `${baseUrl}library/wagmi-verify-valid/`, + 'wagmi-verify-domain-mismatch': `${baseUrl}library/wagmi-verify-domain-mismatch/`, + 'wagmi-verify-evil': maliciousUrl, + 'ethers-verify-valid': `${baseUrl}library/ethers-verify-valid/`, + 'ethers-verify-domain-mismatch': `${baseUrl}library/ethers-verify-domain-mismatch/`, + 'ethers-verify-evil': maliciousUrl } return urlsByFlavor[flavor] || `${baseUrl}library/${library}-${flavor}/` @@ -57,8 +63,11 @@ export class ModalPage { } async load() { - if (this.flavor === 'verify-evil') { - await routeInterceptUrl(this.page, maliciousUrl, this.baseURL, '/library/verify-evil/') + if (this.flavor === 'wagmi-verify-evil') { + await routeInterceptUrl(this.page, maliciousUrl, this.baseURL, '/library/wagmi-verify-evil/') + } + if (this.flavor === 'ethers-verify-evil') { + await routeInterceptUrl(this.page, maliciousUrl, this.baseURL, '/library/ethers-verify-evil/') } await this.page.goto(this.url) diff --git a/apps/laboratory/tests/shared/utils/project.ts b/apps/laboratory/tests/shared/utils/project.ts index fe6f6c4b52..a1ddc28583 100644 --- a/apps/laboratory/tests/shared/utils/project.ts +++ b/apps/laboratory/tests/shared/utils/project.ts @@ -50,7 +50,8 @@ const SINGLE_ADAPTER_EVM_TESTS = [ 'siwe.spec.ts', 'smart-account.spec.ts', 'wallet-features.spec.ts', - 'wallet.spec.ts' + 'wallet.spec.ts', + 'verify.spec.ts' ] const SINGLE_ADAPTER_EVM_MOBILE_TESTS = ['mobile-wallet-features.spec.ts'] diff --git a/apps/laboratory/tests/verify.spec.ts b/apps/laboratory/tests/verify.spec.ts index 7f68b5f3af..b1ab8ce50b 100644 --- a/apps/laboratory/tests/verify.spec.ts +++ b/apps/laboratory/tests/verify.spec.ts @@ -1,19 +1,23 @@ -import { DEFAULT_CHAIN_NAME, DEFAULT_SESSION_PARAMS } from './shared/constants' +import { BASE_URL, DEFAULT_CHAIN_NAME, DEFAULT_SESSION_PARAMS } from './shared/constants' import { testM } from './shared/fixtures/w3m-fixture' -import { testMVerifyDomainMismatch } from './shared/fixtures/w3m-verify-domain-mismatch-fixture' -import { testMVerifyEvil } from './shared/fixtures/w3m-verify-evil-fixture' -import { testMVerifyValid } from './shared/fixtures/w3m-verify-valid-fixture' +import { testMWagmiVerifyDomainMismatch } from './shared/fixtures/w3m-wagmi-verify-domain-mismatch-fixture' +import { testMWagmiVerifyEvil } from './shared/fixtures/w3m-wagmi-verify-evil-fixture' +import { testMWagmiVerifyValid } from './shared/fixtures/w3m-wagmi-verify-valid-fixture' +import { testMEthersVerifyDomainMismatch } from './shared/fixtures/w3m-ethers-verify-domain-mismatch-fixture' +import { testMEthersVerifyEvil } from './shared/fixtures/w3m-ethers-verify-evil-fixture' +import { testMEthersVerifyValid } from './shared/fixtures/w3m-ethers-verify-valid-fixture' import { WalletPage } from './shared/pages/WalletPage' import { ModalValidator } from './shared/validators/ModalValidator' import { WalletValidator } from './shared/validators/WalletValidator' -import { expect } from '@playwright/test' +import test, { expect, type Page } from '@playwright/test' +import { timingFixture } from './shared/fixtures/timing-fixture' +import { ModalPage } from './shared/pages/ModalPage' +import { routeInterceptUrl } from './shared/utils/verify' testM( 'connection and signature requests from non-verified project should show as cannot verify', async ({ modalPage, context }) => { - if (modalPage.library === 'solana') { - return - } + test.skip(modalPage.library === 'solana') const modalValidator = new ModalValidator(modalPage.page) const walletPage = new WalletPage(await context.newPage()) @@ -40,12 +44,10 @@ testM( } ) -testMVerifyValid( - 'connection and signature requests from non-scam verified domain should show as domain match', +testMWagmiVerifyValid( + 'wagmi: connection and signature requests from non-scam verified domain should show as domain match', async ({ modalPage, context }) => { - if (modalPage.library === 'solana') { - return - } + test.skip(modalPage.library !== 'wagmi', 'fixture always uses wagmi') const modalValidator = new ModalValidator(modalPage.page) const walletPage = new WalletPage(await context.newPage()) @@ -60,7 +62,7 @@ testMVerifyValid( await walletValidator.expectConnected() await modalPage.sign() - const chainName = modalPage.library === 'solana' ? 'Solana' : DEFAULT_CHAIN_NAME + const chainName = DEFAULT_CHAIN_NAME await walletValidator.expectReceivedSign({ chainName }) await expect(walletPage.page.getByTestId('session-info-verified')).toBeVisible() await walletPage.handleRequest({ accept: true }) @@ -72,12 +74,10 @@ testMVerifyValid( } ) -testMVerifyDomainMismatch( - 'connection and signature requests from non-scam verified domain but on localhost should show as invalid domain', +testMWagmiVerifyDomainMismatch( + 'wagmi: connection and signature requests from non-scam verified domain but on localhost should show as invalid domain', async ({ modalPage, context }) => { - if (modalPage.library === 'solana') { - return - } + test.skip(modalPage.library !== 'wagmi', 'fixture always uses wagmi') const modalValidator = new ModalValidator(modalPage.page) const walletPage = new WalletPage(await context.newPage()) @@ -92,7 +92,7 @@ testMVerifyDomainMismatch( await walletValidator.expectConnected() await modalPage.sign() - const chainName = modalPage.library === 'solana' ? 'Solana' : DEFAULT_CHAIN_NAME + const chainName = DEFAULT_CHAIN_NAME await walletValidator.expectReceivedSign({ chainName }) await expect(walletPage.page.getByText('Invalid Domain')).toBeVisible() await walletPage.handleRequest({ accept: true }) @@ -104,12 +104,10 @@ testMVerifyDomainMismatch( } ) -testMVerifyEvil( - 'connection and signature requests from scam verified domain should show as scam domain', +testMWagmiVerifyEvil( + 'wagmi: connection and signature requests from scam verified domain should show as scam domain', async ({ modalPage, context }) => { - if (modalPage.library === 'solana') { - return - } + test.skip(modalPage.library !== 'wagmi', 'fixture always uses wagmi') const modalValidator = new ModalValidator(modalPage.page) const walletPage = new WalletPage(await context.newPage()) @@ -126,7 +124,7 @@ testMVerifyEvil( await walletValidator.expectConnected() await modalPage.sign() - const chainName = modalPage.library === 'solana' ? 'Solana' : DEFAULT_CHAIN_NAME + const chainName = DEFAULT_CHAIN_NAME await expect(walletPage.page.getByText('Website flagged')).toBeVisible() await walletPage.page.getByText('Proceed anyway').click() await walletValidator.expectReceivedSign({ chainName }) @@ -139,3 +137,170 @@ testMVerifyEvil( await walletValidator.expectDisconnected() } ) + +testMEthersVerifyValid( + 'ethers: connection and signature requests from non-scam verified domain should show as domain match', + async ({ modalPage, context }) => { + test.skip(modalPage.library !== 'ethers', 'fixture always uses ethers') + + const modalValidator = new ModalValidator(modalPage.page) + const walletPage = new WalletPage(await context.newPage()) + await walletPage.load() + const walletValidator = new WalletValidator(walletPage.page) + + const uri = await modalPage.getConnectUri() + await walletPage.connectWithUri(uri) + await expect(walletPage.page.getByTestId('session-info-verified')).toBeVisible() + await walletPage.handleSessionProposal(DEFAULT_SESSION_PARAMS) + await modalValidator.expectConnected() + await walletValidator.expectConnected() + + await modalPage.sign() + const chainName = DEFAULT_CHAIN_NAME + await walletValidator.expectReceivedSign({ chainName }) + await expect(walletPage.page.getByTestId('session-info-verified')).toBeVisible() + await walletPage.handleRequest({ accept: true }) + await modalValidator.expectAcceptedSign() + + await modalPage.disconnect() + await modalValidator.expectDisconnected() + await walletValidator.expectDisconnected() + } +) + +testMEthersVerifyDomainMismatch( + 'ethers: connection and signature requests from non-scam verified domain but on localhost should show as invalid domain', + async ({ modalPage, context }) => { + test.skip(modalPage.library !== 'ethers', 'fixture always uses ethers') + + const modalValidator = new ModalValidator(modalPage.page) + const walletPage = new WalletPage(await context.newPage()) + await walletPage.load() + const walletValidator = new WalletValidator(walletPage.page) + + const uri = await modalPage.getConnectUri() + await walletPage.connectWithUri(uri) + await expect(walletPage.page.getByText('Invalid Domain')).toBeVisible() + await walletPage.handleSessionProposal(DEFAULT_SESSION_PARAMS) + await modalValidator.expectConnected() + await walletValidator.expectConnected() + + await modalPage.sign() + const chainName = DEFAULT_CHAIN_NAME + await walletValidator.expectReceivedSign({ chainName }) + await expect(walletPage.page.getByText('Invalid Domain')).toBeVisible() + await walletPage.handleRequest({ accept: true }) + await modalValidator.expectAcceptedSign() + + await modalPage.disconnect() + await modalValidator.expectDisconnected() + await walletValidator.expectDisconnected() + } +) + +testMEthersVerifyEvil( + 'ethers: connection and signature requests from scam verified domain should show as scam domain', + async ({ modalPage, context }) => { + test.skip(modalPage.library !== 'ethers', 'fixture always uses ethers') + + const modalValidator = new ModalValidator(modalPage.page) + const walletPage = new WalletPage(await context.newPage()) + await walletPage.load() + const walletValidator = new WalletValidator(walletPage.page) + + const uri = await modalPage.getConnectUri() + await walletPage.connectWithUri(uri) + await expect(walletPage.page.getByText('Website flagged')).toBeVisible() + await walletPage.page.getByText('Proceed anyway').click() + await expect(walletPage.page.getByText('Potential threat')).toBeVisible() + await walletPage.handleSessionProposal(DEFAULT_SESSION_PARAMS) + await modalValidator.expectConnected() + await walletValidator.expectConnected() + + await modalPage.sign() + const chainName = DEFAULT_CHAIN_NAME + await expect(walletPage.page.getByText('Website flagged')).toBeVisible() + await walletPage.page.getByText('Proceed anyway').click() + await walletValidator.expectReceivedSign({ chainName }) + await expect(walletPage.page.getByText('Potential threat')).toBeVisible() + await walletPage.handleRequest({ accept: true }) + await modalValidator.expectAcceptedSign() + + await modalPage.disconnect() + await modalValidator.expectDisconnected() + await walletValidator.expectDisconnected() + } +) + +const prodVerifyServer = 'https://verify.walletconnect.org' + +// "https://verify-server-staging.walletconnect-v1-bridge.workers.dev" +const altVerifyServer = null + +interface TimingFixtureWithLibrary { + library: string +} + +timingFixture.extend({ + library: ['wagmi', { option: true }] +})('wagmi: AppKit in iframe + verify happy case', async ({ page: rootPage, context, library }) => { + test.skip(library !== 'wagmi', 'test always uses wagmi') + + const verifyApiNestedIframesTestOuterDomain = + 'https://verify-api-nested-iframes-test-outer-domain.com' + const outerUrl = verifyApiNestedIframesTestOuterDomain + const innerUrl = `${BASE_URL}library/wagmi-verify-valid` + await rootPage.route(outerUrl, async route => { + await route.fulfill({ + body: `` + }) + }) + if (altVerifyServer) { + await routeInterceptUrl(rootPage, prodVerifyServer, altVerifyServer, '/') + } + await rootPage.goto(outerUrl) + + const frame = rootPage.frame({ name: 'innerFrame' }) + if (frame === null) { + throw new Error('iframe not found') + } + + /* + * Forcibly cast the Frame to a Page so ModalPage accepts it. It has the same necessary functions for this test case, so this is OK. + * Note we don't call `.load()` on the ModalPage since it would navigate the top-level page instead of the iframe. + * Tried using `Page | Frame` on the ModalPage constructor but we have other functions that depend on fields specific to Page. + */ + const page = frame as unknown as Page + + const modalPage = new ModalPage(page, 'wagmi', 'wagmi-verify-valid') + if (modalPage.library === 'solana') { + return + } + + const modalValidator = new ModalValidator(modalPage.page) + const walletPagePage = await context.newPage() + if (altVerifyServer) { + await routeInterceptUrl(walletPagePage, prodVerifyServer, altVerifyServer, '/') + } + const walletPage = new WalletPage(walletPagePage) + await walletPage.load() + const walletValidator = new WalletValidator(walletPage.page) + + const uri = await modalPage.getConnectUri() + await walletPage.connectWithUri(uri) + await expect(walletPage.page.getByTestId('session-info-verified')).toBeVisible() + await walletPage.handleSessionProposal(DEFAULT_SESSION_PARAMS) + await modalValidator.expectConnected() + await walletValidator.expectConnected() + + await modalPage.sign() + const chainName = modalPage.library === 'solana' ? 'Solana' : DEFAULT_CHAIN_NAME + await walletValidator.expectReceivedSign({ chainName }) + await expect(walletPage.page.getByTestId('session-info-verified')).toBeVisible() + await walletPage.handleRequest({ accept: true }) + await modalValidator.expectAcceptedSign() + + await modalPage.disconnect() + await modalValidator.expectDisconnected() + await walletValidator.expectDisconnected() +})