Skip to content

Commit

Permalink
feat: sanity check invoice memos
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Jul 3, 2024
1 parent a8b37c0 commit 5f95d82
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/swap/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,8 @@ export default {
message: 'invalid address signature',
code: concatErrorCode(ErrorCodePrefix.Swap, 21),
}),
INVALID_INVOICE_MEMO: (): Error => ({
message: 'invalid invoice memo',
code: concatErrorCode(ErrorCodePrefix.Swap, 22),
}),
};
15 changes: 15 additions & 0 deletions lib/swap/NodeFallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type HolisticInvoice = InvoiceWithRoutingHints & {

class NodeFallback {
private static readonly addInvoiceTimeout = 10_000;
private static readonly invoiceMemoRegex = new RegExp(
// Visible ASCII characters with a maximal length of 40
'^[\x20-\x7E]{0,40}$',
);

constructor(
private logger: Logger,
Expand All @@ -37,6 +41,7 @@ class NodeFallback {
memo?: string,
routingHints?: HopHint[][],
): Promise<HolisticInvoice> => {
this.checkInvoiceMemo(memo);
let nodeForSwap = this.nodeSwitch.getNodeForReverseSwap(
id,
currency,
Expand Down Expand Up @@ -129,6 +134,16 @@ class NodeFallback {
NodeFallback.addInvoiceTimeout,
);
};

private checkInvoiceMemo = (memo?: string) => {
if (memo === undefined) {
return;
}

if (!NodeFallback.invoiceMemoRegex.test(memo)) {
throw Errors.INVALID_INVOICE_MEMO();
}
};
}

export default NodeFallback;
53 changes: 53 additions & 0 deletions test/unit/swap/NodeFallback.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,57 @@ describe('NodeFallback', () => {
),
).rejects.toEqual(Errors.NO_AVAILABLE_LIGHTNING_CLIENT());
});

describe('checkInvoiceMemo', () => {
test.each`
input
${'A'}
${'test'}
${' a'}
${'some longer string that is still ok;!..!'}
${'asdf - ! +*##asdf'}
`('should allow visible ASCII characters: $input', ({ input }) => {
fallback['checkInvoiceMemo'](input);
});

test('should allow empty strings', () => {
fallback['checkInvoiceMemo']('');
});

test('should allow undefined', () => {
fallback['checkInvoiceMemo'](undefined);
});

test.each`
input
${'\n'}
${'\r'}
${'\r\n'}
${'normal\nstring'}
`('should throw on newline', ({ input }) => {
expect(() => fallback['checkInvoiceMemo'](input)).toThrow(
Errors.INVALID_INVOICE_MEMO().message,
);
});

test.each`
input
${'Ä'}
${'ö'}
${'Ü'}
${'€'}
`('should throw on non-ASCII characters: $input', ({ input }) => {
expect(() => fallback['checkInvoiceMemo'](input)).toThrow(
Errors.INVALID_INVOICE_MEMO().message,
);
});

test('should limit length to 50', () => {
const msg = 'this is a very long string. buncha charss';
expect(msg).toHaveLength(41);
expect(() => fallback['checkInvoiceMemo'](msg)).toThrow(
Errors.INVALID_INVOICE_MEMO().message,
);
});
});
});

0 comments on commit 5f95d82

Please sign in to comment.