Skip to content

Commit

Permalink
fix: ensure API signatures are even in length
Browse files Browse the repository at this point in the history
  • Loading branch information
iamacook committed Mar 7, 2025
1 parent 2e660bf commit ece3bfa
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ describe('MessageConfirmationSchema', () => {
message: 'Invalid "0x" notated hex string',
path: ['signature'],
},
{
code: 'custom',
message: 'Invalid signature',
path: ['signature'],
},
]),
);
});
Expand Down
4 changes: 2 additions & 2 deletions src/domain/messages/entities/message-confirmation.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SignatureType } from '@/domain/common/entities/signature-type.entity';
import { AddressSchema } from '@/validation/entities/schemas/address.schema';
import { HexSchema } from '@/validation/entities/schemas/hex.schema';
import { SignatureLikeSchema } from '@/validation/entities/schemas/signature.schema';
import { z } from 'zod';

export type MessageConfirmation = z.infer<typeof MessageConfirmationSchema>;
Expand All @@ -9,6 +9,6 @@ export const MessageConfirmationSchema = z.object({
created: z.coerce.date(),
modified: z.coerce.date(),
owner: AddressSchema,
signature: HexSchema,
signature: SignatureLikeSchema,
signatureType: z.nativeEnum(SignatureType),
});
4 changes: 2 additions & 2 deletions src/domain/safe/entities/multisig-transaction.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { HexSchema } from '@/validation/entities/schemas/hex.schema';
import { NumericStringSchema } from '@/validation/entities/schemas/numeric-string.schema';
import { z } from 'zod';
import { CoercedNumberSchema } from '@/validation/entities/schemas/coerced-number.schema';
import { SignatureSchema } from '@/validation/entities/schemas/signature.schema';
import { SignatureLikeSchema } from '@/validation/entities/schemas/signature.schema';

export type Confirmation = z.infer<typeof ConfirmationSchema>;

Expand All @@ -18,7 +18,7 @@ export const ConfirmationSchema = z.object({
submissionDate: z.coerce.date(),
transactionHash: HexSchema.nullish().default(null),
signatureType: z.nativeEnum(SignatureType),
signature: SignatureSchema.nullish().default(null),
signature: SignatureLikeSchema.nullish().default(null),
});

export const MultisigTransactionSchema = z.object({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ describe('Get by id - Transactions Controller (Unit)', () => {
signers,
safe,
});
multisigTransaction.confirmations![0].signature = `0xdeadbeef`;
multisigTransaction.confirmations![0].signature = `0xdeadbee`;
const getSafeUrl = `${chain.transactionService}/api/v1/safes/${safe.address}`;
const getChainUrl = `${safeConfigUrl}/api/v1/chains/${chain.chainId}`;
const getMultisigTransactionUrl = `${chain.transactionService}/api/v1/multisig-transactions/${multisigTransaction.safeTxHash}/`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ describe('List queued transactions by Safe - Transactions Controller (Unit)', ()
});
};
const nonce1 = await getTransaction(1);
nonce1.confirmations![0].signature = '0xdeadbeef';
nonce1.confirmations![0].signature = '0xdeadbee';
const nonce2 = await getTransaction(2);
const transactions: Array<MultisigTransaction> = [
multisigToJson(nonce1) as MultisigTransaction,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { faker } from '@faker-js/faker';
import { SignatureSchema } from '@/validation/entities/schemas/signature.schema';
import {
SignatureLikeSchema,
SignatureSchema,
} from '@/validation/entities/schemas/signature.schema';

describe('SignatureSchema', () => {
it('should validate a signature', () => {
Expand Down Expand Up @@ -57,3 +60,51 @@ describe('SignatureSchema', () => {
]);
});
});

describe('SignatureLikeSchema', () => {
it('should validate a signature', () => {
const signature = faker.string.hexadecimal({
// Somewhat "standard" length for dynamic signature
length: 130 + 64,
}) as `0x${string}`;

const result = SignatureLikeSchema.safeParse(signature);

expect(result.success).toBe(true);
});

it('should not validate a non-hex signature', () => {
const signature = faker.string.alphanumeric() as `0x${string}`;

const result = SignatureLikeSchema.safeParse(signature);

expect(!result.success && result.error.issues).toStrictEqual([
{
code: 'custom',
message: 'Invalid "0x" notated hex string',
path: [],
},
{
code: 'custom',
message: 'Invalid signature',
path: [],
},
]);
});

it('should not validate a incorrect length signature', () => {
const signature = faker.string.hexadecimal({
length: 129,
}) as `0x${string}`;

const result = SignatureLikeSchema.safeParse(signature);

expect(!result.success && result.error.issues).toStrictEqual([
{
code: 'custom',
message: 'Invalid signature',
path: [],
},
]);
});
});
14 changes: 14 additions & 0 deletions src/validation/entities/schemas/signature.schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { HexSchema } from '@/validation/entities/schemas/hex.schema';

// This does not take dynamic parts into account but we can safely
// apply it to proposed signatures as we do not support contract
// signatures in the inferface
function isSignature(value: `0x${string}`): boolean {
// We accept proposals of singular or concatenated signatures
return (value.length - 2) % 130 === 0;
Expand All @@ -8,3 +11,14 @@ function isSignature(value: `0x${string}`): boolean {
export const SignatureSchema = HexSchema.refine(isSignature, {
message: 'Invalid signature',
});

// As indexed signatures may be contract signatures, we need to assume
// that signatures from our API may have a dynamic part meaning that
// we can only check that the length is "byte-aligned"
function isSignatureLike(value: `0x${string}`): boolean {
return value.length % 2 === 0;
}

export const SignatureLikeSchema = HexSchema.refine(isSignatureLike, {
message: 'Invalid signature',
});

0 comments on commit ece3bfa

Please sign in to comment.