From 5b5ccbb590f48266d059389dba5a66260fa878e8 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 15 Oct 2021 22:23:29 +0200 Subject: [PATCH] Add API data sanitization --- api/v0/index.js | 196 +++++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + server/index.js | 32 ++++---- yarn.lock | 20 +++++ 4 files changed, 229 insertions(+), 20 deletions(-) diff --git a/api/v0/index.js b/api/v0/index.js index c762d80..944f125 100644 --- a/api/v0/index.js +++ b/api/v0/index.js @@ -29,17 +29,149 @@ more information on this, and how to apply and follow the GNU AGPL, see { let promises = [] let results = [] for (let iUser = 0; iUser < data.keyData.users.length; iUser++) { - const user = data.keyData.users[iUser]; + const user = data.keyData.users[iUser] for (let iClaim = 0; iClaim < user.claims.length; iClaim++) { - const claim = user.claims[iClaim]; + const claim = user.claims[iClaim] promises.push( new Promise(async (resolve, reject) => { @@ -59,6 +191,34 @@ const doVerification = async (data) => { return data } +const sanitize = (data) => { + let results = [] + + const dataClone = JSON.parse(JSON.stringify(data)) + + for (let iUser = 0; iUser < dataClone.keyData.users.length; iUser++) { + const user = dataClone.keyData.users[iUser] + + for (let iClaim = 0; iClaim < user.claims.length; iClaim++) { + const claim = user.claims[iClaim] + + // TODO Fix upstream + if (!claim.verification) { + claim.verification = {} + } + + data.keyData.users[iUser].claims[iClaim] = claim + } + } + + const valid = apiProfileValidate(data) + if (!valid) { + throw new Error(`Profile data sanitization error`) + } + + return data +} + router.get('/profile/fetch', check('query').exists(), check('protocol').optional().toLowerCase().isIn(["hkp", "wkd"]), @@ -100,7 +260,21 @@ router.get('/profile/fetch', data = await doVerification(data) } - res.send(data) + try { + // Sanitize JSON + data = sanitize(data); + } catch (error) { + data.keyData = {} + data.extra = {} + data.errors = [error.message] + } + + let statusCode = 200 + if (data.errors.length > 0) { + statusCode = 500 + } + + res.status(500).send(data) } ) @@ -116,7 +290,21 @@ router.get('/profile/verify', // Do verification data = await doVerification(req.query.data) - res.send(data) + try { + // Sanitize JSON + data = sanitize(data); + } catch (error) { + data.keyData = {} + data.extra = {} + data.errors = [error.message] + } + + let statusCode = 200 + if (data.errors.length > 0) { + statusCode = 500 + } + + res.status(500).send(data) } ) diff --git a/package.json b/package.json index a4de81f..07403e6 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A modern, secure and privacy-friendly platform to establish your decentralized online identity", "main": "index.js", "dependencies": { + "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", "dialog-polyfill": "^0.5.6", diff --git a/server/index.js b/server/index.js index 259b9cc..043e182 100644 --- a/server/index.js +++ b/server/index.js @@ -37,7 +37,7 @@ const generateWKDProfile = async (id) => { let keyData = await doip.keys.process(key.publicKey) keyData.key.fetchMethod = 'wkd' keyData.key.uri = key.fetchURL - keyData.key.data = null + keyData.key.data = {} keyData = processKeyData(keyData) return { @@ -49,9 +49,9 @@ const generateWKDProfile = async (id) => { }) .catch(err => { return { - key: null, - keyData: null, - extra: null, + key: {}, + keyData: {}, + extra: {}, errors: [err.message] } }) @@ -63,7 +63,7 @@ const generateHKPProfile = async (id, keyserverDomain) => { let keyData = await doip.keys.process(key.publicKey) keyData.key.fetchMethod = 'hkp' keyData.key.uri = key.fetchURL - keyData.key.data = null + keyData.key.data = {} keyData = processKeyData(keyData) return { @@ -75,9 +75,9 @@ const generateHKPProfile = async (id, keyserverDomain) => { }) .catch(err => { return { - key: null, - keyData: null, - extra: null, + key: {}, + keyData: {}, + extra: {}, errors: [err.message] } }) @@ -88,7 +88,7 @@ const generateSignatureProfile = async (signature) => { .then(async key => { let keyData = key.keyData delete key.keyData - keyData.key.data = null + keyData.key.data = {} keyData = processKeyData(keyData) return { @@ -100,9 +100,9 @@ const generateSignatureProfile = async (signature) => { }) .catch(err => { return { - key: null, - keyData: null, - extra: null, + key: {}, + keyData: {}, + extra: {}, errors: [err.message] } }) @@ -114,7 +114,7 @@ const generateKeybaseProfile = async (username, fingerprint) => { let keyData = await doip.keys.process(key.publicKey) keyData.key.fetchMethod = 'hkp' keyData.key.uri = key.fetchURL - keyData.key.data = null + keyData.key.data = {} keyData = processKeyData(keyData) return { @@ -126,9 +126,9 @@ const generateKeybaseProfile = async (username, fingerprint) => { }) .catch(err => { return { - key: null, - keyData: null, - extra: null, + key: {}, + keyData: {}, + extra: {}, errors: [err.message] } }) diff --git a/yarn.lock b/yarn.lock index 9c8b463..8cea45e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -645,6 +645,16 @@ ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.6.3: + version "8.6.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" + integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz" @@ -2666,6 +2676,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" @@ -3706,6 +3721,11 @@ require-directory@^2.1.1: resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz"