Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added getAccounts suppressUnauthorized param #116

Merged
merged 15 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ interface TypedMessageParams extends MessageParams {
version: string;
}
interface WalletMiddlewareOptions {
getAccounts: (req: JsonRpcRequest<unknown>) => Promise<string[]>;
getAccounts: (
req: JsonRpcRequest<unknown>,
options?: {
suppressUnauthorized?: boolean;
},
) => Promise<string[]>;
processDecryptMessage?: (
msgParams: MessageParams,
req: JsonRpcRequest<unknown>,
Expand Down Expand Up @@ -376,9 +381,17 @@ export function createWalletMiddleware({
address: string,
req: JsonRpcRequest<unknown>,
): Promise<string> {
if (typeof address === 'string' && address.length > 0) {
// ensure address is included in provided accounts
const accounts: string[] = await getAccounts(req);
if (
typeof address === 'string' &&
address.length > 0 &&
resemblesAddress(address)
) {
// ensure address is included in provided accounts. `suppressUnauthorized: false` is passed to `getAccounts`
// so that an "unauthorized" error is thrown if the requester does not have the `eth_accounts`
// permission.
const accounts: string[] = await getAccounts(req, {
suppressUnauthorized: false,
});
const normalizedAccounts: string[] = accounts.map((_address) =>
_address.toLowerCase(),
);
Expand All @@ -387,6 +400,7 @@ export function createWalletMiddleware({
if (normalizedAccounts.includes(normalizedAddress)) {
return normalizedAddress;
}
throw ethErrors.provider.unauthorized();
}
throw ethErrors.rpc.invalidParams({
message: `Invalid parameters: must provide an Ethereum address.`,
Expand Down
104 changes: 100 additions & 4 deletions test/wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ describe('wallet', () => {
};
engine.push(createWalletMiddleware({ getAccounts, processTransaction }));
const txParams = {
from: testUnkownAddress,
from: '0x3d',
};

const payload = { method: 'eth_sendTransaction', params: [txParams] };
Expand All @@ -103,6 +103,26 @@ describe('wallet', () => {
);
}
});

it('throws unauthorized for unknown addresses', async () => {
const { engine } = createTestSetup();
const getAccounts = async () => testAddresses.slice(0, 2);
const witnessedTxParams: TransactionParams[] = [];
const processTransaction = async (_txParams: TransactionParams) => {
witnessedTxParams.push(_txParams);
return testTxHash;
};
engine.push(createWalletMiddleware({ getAccounts, processTransaction }));
const txParams = {
from: testUnkownAddress,
};

const payload = { method: 'eth_sendTransaction', params: [txParams] };
const promise = pify(engine.handle).call(engine, payload);
await expect(promise).rejects.toThrow(
'The requested account and/or method has not been authorized by the user.',
);
});
});

describe('signTransaction', () => {
Expand Down Expand Up @@ -144,7 +164,7 @@ describe('wallet', () => {
createWalletMiddleware({ getAccounts, processSignTransaction }),
);
const txParams = {
from: testAddresses[0],
from: '0x3',
};

const payload = { method: 'eth_signTransaction', params: [txParams] };
Expand All @@ -156,6 +176,29 @@ describe('wallet', () => {
);
}
});

it('should throw when provided unknown address', async () => {
const { engine } = createTestSetup();
const getAccounts = async () => testAddresses.slice(0, 2);
const witnessedTxParams: TransactionParams[] = [];
const processSignTransaction = async (_txParams: TransactionParams) => {
witnessedTxParams.push(_txParams);
return testTxHash;
};

engine.push(
createWalletMiddleware({ getAccounts, processSignTransaction }),
);
const txParams = {
from: testUnkownAddress,
};

const payload = { method: 'eth_signTransaction', params: [txParams] };
const promise = pify(engine.handle).call(engine, payload);
await expect(promise).rejects.toThrow(
'The requested account and/or method has not been authorized by the user.',
);
});
});

describe('signTypedData', () => {
Expand Down Expand Up @@ -213,7 +256,7 @@ describe('wallet', () => {

const payload = {
method: 'eth_signTypedData',
params: [message, testUnkownAddress],
params: [message, '0x3d'],
};
try {
await pify(engine.handle).call(engine, payload);
Expand All @@ -223,6 +266,34 @@ describe('wallet', () => {
);
}
});

it('should throw with unknown address', async () => {
const { engine } = createTestSetup();
const getAccounts = async () => testAddresses.slice();
const witnessedMsgParams: MessageParams[] = [];
const processTypedMessage = async (msgParams: MessageParams) => {
witnessedMsgParams.push(msgParams);
return testMsgSig;
};

engine.push(createWalletMiddleware({ getAccounts, processTypedMessage }));
const message = [
{
type: 'string',
name: 'message',
value: 'Hi, Alice!',
},
];

const payload = {
method: 'eth_signTypedData',
params: [message, testUnkownAddress],
};
const promise = pify(engine.handle).call(engine, payload);
await expect(promise).rejects.toThrow(
'The requested account and/or method has not been authorized by the user.',
);
});
});

describe('sign', () => {
Expand Down Expand Up @@ -272,7 +343,7 @@ describe('wallet', () => {
const message = 'haay wuurl';
const payload = {
method: 'personal_sign',
params: [message, testUnkownAddress],
params: [message, '0x3d'],
};

try {
Expand All @@ -283,6 +354,31 @@ describe('wallet', () => {
);
}
});

it('should error when provided unknown address', async () => {
const { engine } = createTestSetup();
const getAccounts = async () => testAddresses.slice();
const witnessedMsgParams: MessageParams[] = [];
const processPersonalMessage = async (msgParams: MessageParams) => {
witnessedMsgParams.push(msgParams);
return testMsgSig;
};

engine.push(
createWalletMiddleware({ getAccounts, processPersonalMessage }),
);

const message = 'haay wuurl';
const payload = {
method: 'personal_sign',
params: [message, testUnkownAddress],
};

const promise = pify(engine.handle).call(engine, payload);
await expect(promise).rejects.toThrow(
'The requested account and/or method has not been authorized by the user.',
);
});
});

describe('personalRecover', () => {
Expand Down