diff --git a/package-lock.json b/package-lock.json index 7c6a3c9a4..28432382f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21862,127 +21862,13 @@ "node": ">=0.12.0 <9.0.0" } }, - "node_modules/http2-express-bridge": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/http2-express-bridge/-/http2-express-bridge-1.0.7.tgz", - "integrity": "sha512-bmzZSyn3nuzXRqs/+WgH7IGOQYMCIZNJeqTJ/1AoDgMPTSP5wXQCxPGsdUbGzzxwiHrMwyT4Z7t8ccbsKqiHrw==", - "license": "MIT", - "dependencies": { - "merge-descriptors": "^1.0.1", - "send": "^0.17.1", - "setprototypeof": "^1.2.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/http2-express-bridge/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/http2-express-bridge/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/http2-express-bridge/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http2-express-bridge/node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", - "license": "MIT" - }, - "node_modules/http2-express-bridge/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http2-express-bridge/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http2-express-bridge/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http2-express-bridge/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http2-express-bridge/node_modules/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/http2-express-bridge/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "node_modules/http2-express": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http2-express/-/http2-express-1.0.0.tgz", + "integrity": "sha512-4sFhcpICPKonFHyBMGD9jT+2/Jg+J8qZTyuOHZPzHe7lbnPPOcJwlYqKfMukfvdH+F+0q6B9zifsHkHEGUJ7cg==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 20.0.0" } }, "node_modules/http2-wrapper": { @@ -42982,7 +42868,7 @@ "cookie-parser": "^1.4.6", "diagnostics": "^2.0.2", "glob": "^8.1.0", - "http2-express-bridge": "^1.0.7" + "http2-express": "^1.0.0" }, "devDependencies": { "@gasket/core": "^7.0.9", diff --git a/packages/gasket-plugin-express/CHANGELOG.md b/packages/gasket-plugin-express/CHANGELOG.md index e801d8e6b..b9dbda0f0 100644 --- a/packages/gasket-plugin-express/CHANGELOG.md +++ b/packages/gasket-plugin-express/CHANGELOG.md @@ -1,5 +1,9 @@ # `@gasket/plugin-express` +### 7.0.14 + +- Switch to `http2-express` to address vulnerabilities ([#959]) + ### 7.0.12 - remove tsconfig.test from fastify, tune glob ignores ([#969]) @@ -127,7 +131,9 @@ [#695]: https://github.com/godaddy/gasket/pull/695 [#708]: https://github.com/godaddy/gasket/pull/708 [#736]: https://github.com/godaddy/gasket/pull/736 +[#892]: https://github.com/godaddy/gasket/pull/892 [#958]: https://github.com/godaddy/gasket/pull/958 [#966]: https://github.com/godaddy/gasket/pull/966 [#967]: https://github.com/godaddy/gasket/pull/967 +[#959]: https://github.com/godaddy/gasket/pull/959 [#969]: https://github.com/godaddy/gasket/pull/969 diff --git a/packages/gasket-plugin-express/lib/index.js b/packages/gasket-plugin-express/lib/index.js index f6174463e..e2dfbe39b 100644 --- a/packages/gasket-plugin-express/lib/index.js +++ b/packages/gasket-plugin-express/lib/index.js @@ -17,7 +17,7 @@ const plugin = { getExpressApp(gasket) { const express = require('express'); const { http2 } = gasket.config; - app ??= http2 ? require('http2-express-bridge')(express) : express(); + app ??= http2 ? require('http2-express')(express) : express(); return app; } diff --git a/packages/gasket-plugin-express/package.json b/packages/gasket-plugin-express/package.json index 945c5b3e8..90abaa339 100644 --- a/packages/gasket-plugin-express/package.json +++ b/packages/gasket-plugin-express/package.json @@ -44,7 +44,7 @@ "cookie-parser": "^1.4.6", "diagnostics": "^2.0.2", "glob": "^8.1.0", - "http2-express-bridge": "^1.0.7" + "http2-express": "^1.0.0" }, "devDependencies": { "@gasket/core": "^7.0.9", diff --git a/packages/gasket-plugin-express/test/plugin.test.js b/packages/gasket-plugin-express/test/plugin.test.js index cc447bca4..d850a3726 100644 --- a/packages/gasket-plugin-express/test/plugin.test.js +++ b/packages/gasket-plugin-express/test/plugin.test.js @@ -11,7 +11,7 @@ const compressionMiddleware = jest.fn(); const mockCompression = jest.fn().mockReturnValue(compressionMiddleware); jest.mock('express', () => mockExpress); -jest.mock('http2-express-bridge', () => mockExpressBridge); +jest.mock('http2-express', () => mockExpressBridge); jest.mock('cookie-parser', () => mockCookieParser); jest.mock('compression', () => mockCompression); diff --git a/packages/gasket-redux/CHANGELOG.md b/packages/gasket-redux/CHANGELOG.md index 7f27ba22f..09a5642ef 100644 --- a/packages/gasket-redux/CHANGELOG.md +++ b/packages/gasket-redux/CHANGELOG.md @@ -1,5 +1,9 @@ # `@gasket/redux` +### 7.0.14 + +- Add configureMakeStore options callback to allow for per-request configuration ([#974]) + ### 7.0.0 - See [Version 7 Upgrade Guide] for overall changes @@ -95,3 +99,4 @@ [#457]: https://github.com/godaddy/gasket/pull/457 [#670]: https://github.com/godaddy/gasket/pull/670 [#693]: https://github.com/godaddy/gasket/pull/693 +[#974]: https://github.com/godaddy/gasket/pull/974 diff --git a/packages/gasket-redux/README.md b/packages/gasket-redux/README.md index fff0b1b01..f32f4fd42 100644 --- a/packages/gasket-redux/README.md +++ b/packages/gasket-redux/README.md @@ -17,6 +17,7 @@ Set up Redux store configuration and return a `makeStore` function **Signature** - `configureMakeStore(options, [postCreate]): makeStore` +- `configureMakeStore(optionsFn, [postCreate]): makeStore` **Props** @@ -30,8 +31,10 @@ Set up Redux store configuration and return a `makeStore` function - `enhancers` - (function[]) Any other redux store enhancers - `logging` - (boolean) set to true if you want to enable redux logger. (default: false) +- `optionsFn` - (function) function that returns the options object. - `postCreate` - (function) Executed after the store is create the resulting store as the argument +- **Return Value** diff --git a/packages/gasket-redux/src/configure-make-store.js b/packages/gasket-redux/src/configure-make-store.js index 9f8cd363d..c43f7029a 100644 --- a/packages/gasket-redux/src/configure-make-store.js +++ b/packages/gasket-redux/src/configure-make-store.js @@ -31,23 +31,23 @@ export function prepareReducer(allReducers, rootReducer) { * @type {import('./index').configureMakeStore} */ export default function configureMakeStore(makeStoreOptions = {}, postCreate) { - const { - thunkMiddleware = thunk, - middleware = [], - logging = false, - enhancers = [(f) => f], - reducers = {}, - rootReducer, - initialState = {} - } = makeStoreOptions; - /** * Wrapper for store create to create instance with SSR and to hydrate in * browser. * @type {import('./index').MakeStoreFn} */ + // eslint-disable-next-line max-statements function makeStore(state = {}, options = {}) { const { req, logger = console } = options; + const { + thunkMiddleware = thunk, + middleware = [], + logging = false, + enhancers = [(f) => f], + reducers = {}, + rootReducer, + initialState = {} + } = typeof makeStoreOptions === 'function' ? makeStoreOptions(options) : makeStoreOptions; // Use existing redux store if it has been already been instantiated by // redux-plugin diff --git a/packages/gasket-redux/src/index.d.ts b/packages/gasket-redux/src/index.d.ts index 1ad6beae0..0040ca824 100644 --- a/packages/gasket-redux/src/index.d.ts +++ b/packages/gasket-redux/src/index.d.ts @@ -8,6 +8,11 @@ declare module 'http' { } } +export type MakeStoreFnOptions = { + logger?: Console | Logger; + req?: IncomingMessage; +} + /** * Wrapper for store create to create instance with SSR and to hydrate in * browser. @@ -16,10 +21,7 @@ export type MakeStoreFn = { ( /** The initial redux state */ state: any, - options: { - logger?: Console | Logger; - req?: IncomingMessage; - } + options: MakeStoreFnOptions ): Store; }; @@ -53,7 +55,7 @@ export function prepareReducer( * Set up redux store configuration and return a makeStore function */ export function configureMakeStore( - options?: ConfigureMakeStoreOptions, + options?: ConfigureMakeStoreOptions | ((options: MakeStoreFnOptions) => ConfigureMakeStoreOptions), postCreate?: Function ): MakeStoreFn; diff --git a/packages/gasket-redux/test/configure-make-store.spec.js b/packages/gasket-redux/test/configure-make-store.spec.js index 3d45a62da..a1d95dea4 100644 --- a/packages/gasket-redux/test/configure-make-store.spec.js +++ b/packages/gasket-redux/test/configure-make-store.spec.js @@ -15,6 +15,7 @@ const mockEnhancer = jest.fn(createStore => (reducer, initialState, enhancer) => const mockPostCreate = jest.fn(); +// eslint-disable-next-line max-statements describe('configureMakeStore', () => { const mockReducers = { reducer1: f => f || {} @@ -47,6 +48,18 @@ describe('configureMakeStore', () => { expect(result).toBeInstanceOf(Function); }); + it('accepts callback optionsFn', () => { + result = configureMakeStore(() => {}); + expect(result).toBeInstanceOf(Function); + }); + + it('accepts callback optionsFn called with makeStoreOptions', () => { + const mockMakeOptions = { logger: {}, req: {} }; + const mockOptionsFn = jest.fn().mockReturnValue({}); + configureMakeStore(mockOptionsFn)({}, mockMakeOptions); + expect(mockOptionsFn).toHaveBeenCalledWith(mockMakeOptions); + }); + it('allows custom initial state', () => { store = configureMakeStore({ initialState: mockInitialState })(); const state = store.getState(); @@ -56,11 +69,25 @@ describe('configureMakeStore', () => { } }); + it('allows custom initial state from callback optionsFn', () => { + store = configureMakeStore(() => ({ initialState: mockInitialState }))(); + const state = store.getState(); + + for (const [key, value] of Object.entries(mockInitialState)) { + expect(state).toHaveProperty(key, value); + } + }); + it('allows custom middleware', () => { configureMakeStore({ middleware: [mockMiddleware] })(); expect(applyMiddlewareSpy.mock.calls[0][1]).toBe(mockMiddleware); }); + it('allows custom middleware from callback optionsFn', () => { + configureMakeStore(() => ({ middleware: [mockMiddleware] }))(); + expect(applyMiddlewareSpy.mock.calls[0][1]).toBe(mockMiddleware); + }); + it('adds thunk and logger middleware if enabled', () => { configureMakeStore({ logging: true })(); expect(applyMiddlewareSpy.mock.calls[0]).toHaveLength(2);