From cec75fee8a07a398028ca4b9d2e39aa2f86e8db8 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 18 Jun 2024 15:11:13 -0500 Subject: [PATCH] Replace jsonwebtoken with jose We received a report of this package failing with the following error: > TypeError: Right-hand side of 'instanceof' is not an object The stack trace pointed at the jsonwebtoken package as the source. At first it looked like this may be a problem on older versions of Node, but it turned out that they were using a relatively recent version (v18.17.1). It appears that a lot of other people may be running into a similar issue: - https://github.com/auth0/node-jsonwebtoken/issues/939 - https://github.com/auth0/node-jsonwebtoken/issues/892 People have reported success by either downgrading jsonwebtoken to v8 or replacing it with jose. Since we updated to jsonwebtoken v9 to fix a security vulnerability, I don't think it is a great idea to go back to v8. jose seems to be well-supported, healthy, and active: https://snyk.io/advisor/npm-package/jose Let's give this a shot! The API is a little bit different than jsonwebtoken, but for the most part I think the new version is simple enough to transition to. I set the alg properity to HS256, since it is required to specify the alg and that is what jsonwebtoken uses as the default. --- package.json | 2 +- src/fetchPng.js | 19 +++++++++++++----- src/makeRequest.js | 10 ++++++---- yarn.lock | 50 ++++++++-------------------------------------- 4 files changed, 29 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index bedead5..f5fe57f 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "form-data": "^4.0.0", "glob": "^7.1.2", "https-proxy-agent": "^5.0.0", + "jose": "^5.4.1", "jsdom": "^16.4.0", - "jsonwebtoken": "^9.0.0", "lcs-image-diff": "^2.0.0", "node-fetch": "^2.6.6", "parse-srcset": "^1.0.2", diff --git a/src/fetchPng.js b/src/fetchPng.js index b442d6c..67508f9 100644 --- a/src/fetchPng.js +++ b/src/fetchPng.js @@ -1,18 +1,24 @@ import { PNG } from 'pngjs'; import fetch from 'node-fetch'; - -import jwt from 'jsonwebtoken'; +import { SignJWT } from 'jose'; export default async function fetchPng(url, { apiKey, apiSecret, endpoint }) { const headers = {}; + if (url.startsWith(endpoint)) { - headers.Authorization = `Bearer ${jwt.sign({ key: apiKey }, apiSecret, { - header: { kid: apiKey }, - })}`; + const encodedSecret = new TextEncoder().encode(apiSecret); + // https://github.com/panva/jose/blob/main/docs/classes/jwt_sign.SignJWT.md + const signed = await new SignJWT({ key: apiKey }) + .setProtectedHeader({ alg: 'HS256', kid: apiKey }) + .sign(encodedSecret); + + headers.Authorization = `Bearer ${signed}`; } + const response = await fetch(url, { headers, }); + if (!response.ok) { throw new Error( `Failed to fetch PNG at ${url}. Response ${ @@ -20,12 +26,15 @@ export default async function fetchPng(url, { apiKey, apiSecret, endpoint }) { }: ${await response.text()}`, ); } + const contentType = response.headers.get('Content-Type') || 'unknown'; + if (!url.startsWith(endpoint) && !contentType.startsWith('image/png')) { throw new Error( `Failed to fetch PNG at ${url}. Response has wrong content type: ${contentType}`, ); } + return new Promise((resolve, reject) => { response.body .pipe(new PNG()) diff --git a/src/makeRequest.js b/src/makeRequest.js index 25fce1f..57467f6 100644 --- a/src/makeRequest.js +++ b/src/makeRequest.js @@ -3,7 +3,7 @@ import FormData from 'form-data'; import HttpsProxyAgent from 'https-proxy-agent'; import asyncRetry from 'async-retry'; import fetch from 'node-fetch'; -import jwt from 'jsonwebtoken'; +import { SignJWT } from 'jose'; import { version } from '../package.json'; @@ -52,9 +52,11 @@ export default async function makeRequest( ? JSON.stringify(jsonBody) : undefined; - const signed = jwt.sign({ key: apiKey }, apiSecret, { - header: { kid: apiKey }, - }); + const encodedSecret = new TextEncoder().encode(apiSecret); + // https://github.com/panva/jose/blob/main/docs/classes/jwt_sign.SignJWT.md + const signed = await new SignJWT({ key: apiKey }) + .setProtectedHeader({ alg: 'HS256', kid: apiKey }) + .sign(encodedSecret); const headers = { Authorization: `Bearer ${signed}`, diff --git a/yarn.lock b/yarn.lock index 7e45d37..159d41e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2042,11 +2042,6 @@ buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2452,13 +2447,6 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - ecstatic@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.3.2.tgz#6d1dd49814d00594682c652adb66076a69d46c48" @@ -3936,6 +3924,11 @@ jest@^27.4.5: import-local "^3.0.2" jest-cli "^27.5.1" +jose@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-5.4.1.tgz#b471ee3963920ba5452fd1b1398c8ba72a7b2fcf" + integrity sha512-U6QajmpV/nhL9SyfAewo000fkiRQ+Yd2H0lBxJJ9apjpOgkOcBQJWOrMo917lxLptdS/n/o/xPzMkXhF46K8hQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4034,16 +4027,6 @@ json5@^2.1.2, json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== -jsonwebtoken@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" - integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== - dependencies: - jws "^3.2.2" - lodash "^4.17.21" - ms "^2.1.1" - semver "^7.3.8" - "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" @@ -4052,23 +4035,6 @@ jsonwebtoken@^9.0.0: array-includes "^3.1.5" object.assign "^4.1.3" -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -4169,7 +4135,7 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== -lodash@^4.17.14, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.7.0: +lodash@^4.17.14, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4937,7 +4903,7 @@ rx-lite@*, rx-lite@^4.0.8: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" integrity sha512-Cun9QucwK6MIrp3mry/Y7hqD1oFqTYLQ4pGxaHTjIdaFDWRGGLikqp6u8LcWJnzpoALg9hap+JGk8sFIUuEGNA== -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -5010,7 +4976,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.8: +semver@^7.3.2: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==