From c7558d1d7c8d74f19b8e33128073d293a48ee94c Mon Sep 17 00:00:00 2001 From: Connor Burton Date: Sun, 6 Mar 2022 01:06:26 +0000 Subject: [PATCH] fix: first attempt at natively ordering body.. to be extensible before merging --- package.json | 4 +- src/index.ts | 5 +- src/order.ts | 21 +++++ tests/{e2e.test.ts => e2e/index.test.ts} | 2 +- tests/{unit.test.ts => unit/index.test.ts} | 2 +- tests/unit/order.test.ts | 101 +++++++++++++++++++++ yarn.lock | 15 +-- 7 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 src/order.ts rename tests/{e2e.test.ts => e2e/index.test.ts} (98%) rename tests/{unit.test.ts => unit/index.test.ts} (99%) create mode 100644 tests/unit/order.test.ts diff --git a/package.json b/package.json index 81b5028..766217b 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,5 @@ "resolutions": { "jest/**/tmpl": "1.0.5" }, - "dependencies": { - "fast-json-stable-stringify": "^2.1.0" - } + "dependencies": {} } diff --git a/src/index.ts b/src/index.ts index 78c89cd..3e084b2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import crypto from 'crypto'; import { Request, RequestHandler, NextFunction } from 'express'; -import stringify from 'fast-json-stable-stringify'; +import order from './order'; import stringToBuffer from './stringToBuffer'; import validateArguments from './validateArguments'; @@ -36,7 +36,8 @@ export function generate(secret: string, algorithm: string = defaults.algorithm, // if we have a body, create a md5 hash of it and add it to the hmac if (typeof body === 'object' && body !== null) { const hash = crypto.createHash('md5'); - hash.update(stringify(body)); + const orderedBody = Array.isArray(body) ? body : order(body); + hash.update(JSON.stringify(orderedBody)); hmac.update(hash.digest('hex')); } diff --git a/src/order.ts b/src/order.ts new file mode 100644 index 0000000..3034a3a --- /dev/null +++ b/src/order.ts @@ -0,0 +1,21 @@ +type UnknownObject = Record; + +export default function order(o: UnknownObject): UnknownObject { + if (typeof o !== 'object' || o === null || Array.isArray(o)) { + return o; + } + + // get the keys in lexigraphic order + const ks = Object.keys(o).sort((a, b) => { + if (a === b) { + return 0; + } + + return a > b ? 1 : -1; + }); + + return ks.reduce((n: UnknownObject, k: string) => { + n[k] = order(o[k] as UnknownObject); + return n; + }, {}); +} \ No newline at end of file diff --git a/tests/e2e.test.ts b/tests/e2e/index.test.ts similarity index 98% rename from tests/e2e.test.ts rename to tests/e2e/index.test.ts index abdf6f5..f437d7f 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e/index.test.ts @@ -1,7 +1,7 @@ import { Server } from 'http'; import express from 'express'; import got, { Response, NormalizedOptions } from 'got'; -import { HMAC, generate } from './../src/index'; +import { HMAC, generate } from '../../src/index'; const PORT = 3000; const SECRET = 'secret'; diff --git a/tests/unit.test.ts b/tests/unit/index.test.ts similarity index 99% rename from tests/unit.test.ts rename to tests/unit/index.test.ts index e8745d1..25609ab 100644 --- a/tests/unit.test.ts +++ b/tests/unit/index.test.ts @@ -2,7 +2,7 @@ import { request, Request, Response } from 'express'; import crypto from 'crypto'; -import { HMAC, AuthError, generate } from './../src/index.js'; +import { HMAC, AuthError, generate } from '../../src/index.js'; interface MockRequest { headers?: { diff --git a/tests/unit/order.test.ts b/tests/unit/order.test.ts new file mode 100644 index 0000000..c91059b --- /dev/null +++ b/tests/unit/order.test.ts @@ -0,0 +1,101 @@ +import order from '../../src/order.js'; + +describe('orderJson', () => { + test('should return json ordered alphabetically by property name', () => { + expect(order({ + "_id": "6223fbad35687c97fd227957", + "index": 0, + "guid": "af604232-484d-4706-8867-d1a5789e8c76", + "isActive": true, + "balance": "$1,141.86", + "picture": "http://placehold.it/32x32", + "age": 35, + "eyeColor": "blue", + "name": "Jacqueline Wooten", + "gender": "female", + "company": "TERRAGEN", + "email": "jacquelinewooten@terragen.com", + "phone": "+1 (875) 405-2646", + "address": "436 Taylor Street, Manchester, Washington, 8727", + "about": "Nulla non eu laboris eu eu laboris duis ipsum. Dolore nostrud qui aliquip velit. Eu minim reprehenderit elit cillum sunt. Aliquip ut et fugiat consectetur veniam tempor eiusmod. Mollit officia laboris aute dolor incididunt id pariatur dolore non ut culpa ullamco enim. Ad ex ipsum irure fugiat laboris magna culpa.\r\n", + "registered": "2017-03-15T09:16:34 -00:00", + "latitude": 0.848836, + "longitude": 70.415866, + "tags": [ + "esse", + "ea", + "dolore", + "velit", + "sint", + "deserunt", + "occaecat" + ], + "friends": [ + { + "id": 0, + "name": "Tracy Harding", + "age": 50 + }, + { + "id": 1, + "name": "Hodge Harrington", + "age": 24 + }, + { + "id": 2, + "name": "Pierce Bailey", + "age": 74 + } + ], + "greeting": "Hello, Jacqueline Wooten! You have 4 unread messages.", + "favoriteFruit": "banana" + })).toEqual({ + "about": "Nulla non eu laboris eu eu laboris duis ipsum. Dolore nostrud qui aliquip velit. Eu minim reprehenderit elit cillum sunt. Aliquip ut et fugiat consectetur veniam tempor eiusmod. Mollit officia laboris aute dolor incididunt id pariatur dolore non ut culpa ullamco enim. Ad ex ipsum irure fugiat laboris magna culpa.\r\n", + "address": "436 Taylor Street, Manchester, Washington, 8727", + "age": 35, + "balance": "$1,141.86", + "company": "TERRAGEN", + "email": "jacquelinewooten@terragen.com", + "eyeColor": "blue", + "favoriteFruit": "banana", + "friends": [ + { + "age": 50, + "id": 0, + "name": "Tracy Harding" + }, + { + "age": 24, + "id": 1, + "name": "Hodge Harrington" + }, + { + "age": 74, + "id": 2, + "name": "Pierce Bailey" + } + ], + "gender": "female", + "greeting": "Hello, Jacqueline Wooten! You have 4 unread messages.", + "guid": "af604232-484d-4706-8867-d1a5789e8c76", + "latitude": 0.848836, + "_id": "6223fbad35687c97fd227957", + "index": 0, + "longitude": 70.415866, + "isActive": true, + "name": "Jacqueline Wooten", + "phone": "+1 (875) 405-2646", + "picture": "http://placehold.it/32x32", + "registered": "2017-03-15T09:16:34 -00:00", + "tags": [ + "esse", + "ea", + "dolore", + "velit", + "sint", + "deserunt", + "occaecat" + ], + }); + }); +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 832b4b9..329af05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,7 +2413,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -4735,10 +4735,10 @@ through@2, "through@>=2.2.7 <3": resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= +tmpl@1.0.5, tmpl@1.0.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" @@ -4773,11 +4773,6 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -trim-newlines@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-4.0.2.tgz#d6aaaf6a0df1b4b536d183879a6b939489808c7c" - integrity sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew== - trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"