Skip to content
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
2 changes: 1 addition & 1 deletion src/client/utils/computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const OperationTypeUserDeposit = 3;
export const userIdToBytes = (uid: string) => {
// User IDs are uint64 decimal strings. Reject hex/exponent/whitespace that
// BigNumber would otherwise coerce (e.g. '0x10' -> 16, '1e3' -> 1000).
if (typeof uid === 'string' && !/^\d+$/.test(uid)) {
if (typeof uid !== 'string' || !/^\d+$/.test(uid)) {
throw new Error(`invalid user id: ${uid}`);
}
let x: BigNumber;
Expand Down
7 changes: 3 additions & 4 deletions src/client/utils/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,13 @@ export class Encoder {
o.keys.forEach(k => {
// Buffer.from(k, 'hex') silently drops non-hex chars, so a typo'd key
// would shift the whole framing and burn funds to a wrong key.
const kb = Buffer.from(k, 'hex');
if (kb.byteLength !== 32) throw new Error(`invalid output key ${k}`);
this.write(kb);
if (!/^[0-9a-fA-F]{64}$/.test(k)) throw new Error(`invalid output key ${k}`);
this.write(Buffer.from(k, 'hex'));
});

const maskHex = o.mask || '';
if (maskHex && !/^[0-9a-fA-F]{64}$/.test(maskHex)) throw new Error(`invalid output mask ${o.mask}`);
const mask = maskHex ? Buffer.from(maskHex, 'hex') : Buffer.alloc(32, 0);
if (mask.byteLength !== 32) throw new Error(`invalid output mask ${o.mask}`);
this.write(mask);

const scriptHex = o.script || '';
Expand Down
13 changes: 11 additions & 2 deletions test/mixin/codec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,20 +180,29 @@ describe('transaction codec', () => {
expect(() => encoder.encodeOutput({ type, amount: '1', keys: [] })).toThrow('invalid output type');
});

it.each([['zz'], ['44'.repeat(31)], ['abc']])('rejects an output with a malformed key: %s', key => {
it.each(['zz', '44'.repeat(31), 'abc', `${'44'.repeat(32)}f`, `${'44'.repeat(32)}zz`])('rejects an output with a malformed key: %s', key => {
const encoder = new Encoder(Buffer.alloc(0));

// Buffer.from(key, 'hex') silently truncates, which would shift the
// framing and burn funds to a wrong key.
expect(() => encoder.encodeOutput({ amount: '1', keys: [key] })).toThrow('invalid output key');
});

it.each(['zz', '44'.repeat(31)])('rejects an output with a malformed mask: %s', mask => {
it.each(['zz', '44'.repeat(31), `${'44'.repeat(32)}f`, `${'44'.repeat(32)}zz`])('rejects an output with a malformed mask: %s', mask => {
const encoder = new Encoder(Buffer.alloc(0));

expect(() => encoder.encodeOutput({ amount: '1', keys: [], mask })).toThrow('invalid output mask');
});

it.each([undefined, '', 'Ab'.repeat(32)])('accepts uppercase hex and an optional mask: %s', mask => {
const encoder = new Encoder(Buffer.alloc(0));
encoder.encodeOutput({ amount: '1', keys: ['Ab'.repeat(32)], mask });
expect(new Decoder(encoder.buffer()).decodeOutput()).toMatchObject({
keys: ['ab'.repeat(32)],
mask: mask ? 'ab'.repeat(32) : '00'.repeat(32),
});
});

it('round-trips sorted signature entries', () => {
const signatures = { 2: '22'.repeat(64), 0: '11'.repeat(64) };
const encoder = new Encoder(Buffer.alloc(0));
Expand Down
4 changes: 4 additions & 0 deletions test/mixin/computer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ describe('computer utilities', () => {
expect(checkSystemCallSize(Buffer.alloc(MAX_SOLANA_TX_SIZE + 1))).toBe(false);
});

it.each([1, Number.MAX_SAFE_INTEGER + 1, BigInt(1), null, undefined, true])('rejects a non-string user ID: %s', userID => {
expect(() => userIdToBytes(userID as unknown as string)).toThrow('invalid user id');
});

it('builds system call extras with and without a fee ID', () => {
const base = Buffer.concat([userIdToBytes('1'), Buffer.from(parseUUID(callID)), Buffer.from([1])]);

Expand Down
Loading