diff --git a/packages/gateway/src/handlers/domain.ts b/packages/gateway/src/handlers/domain.ts index 3331e8b..a3537a6 100644 --- a/packages/gateway/src/handlers/domain.ts +++ b/packages/gateway/src/handlers/domain.ts @@ -44,9 +44,9 @@ export function withRegisterDomain( return { error: { message: 'Domain already exists', status: 400 } } } - const addresses = parseEncodedAddressCalls(data, signature) - const texts = parseEncodedTextCalls(data, signature) - const contenthash = parseEncodedContentHashCall(data, signature) + const addresses = parseEncodedAddressCalls(data, node, signature) + const texts = parseEncodedTextCalls(data, node, signature) + const contenthash = parseEncodedContentHashCall(data, node, signature) await repo.register({ name, diff --git a/packages/gateway/src/services/calldata.ts b/packages/gateway/src/services/calldata.ts index 7dd29bc..6d51127 100644 --- a/packages/gateway/src/services/calldata.ts +++ b/packages/gateway/src/services/calldata.ts @@ -4,7 +4,11 @@ import { abi } from '../abi' import { TypedSignature } from '../types' import { Text, Address, Contenthash } from '../entities' -export function parseEncodedTextCalls(data: Hex[], signature: TypedSignature) { +export function parseEncodedTextCalls( + data: Hex[], + node: Hex, + signature: TypedSignature, +) { const callData = data.map((d: Hex) => decodeFunctionData({ abi: parseAbi(abi), data: d }), ) @@ -13,6 +17,10 @@ export function parseEncodedTextCalls(data: Hex[], signature: TypedSignature) { .filter((d) => d.functionName === 'setText') .map(({ args }): Omit => { const [domain, key, value] = args as [Hex, string, string] + + if (domain !== node) + throw new Error('All records must have a matching namehash') + return { key, value, @@ -25,6 +33,7 @@ export function parseEncodedTextCalls(data: Hex[], signature: TypedSignature) { export function parseEncodedAddressCalls( data: Hex[], + node: Hex, signature: TypedSignature, ) { const callData = data.map((d: Hex) => @@ -36,6 +45,9 @@ export function parseEncodedAddressCalls( .map(({ args }): Omit => { if (args?.length !== 3) { const [domain, address] = args as [Hex, string] + if (domain !== node) + throw new Error('All records must have a matching namehash') + return { domain, address: address.toLowerCase(), @@ -45,6 +57,9 @@ export function parseEncodedAddressCalls( } } const [domain, coin, address] = args as [Hex, string, string] + if (domain !== node) + throw new Error('All records must have a matching namehash') + return { domain, address, @@ -57,6 +72,7 @@ export function parseEncodedAddressCalls( export function parseEncodedContentHashCall( data: Hex[], + node: Hex, signature: TypedSignature, ): Omit | undefined { const callData = data.map((d: Hex) => @@ -67,6 +83,10 @@ export function parseEncodedContentHashCall( .filter((d) => d.functionName === 'setContenthash') .map(({ args }): Omit => { const [domain, contenthash] = args as [Hex, Hex] + + if (domain !== node) + throw new Error('All records must have a matching namehash') + return { domain, contenthash: hexToString(contenthash), diff --git a/packages/gateway/test/database.spec.ts b/packages/gateway/test/database.spec.ts index fb3c4ee..1321ea5 100644 --- a/packages/gateway/test/database.spec.ts +++ b/packages/gateway/test/database.spec.ts @@ -240,6 +240,64 @@ describe('Gateway Database', () => { }) expect(actualAddressWithCoin).toBe(true) }) + + it('should block registering a domain with records from another domain', async () => { + const pvtKey = generatePrivateKey() + const owner = privateKeyToAddress(pvtKey) + const name = 'blockful.eth' + const server = new ccip.Server() + server.add(abi, withRegisterDomain(repo)) + + const anotherNode = namehash('another.eth') + + const calldata = [ + encodeFunctionData({ + abi: parseAbi(abi), + functionName: 'setText', + args: [anotherNode, 'com.twitter', '@blockful.eth'], + }), + encodeFunctionData({ + functionName: 'setAddr', + abi: parseAbi(abi), + args: [anotherNode, '0x3a872f8fed4421e7d5be5c98ab5ea0e0245169a0'], + }), + encodeFunctionData({ + functionName: 'setAddr', + abi: parseAbi(abi), + args: [ + anotherNode, + 1n, + '0x3a872f8fed4421e7d5be5c98ab5ea0e0245169a2', + ], + }), + ] + await doCall({ + server, + abi, + sender: TEST_ADDRESS, + method: 'register', + pvtKey, + args: [ + { + name: toHex(packetToBytes(name)), + owner, + duration: 300n, + secret: zeroHash, + resolver: TEST_ADDRESS, + data: calldata, + reverseRecord: false, + fuses: 0, + extraData: zeroHash, + }, + ], + }) + + const actual = await datasource.getRepository(Domain).existsBy({ + node: namehash(name), + owner, + }) + expect(actual).toBe(false) + }) }) describe('Transfer Domain', () => {