app: patch vulnerable production dependencies

Bumps the two production dependencies flagged by `yarn audit` that have
fixes available.

http-proxy-middleware goes to 2.0.10 for the Host-header routing bypass.
It also needs a `resolutions` entry, otherwise yarn keeps a second copy
at 2.0.9 under react-scripts > webpack-dev-server because the existing
`^2.0.0` range is already satisfied.

react-router-dom goes to 6.30.4 for the untrusted-path open redirect. It
is pinned exactly rather than with a caret: @remix-run/router is a direct
dependency at an exact version and react-router-dom pins its own copy
exactly too, so a caret here would let a future patch release install a
second copy of the router and hand <HistoryRouter> a history built by a
different module instance.

That bump pulls in a few related changes:

- react-router 6.4 folded the `history` package into @remix-run/router,
  so the store now imports `createBrowserHistory` from there. Passing our
  own history object is discouraged and would bundle history twice.
- @remix-run/router's type declarations use accessors in interfaces,
  which needs TypeScript 4.3 or newer, so typescript goes to 4.9.5. TS
  4.4 also began typing `catch` variables as `unknown`, so `handleError`
  now takes `unknown` and narrows before reading `.message`, and the five
  sites that read it directly narrow first as well.
- @types/history and @types/react-router-dom are v5-era leftovers that
  now conflict with the types react-router-dom ships itself.
- The new history only accepts one active listener, but RouterStore and
  <HistoryRouter> both need one, so RouterStore subscribes once and fans
  updates out. It also needs `v5Compat` so listeners fire on push() and
  replace() rather than only on back/forward.

reactour still pulls in a vulnerable lodash.pick with no patched version
published. reactour 1.18.0 is the end of the v1 line and v2 is a rewrite,
so that one is left alone for now.
This commit is contained in:
jamaljsr 2026-08-10 15:44:18 -05:00
parent 23433a05ed
commit b6646d7868
No known key found for this signature in database
GPG key ID: 8680860C961AD657
10 changed files with 80 additions and 71 deletions

View file

@ -22,6 +22,7 @@
"@emotion/react": "11.4.0",
"@emotion/styled": "11.3.0",
"@improbable-eng/grpc-web": "0.14.0",
"@remix-run/router": "1.23.3",
"@types/react-collapse": "^5.0.1",
"big.js": "6.1.1",
"bootstrap": "4.6.1",
@ -31,7 +32,7 @@
"date-fns": "2.14.0",
"debug": "4.3.1",
"file-saver": "2.0.2",
"http-proxy-middleware": "2.0.9",
"http-proxy-middleware": "2.0.10",
"i18next": "19.5.1",
"i18next-browser-languagedetector": "5.0.0",
"lodash": "4.18.1",
@ -48,7 +49,7 @@
"react-collapse": "^5.1.1",
"react-dom": "17.0.2",
"react-i18next": "13.5.0",
"react-router-dom": "^6.3.0",
"react-router-dom": "6.30.4",
"react-toastify": "6.0.6",
"react-virtualized": "9.21.2",
"reactour": "1.18.0",
@ -71,13 +72,11 @@
"@types/debug": "4.1.5",
"@types/file-saver": "2.0.1",
"@types/google-protobuf": "3.15.10",
"@types/history": "4.7.6",
"@types/jest": "27.4.1",
"@types/lodash": "4.14.157",
"@types/node": "14.0.14",
"@types/react": "17.0.13",
"@types/react-dom": "17.0.8",
"@types/react-router-dom": "^5.3.3",
"@types/react-virtualized": "9.21.10",
"@types/reactour": "1.17.1",
"@types/semver": "^7.3.9",
@ -96,10 +95,11 @@
"sass": "1.43.4",
"storybook": "7.5.3",
"ts-protoc-gen": "0.15.0",
"typescript": "4.1.6",
"typescript": "4.9.5",
"webpack": "5.89.0"
},
"resolutions": {
"http-proxy-middleware": "2.0.10",
"strip-ansi": "6.0.1",
"jackspeak": "2.1.1",
"wrap-ansi": "7.0.0"

View file

@ -36,6 +36,16 @@ describe('AppView', () => {
expect(store.alerts.size).toBe(1);
});
it('should handle values thrown that are not Errors', () => {
// Nothing in the language guarantees a thrown value is an Error, and
// showing the user an empty alert is worse than showing the raw value.
store.handleError('something went wrong', 'title');
expect(store.alerts.size).toBe(1);
const alert = values(store.alerts)[0];
expect(alert.message).toBe('something went wrong');
expect(alert.title).toBe('title');
});
it('should handle authentication errors', () => {
rootStore.authStore.authenticated = true;
expect(store.alerts.size).toBe(0);

View file

@ -111,7 +111,7 @@ const AuthPage: React.FC = () => {
try {
await store.authStore.login(pass);
} catch (err) {
setError(err.message);
setError(err instanceof Error ? err.message : String(err));
const errors = store.authStore.errors;
setErrorDetailLit(errors.litDetail);
setErrorDetailLnd(errors.lndDetail);

View file

@ -1,6 +1,6 @@
import { autorun, makeAutoObservable, runInAction } from 'mobx';
import { IS_DEV, IS_TEST, USE_SAMPLE_DATA } from 'config';
import { createBrowserHistory, History } from 'history';
import { createBrowserHistory, History } from '@remix-run/router';
import AppStorage from 'util/appStorage';
import CsvExporter from 'util/csv';
import { actionLog, Logger } from 'util/log';
@ -217,7 +217,9 @@ export const createStore = (grpcClient?: GrpcClient, appStorage?: AppStorage) =>
const poolApi = new PoolApi(grpc);
const litApi = new LitApi(grpc);
const csv = new CsvExporter();
const history = createBrowserHistory();
// v5Compat is required for listeners to be notified of push() and replace()
// calls, not just browser back/forward navigation.
const history = createBrowserHistory({ v5Compat: true });
const store = new Store(
lndApi,

View file

@ -126,7 +126,9 @@ export default class AuthStore {
this.setCredentials('');
this._store.log.error('connection failure');
this.errors = { mainErr: '', litDetail: '', lndDetail: '' };
throw new Error(await this.getErrMsg(error.message));
throw new Error(
await this.getErrMsg(error instanceof Error ? error.message : String(error)),
);
}
}

View file

@ -150,7 +150,7 @@ export default class BatchStore {
this.loading = false;
});
} catch (error) {
if (error.message !== 'batch snapshot not found') {
if (!(error instanceof Error) || error.message !== 'batch snapshot not found') {
this._store.appView.handleError(error, `Unable to fetch batch with id ${prevId}`);
}
}
@ -171,7 +171,7 @@ export default class BatchStore {
this._store.log.info('updated batchStore.markets', toJS(this.markets));
});
} catch (error) {
if (error.message === 'batch snapshot not found') return;
if (error instanceof Error && error.message === 'batch snapshot not found') return;
this._store.appView.handleError(error, 'Unable to fetch the latest batch');
}
}

View file

@ -1,5 +1,8 @@
import { makeAutoObservable, runInAction } from 'mobx';
import { History, Location } from 'history';
import { History, Location } from '@remix-run/router';
/** the listener callback accepted by the history object */
type Listener = Parameters<History['listen']>[0];
export default class RouterStore {
/** the history object */
@ -11,13 +14,31 @@ export default class RouterStore {
constructor(history: History) {
makeAutoObservable(this, { history: false }, { deep: false, autoBind: true });
this.history = history;
this.location = history.location;
history.listen(({ location }) => {
// The router's history implementation only accepts a single active
// listener, but both this store and the <HistoryRouter> component need to
// observe navigation. Subscribe once here and fan the updates out to any
// additional listeners.
const listeners = new Set<Listener>();
history.listen(update => {
runInAction(() => {
this.location = location;
this.location = update.location;
});
listeners.forEach(listener => listener(update));
});
// Expose a history that hands out fan-out subscriptions instead of the
// single underlying one. A proxy is used so that the `location` and
// `action` getters continue to read through to the real history object.
this.history = new Proxy(history, {
get: (target, prop, receiver) => {
if (prop !== 'listen') return Reflect.get(target, prop, receiver);
return (listener: Listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
};
},
});
}

View file

@ -210,13 +210,13 @@ export default class AppView {
}
/** handle errors by showing a notification and/or the auth screen */
handleError(error: Error, title?: string) {
handleError(error: unknown, title?: string) {
if (error instanceof AuthenticationError) {
// this will automatically redirect to the auth page
this._store.authStore.authenticated = false;
this.notify(l('authErrorMsg'), l('authErrorTitle'));
} else {
this.notify(error.message, title);
this.notify(error instanceof Error ? error.message : String(error), title);
}
}
}

View file

@ -96,7 +96,10 @@ export default class AppStorage {
this.set(cacheKey, { expires, data });
log.info(`updated cache with ${keys.length} new ${cacheKey}`);
} catch (error) {
log.error(`failed to fetch ${cacheKey} from the API`, error.message);
log.error(
`failed to fetch ${cacheKey} from the API`,
error instanceof Error ? error.message : String(error),
);
}
}

View file

@ -3177,6 +3177,11 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@remix-run/router@1.23.3":
version "1.23.3"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.23.3.tgz#957c098d4393d301a8aa7dccf3ef28ea5430e36a"
integrity sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==
"@rollup/plugin-babel@^5.2.0":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@ -4545,16 +4550,6 @@
dependencies:
"@types/node" "*"
"@types/history@4.7.6":
version "4.7.6"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356"
integrity sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w==
"@types/history@^4.7.11":
version "4.7.11"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
"@types/html-minifier-terser@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@ -4708,23 +4703,6 @@
dependencies:
"@types/react" "*"
"@types/react-router-dom@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
dependencies:
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.18"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.18.tgz#c8851884b60bc23733500d86c1266e1cfbbd9ef3"
integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==
dependencies:
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-virtualized@9.21.10":
version "9.21.10"
resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.10.tgz#cd072dc9c889291ace2c4c9de8e8c050da8738b7"
@ -9096,13 +9074,6 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
history@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
dependencies:
"@babel/runtime" "^7.7.6"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.1:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
@ -9238,10 +9209,10 @@ http-proxy-agent@^4.0.1:
agent-base "6"
debug "4"
http-proxy-middleware@2.0.9, http-proxy-middleware@^2.0.0:
version "2.0.9"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef"
integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==
http-proxy-middleware@2.0.10, http-proxy-middleware@^2.0.0:
version "2.0.10"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz#b2df7b705203d7a8c269ac8450cf96b00c532f94"
integrity sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==
dependencies:
"@types/http-proxy" "^1.17.8"
http-proxy "^1.18.1"
@ -12811,20 +12782,20 @@ react-remove-scroll@2.5.5:
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-router-dom@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d"
integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==
react-router-dom@6.30.4:
version "6.30.4"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.30.4.tgz#f7167bf3da6c7d9132130ea985dd06def25e84d5"
integrity sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==
dependencies:
history "^5.2.0"
react-router "6.3.0"
"@remix-run/router" "1.23.3"
react-router "6.30.4"
react-router@6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
react-router@6.30.4:
version "6.30.4"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.30.4.tgz#638f35176527bd243d96d81d35d33b757bad46c2"
integrity sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==
dependencies:
history "^5.2.0"
"@remix-run/router" "1.23.3"
react-scripts@^5.0.1:
version "5.0.1"
@ -14574,10 +14545,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
typescript@4.1.6:
version "4.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.6.tgz#1becd85d77567c3c741172339e93ce2e69932138"
integrity sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==
typescript@4.9.5:
version "4.9.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
uglify-js@^3.1.4:
version "3.15.5"