Skip to content

Commit 873c9bd

Browse files
authored
fix: validate crypto authorize pattern against target wallet (#471)
1 parent f5a3f0b commit 873c9bd

4 files changed

Lines changed: 64 additions & 23 deletions

File tree

src/services/crypto-box/executor.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
AsymmetricEncryptParams,
1919
SignParams,
2020
} from './types'
21+
import { CryptoBoxErrorCodes } from './types'
2122

2223
// 导入加密工具
2324
import {
@@ -141,7 +142,10 @@ class CryptoExecutor {
141142
if (err instanceof Error && err.message.includes('Address mismatch')) {
142143
throw err
143144
}
144-
throw new Error(`Failed to get keypair for wallet ${walletId}: invalid patternKey or wallet not found`)
145+
throw Object.assign(
146+
new Error('Crypto authorization is invalid. Please re-authorize.'),
147+
{ code: CryptoBoxErrorCodes.INVALID_SESSION_SECRET }
148+
)
145149
}
146150
}
147151

src/stackflow/activities/sheets/CryptoAuthorizeJob.tsx

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { useFlow } from '../../stackflow';
1313
import { ActivityParamsProvider, useActivityParams } from '../../hooks';
1414
import { MiniappSheetHeader } from '@/components/ecosystem';
1515
import { PatternLock, patternToString } from '@/components/security/pattern-lock';
16-
import { walletStorageService } from '@/services/wallet-storage';
1716
import {
1817
type CryptoAction,
1918
type TokenDuration,
@@ -24,6 +23,7 @@ import {
2423
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
2524
import { walletStore } from '@/stores';
2625
import { superjson } from '@biochain/chain-effect';
26+
import { verifyCryptoAuthorizePattern } from './crypto-authorize-pattern';
2727

2828
type CryptoAuthorizeJobParams = {
2929
/** 请求的操作权限 (superjson 字符串) */
@@ -66,27 +66,8 @@ function CryptoAuthorizeJobContent() {
6666

6767
try {
6868
const patternKey = patternToString(nodes);
69-
70-
// 验证手势密码是否正确
71-
const wallets = await walletStorageService.getAllWallets();
72-
if (wallets.length === 0) {
73-
setError(true);
74-
setPattern([]);
75-
setIsVerifying(false);
76-
return;
77-
}
78-
79-
let isValid = false;
80-
for (const wallet of wallets) {
81-
try {
82-
await walletStorageService.getMnemonic(wallet.id, patternKey);
83-
isValid = true;
84-
break;
85-
} catch {
86-
// 继续尝试下一个钱包
87-
}
88-
}
89-
69+
// 验证手势密码必须匹配当前目标钱包,避免“任意钱包可解锁”导致后续执行失败
70+
const isValid = await verifyCryptoAuthorizePattern(walletId, patternKey);
9071
if (isValid && walletId) {
9172
// 发送成功事件(包含 walletId 和 selectedDuration 用于 Token 创建)
9273
const event = new CustomEvent('crypto-authorize-confirm', {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
const { mockGetMnemonic } = vi.hoisted(() => ({
4+
mockGetMnemonic: vi.fn(),
5+
}))
6+
7+
vi.mock('@/services/wallet-storage', () => ({
8+
walletStorageService: {
9+
getMnemonic: mockGetMnemonic,
10+
},
11+
}))
12+
13+
import { verifyCryptoAuthorizePattern } from '../crypto-authorize-pattern'
14+
15+
describe('verifyCryptoAuthorizePattern', () => {
16+
beforeEach(() => {
17+
vi.clearAllMocks()
18+
})
19+
20+
it('returns false when walletId is missing', async () => {
21+
const result = await verifyCryptoAuthorizePattern(undefined, '0-1-2-5-8')
22+
expect(result).toBe(false)
23+
expect(mockGetMnemonic).not.toHaveBeenCalled()
24+
})
25+
26+
it('validates pattern against target wallet only', async () => {
27+
mockGetMnemonic.mockResolvedValueOnce('mnemonic')
28+
29+
const result = await verifyCryptoAuthorizePattern('wallet-target', '0-1-2-5-8')
30+
31+
expect(result).toBe(true)
32+
expect(mockGetMnemonic).toHaveBeenCalledTimes(1)
33+
expect(mockGetMnemonic).toHaveBeenCalledWith('wallet-target', '0-1-2-5-8')
34+
})
35+
36+
it('returns false when target wallet pattern is invalid', async () => {
37+
mockGetMnemonic.mockRejectedValueOnce(new Error('Failed to decrypt mnemonic'))
38+
39+
const result = await verifyCryptoAuthorizePattern('wallet-target', '0-1-2-5-8')
40+
41+
expect(result).toBe(false)
42+
expect(mockGetMnemonic).toHaveBeenCalledTimes(1)
43+
expect(mockGetMnemonic).toHaveBeenCalledWith('wallet-target', '0-1-2-5-8')
44+
})
45+
})
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { walletStorageService } from '@/services/wallet-storage'
2+
3+
export async function verifyCryptoAuthorizePattern(walletId: string | undefined, patternKey: string): Promise<boolean> {
4+
if (!walletId) return false
5+
try {
6+
await walletStorageService.getMnemonic(walletId, patternKey)
7+
return true
8+
} catch {
9+
return false
10+
}
11+
}

0 commit comments

Comments
 (0)