feat: add explicit value checking on saving to / reading from budget

This commit is contained in:
Tom French 2022-07-29 01:06:55 +01:00 committed by James Long
parent 3a925948e2
commit 6cbc22312f
3 changed files with 24 additions and 5 deletions

View file

@ -3,10 +3,11 @@ import * as db from '../db';
import * as prefs from '../prefs'; import * as prefs from '../prefs';
import * as sheet from '../sheet'; import * as sheet from '../sheet';
import { batchMessages } from '../sync'; import { batchMessages } from '../sync';
import { safeNumber } from './util';
async function getSheetValue(sheetName, cell) { async function getSheetValue(sheetName, cell) {
const node = await sheet.getCell(sheetName, cell); const node = await sheet.getCell(sheetName, cell);
return typeof node.value === 'number' ? node.value : 0; return safeNumber(typeof node.value === 'number' ? node.value : 0);
} }
// We want to only allow the positive movement of money back and // We want to only allow the positive movement of money back and
@ -71,9 +72,7 @@ export function getBudget({ category, month }) {
} }
export function setBudget({ category, month, amount }) { export function setBudget({ category, month, amount }) {
if (typeof amount !== 'number') { amount = safeNumber(typeof amount === 'number' ? amount : 0);
amount = 0;
}
const table = getBudgetTable(); const table = getBudgetTable();
let existing = db.firstSync( let existing = db.firstSync(

View file

@ -1,6 +1,6 @@
import * as monthUtils from '../../shared/months'; import * as monthUtils from '../../shared/months';
import * as sheet from '../sheet'; import * as sheet from '../sheet';
import { number, sumAmounts, flatten2, unflatten2 } from './util'; import { number, sumAmounts, flatten2, unflatten2, safeNumber } from './util';
const { resolveName } = require('../spreadsheet/util'); const { resolveName } = require('../spreadsheet/util');

View file

@ -19,3 +19,23 @@ export function unflatten2(arr) {
} }
return res; return res;
} }
// Note that we don't restrict values to `Number.MIN_SAFE_INTEGER <= value <= Number.MAX_SAFE_INTEGER`
// where `Number.MAX_SAFE_INTEGER == 2^53 - 1` but a smaller range over `-(2^43-1) <= value <= 2^43 - 1`.
// This ensure that the number is accurate not just for the integer component but for 3 decimal places also.
//
// This gives us the guarantee that can use `safeNumber` on number whether they are unscaled user inputs
// or they have been converted to integers (using `amountToInteger`).
const MAX_SAFE_NUMBER = 2 ** 43 - 1;
const MIN_SAFE_NUMBER = -MAX_SAFE_NUMBER;
export function safeNumber(value) {
value = number(value);
if (value > MAX_SAFE_NUMBER || value < MIN_SAFE_NUMBER) {
throw new Error(
"Can't safely perform arithmetic with number: " + JSON.stringify(value)
);
}
return value;
}