From 5b601548e08cca98af96a59c4fa4665c35ea10e9 Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins Date: Sat, 23 Mar 2024 10:44:52 +0000 Subject: [PATCH] :bug: (import) disallow importing transactions with too large or small amounts Closes #1811 --- .../components/modals/ImportTransactions.jsx | 79 +++++++++++++------ packages/loot-core/src/shared/util.ts | 11 ++- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/packages/desktop-client/src/components/modals/ImportTransactions.jsx b/packages/desktop-client/src/components/modals/ImportTransactions.jsx index ef0fec38286..a65a020a193 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactions.jsx +++ b/packages/desktop-client/src/components/modals/ImportTransactions.jsx @@ -6,6 +6,7 @@ import { format as formatDate_ } from 'loot-core/src/shared/months'; import { amountToCurrency, amountToInteger, + isSafeNumber, looselyParseAmount, } from 'loot-core/src/shared/util'; @@ -248,14 +249,19 @@ function applyFieldMappings(transaction, mappings) { return result; } -function parseAmount(amount, mapper) { +function parseAmount(amount, mapper, multiplier) { if (amount == null) { return null; } + const parsed = typeof amount === 'string' ? looselyParseAmount(amount) : amount; - const value = mapper(parsed); - return value; + + if (parsed === null) { + return null; + } + + return mapper(parsed) * multiplier; } function parseAmountFields( @@ -272,10 +278,10 @@ function parseAmountFields( // Split mode is a little weird; first we look for an outflow and // if that has a value, we never want to show a number in the // inflow. Same for `amount`; we choose outflow first and then inflow - const outflow = parseAmount(trans.outflow, n => -Math.abs(n)) * multiplier; + const outflow = parseAmount(trans.outflow, n => -Math.abs(n), multiplier); const inflow = outflow ? 0 - : parseAmount(trans.inflow, n => Math.abs(n)) * multiplier; + : parseAmount(trans.inflow, n => Math.abs(n), multiplier); return { amount: outflow || inflow, @@ -285,17 +291,21 @@ function parseAmountFields( } if (inOutMode) { return { - amount: - parseAmount(trans.amount, n => - trans.inOut === outValue ? Math.abs(n) * -1 : Math.abs(n), - ) * multiplier, + amount: parseAmount( + trans.amount, + n => (trans.inOut === outValue ? Math.abs(n) * -1 : Math.abs(n)), + multiplier, + ), outflow: null, inflow: null, }; } return { - amount: - parseAmount(trans.amount, n => (flipAmount ? n * -1 : n)) * multiplier, + amount: parseAmount( + trans.amount, + n => (flipAmount ? n * -1 : n), + multiplier, + ), outflow: null, inflow: null, }; @@ -336,7 +346,7 @@ function Transaction({ [rawTransaction, fieldMappings], ); - let { amount, outflow, inflow } = parseAmountFields( + const { amount, outflow, inflow } = parseAmountFields( transaction, splitMode, inOutMode, @@ -344,9 +354,6 @@ function Transaction({ flipAmount, multiplierAmount, ); - amount = amountToCurrency(amount); - outflow = amountToCurrency(outflow); - inflow = amountToCurrency(inflow); return ( - {outflow} + {amountToCurrency(outflow)} - {inflow} + {amountToCurrency(inflow)} ) : ( @@ -414,10 +437,18 @@ function Transaction({ )} - {amount} + {amountToCurrency(amount)} )} diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts index 91f39de604a..b22a258836a 100644 --- a/packages/loot-core/src/shared/util.ts +++ b/packages/loot-core/src/shared/util.ts @@ -290,6 +290,14 @@ export function getNumberFormat({ const MAX_SAFE_NUMBER = 2 ** 51 - 1; const MIN_SAFE_NUMBER = -MAX_SAFE_NUMBER; +export function isSafeNumber(value: number): boolean { + try { + safeNumber(value); + return true; + } catch (e) { + return false; + } +} export function safeNumber(value: number) { if (!Number.isInteger(value)) { throw new Error( @@ -359,7 +367,8 @@ export function integerToAmount(n) { // currencies. We extract out the numbers and just ignore separators. export function looselyParseAmount(amount: string) { function safeNumber(v: number): null | number { - return isNaN(v) ? null : v; + if (isSafeNumber(v)) return v; + return null; } function extractNumbers(v: string): string {