Merge pull request #152 from Zamion101/feat/error-handling

Refactoring backend codebase to use central error handling and logging
This commit is contained in:
BlackMagiq
2022-12-24 20:43:40 -05:00
committed by GitHub
26 changed files with 1003 additions and 255 deletions
+2
View File
@@ -14,6 +14,8 @@ declare global {
JWT_SIGNUP_SECRET: string; JWT_SIGNUP_SECRET: string;
MONGO_URL: string; MONGO_URL: string;
NODE_ENV: 'development' | 'staging' | 'testing' | 'production'; NODE_ENV: 'development' | 'staging' | 'testing' | 'production';
VERBOSE_ERROR_OUTPUT: string;
LOKI_HOST: string;
CLIENT_ID_HEROKU: string; CLIENT_ID_HEROKU: string;
CLIENT_ID_VERCEL: string; CLIENT_ID_VERCEL: string;
CLIENT_ID_NETLIFY: string; CLIENT_ID_NETLIFY: string;
+408 -33
View File
@@ -1,7 +1,7 @@
{ {
"name": "infisical-api", "name": "infisical-api",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 2, "lockfileVersion": 1,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
@@ -12957,6 +12957,11 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true "dev": true
}, },
"@colors/colors": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="
},
"@cspotcode/source-map-support": { "@cspotcode/source-map-support": {
"version": "0.8.1", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -12966,6 +12971,16 @@
"@jridgewell/trace-mapping": "0.3.9" "@jridgewell/trace-mapping": "0.3.9"
} }
}, },
"@dabh/diagnostics": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
"requires": {
"colorspace": "1.1.x",
"enabled": "2.0.x",
"kuler": "^2.0.0"
}
},
"@eslint/eslintrc": { "@eslint/eslintrc": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
@@ -13413,6 +13428,84 @@
"maxmind": "^4.2.0" "maxmind": "^4.2.0"
} }
}, },
"@napi-rs/snappy-android-arm-eabi": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.1.1.tgz",
"integrity": "sha512-NKd/ztuVEgQaAaNVQ5zZaCB9VV+7+uBXBHqhaE5iSapQhLc41szTlT0s68FCee75OoT3vhqdA6Jp5TrzZ2WOaw==",
"optional": true
},
"@napi-rs/snappy-android-arm64": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm64/-/snappy-android-arm64-7.1.1.tgz",
"integrity": "sha512-DktruMAO0K0toTnxNHg2GWNIAPJqdvIchCsdsRaKyuEnG101qBg0mYiRCAhxHgbT6RJlOGbUPKkbA9KKRhEJUg==",
"optional": true
},
"@napi-rs/snappy-darwin-arm64": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-arm64/-/snappy-darwin-arm64-7.1.1.tgz",
"integrity": "sha512-3LZyoAw3Qa5F7sCCTkSkhmGlydwUKU6L3Jl46eKHO2Ctm8Gcjxww6T7MfwlwGZ6JqAM6d1d++WLzUZPCGXVmag==",
"optional": true
},
"@napi-rs/snappy-darwin-x64": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-x64/-/snappy-darwin-x64-7.1.1.tgz",
"integrity": "sha512-X1D2F67bQkPwr5iSR29/RnOrLwAkB55YO6t41toABzla3mflLDpzZcakz6FokIukykf7ey31/t73v/4pbgaBkg==",
"optional": true
},
"@napi-rs/snappy-freebsd-x64": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-freebsd-x64/-/snappy-freebsd-x64-7.1.1.tgz",
"integrity": "sha512-vSeuf+An8jFVHPAn5IbWE9hTGU9PFAaZLj/X7rKTQQtZstnDsHgWe6u4g7FHLuOdwQ8TvhcxAEpNlYIXIk4AJg==",
"optional": true
},
"@napi-rs/snappy-linux-arm-gnueabihf": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm-gnueabihf/-/snappy-linux-arm-gnueabihf-7.1.1.tgz",
"integrity": "sha512-/yyN6QsnOs3D1+jI3SfRX+gtnD86rbixdfmgxv9g40+FrDaDTLAu/3VuZIqH02qqq/xiWbDnkO+42RGxXDzTCw==",
"optional": true
},
"@napi-rs/snappy-linux-arm64-gnu": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-gnu/-/snappy-linux-arm64-gnu-7.1.1.tgz",
"integrity": "sha512-StEeUCSwUoajgrBtiCQPTkHu+0Q4QlYndghGZNdbN1zJ1ny70YzPpevaFBUyjI/eJ+FN9uICKtwTPtQNSILS5g==",
"optional": true
},
"@napi-rs/snappy-linux-arm64-musl": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-musl/-/snappy-linux-arm64-musl-7.1.1.tgz",
"integrity": "sha512-jWEBRzj+lswZVYf0b5eY0fjMlBL9L9yqjmTuv2UIMjJNHPuR282LK/s3Fm9sYIXQtKkiCo5JyhmIcoghZ3v0Eg==",
"optional": true
},
"@napi-rs/snappy-linux-x64-gnu": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-gnu/-/snappy-linux-x64-gnu-7.1.1.tgz",
"integrity": "sha512-41DPoAUFAU9VNrj/96qKfStH2Xq88ZYIsSz8BlITDm2ScoeDGOGbmaWguCXU7I+bC2uKWTmUVMXKqk6tVY6LEg==",
"optional": true
},
"@napi-rs/snappy-linux-x64-musl": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-musl/-/snappy-linux-x64-musl-7.1.1.tgz",
"integrity": "sha512-xR4hzFQqVq6J8Zf6XyUVtFJBaRgDyAQYUoBsCr92tZ7gI/0RlWCV6Q6JMO/wP5CSsvyFJIAtSUXXqlzIpw0GPA==",
"optional": true
},
"@napi-rs/snappy-win32-arm64-msvc": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-arm64-msvc/-/snappy-win32-arm64-msvc-7.1.1.tgz",
"integrity": "sha512-2mHPadctsaYtrfSV5Na8ooTdI5rflPxP1pceY4us6vbjeWrfgB+KQCuEFOHsGXqFNfsi6L9nWH8nB9swnxnSyw==",
"optional": true
},
"@napi-rs/snappy-win32-ia32-msvc": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-ia32-msvc/-/snappy-win32-ia32-msvc-7.1.1.tgz",
"integrity": "sha512-FOMgs9W71hdgjyl3T9F7b/WEIuoryfgBqsyhtHjAaa/98R0BUHl0bOoHg8ka0b5GgnhLBHkX2Yd6VD+Si9Q2ww==",
"optional": true
},
"@napi-rs/snappy-win32-x64-msvc": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-x64-msvc/-/snappy-win32-x64-msvc-7.1.1.tgz",
"integrity": "sha512-Mu3yELySvzhBcNTVCq+hYxVh+lH3/KjoQ5HIEb3DDPoX0AGRTm3XZa+usq8pFWjl91Cgp9nWK+9lVSkCCIRaKA==",
"optional": true
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -13560,6 +13653,60 @@
"@maxmind/geoip2-node": "^3.4.0" "@maxmind/geoip2-node": "^3.4.0"
} }
}, },
"@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
},
"@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
},
"@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
},
"@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
},
"@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"requires": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
},
"@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
},
"@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
},
"@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
},
"@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"@sentry/node": { "@sentry/node": {
"version": "7.19.0", "version": "7.19.0",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz",
@@ -13852,6 +13999,10 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
"@types/libsodium-wrappers": { "@types/libsodium-wrappers": {
"version": "0.7.10", "version": "0.7.10",
"resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
@@ -13877,6 +14028,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/prettier": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
"integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==",
"dev": true
},
"@types/qs": { "@types/qs": {
"version": "6.9.7", "version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@@ -14098,8 +14255,7 @@
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true, "dev": true
"requires": {}
}, },
"acorn-walk": { "acorn-walk": {
"version": "8.2.0", "version": "8.2.0",
@@ -14204,6 +14360,16 @@
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true "dev": true
}, },
"async": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
"integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
},
"async-exit-hook": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw=="
},
"asynckit": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -14414,6 +14580,11 @@
"buffer": "^5.6.0" "buffer": "^5.6.0"
} }
}, },
"btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
},
"buffer": { "buffer": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -14565,6 +14736,30 @@
"integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
"dev": true "dev": true
}, },
"color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
"requires": {
"color-convert": "^1.9.3",
"color-string": "^1.6.0"
},
"dependencies": {
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
}
}
},
"color-convert": { "color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -14577,8 +14772,25 @@
"color-name": { "color-name": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"dev": true },
"color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"colorspace": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
"requires": {
"color": "^3.1.3",
"text-hex": "1.0.x"
}
}, },
"combined-stream": { "combined-stream": {
"version": "1.0.8", "version": "1.0.8",
@@ -14846,6 +15058,11 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true "dev": true
}, },
"enabled": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
},
"encodeurl": { "encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -15140,8 +15357,7 @@
"express-rate-limit": { "express-rate-limit": {
"version": "6.7.0", "version": "6.7.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
"integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==", "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA=="
"requires": {}
}, },
"express-validator": { "express-validator": {
"version": "6.14.2", "version": "6.14.2",
@@ -15233,6 +15449,11 @@
"bser": "2.1.1" "bser": "2.1.1"
} }
}, },
"fecha": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
},
"file-entry-cache": { "file-entry-cache": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -15311,6 +15532,11 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true "dev": true
}, },
"fn.name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
},
"follow-redirects": { "follow-redirects": {
"version": "1.15.2", "version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
@@ -15711,8 +15937,7 @@
"is-stream": { "is-stream": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
"dev": true
}, },
"isexe": { "isexe": {
"version": "2.0.0", "version": "2.0.0",
@@ -16018,8 +16243,7 @@
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
"integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
"dev": true, "dev": true
"requires": {}
}, },
"jest-regex-util": { "jest-regex-util": {
"version": "29.2.0", "version": "29.2.0",
@@ -16129,6 +16353,7 @@
"@jest/transform": "^29.3.1", "@jest/transform": "^29.3.1",
"@jest/types": "^29.3.1", "@jest/types": "^29.3.1",
"@types/babel__traverse": "^7.0.6", "@types/babel__traverse": "^7.0.6",
"@types/prettier": "^2.1.5",
"babel-preset-current-node-syntax": "^1.0.0", "babel-preset-current-node-syntax": "^1.0.0",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"expect": "^29.3.1", "expect": "^29.3.1",
@@ -16336,6 +16561,11 @@
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"dev": true "dev": true
}, },
"kuler": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
},
"leven": { "leven": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -16397,10 +16627,27 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true "dev": true
}, },
"lru_map": { "lodash.once": {
"version": "0.3.3", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
},
"logform": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz",
"integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==",
"requires": {
"@colors/colors": "1.5.0",
"fecha": "^4.2.0",
"ms": "^2.1.1",
"safe-stable-stringify": "^2.3.1",
"triple-beam": "^1.3.0"
}
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
}, },
"lru-cache": { "lru-cache": {
"version": "6.0.0", "version": "6.0.0",
@@ -16410,6 +16657,11 @@
"yallist": "^4.0.0" "yallist": "^4.0.0"
} }
}, },
"lru_map": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
"integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="
},
"make-dir": { "make-dir": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -18393,14 +18645,6 @@
"minipass": "^3.1.1" "minipass": "^3.1.1"
} }
}, },
"string_decoder": {
"version": "1.3.0",
"bundled": true,
"dev": true,
"requires": {
"safe-buffer": "~5.2.0"
}
},
"string-width": { "string-width": {
"version": "4.2.3", "version": "4.2.3",
"bundled": true, "bundled": true,
@@ -18411,6 +18655,14 @@
"strip-ansi": "^6.0.1" "strip-ansi": "^6.0.1"
} }
}, },
"string_decoder": {
"version": "1.3.0",
"bundled": true,
"dev": true,
"requires": {
"safe-buffer": "~5.2.0"
}
},
"strip-ansi": { "strip-ansi": {
"version": "6.0.1", "version": "6.0.1",
"bundled": true, "bundled": true,
@@ -18578,6 +18830,14 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"one-time": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
"requires": {
"fn.name": "1.x.x"
}
},
"onetime": { "onetime": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
@@ -18805,6 +19065,26 @@
"sisteransi": "^1.0.5" "sisteransi": "^1.0.5"
} }
}, },
"protobufjs": {
"version": "6.11.3",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
"integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==",
"requires": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/long": "^4.0.1",
"@types/node": ">=13.7.0",
"long": "^4.0.0"
}
},
"proxy-addr": { "proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -18999,6 +19279,11 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}, },
"safe-stable-stringify": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz",
"integrity": "sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA=="
},
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -19124,6 +19409,21 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true "dev": true
}, },
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"requires": {
"is-arrayish": "^0.3.1"
},
"dependencies": {
"is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
}
}
},
"simple-update-notifier": { "simple-update-notifier": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
@@ -19158,6 +19458,27 @@
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
}, },
"snappy": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/snappy/-/snappy-7.1.1.tgz",
"integrity": "sha512-mL7GGPJ+WdsaFT5aR/uEqCq8cPg2VbhyifDEP7AeqIVDsAC8LBGYbZP1Qzoa2Ym84OW7JEQXqIpwqFp1EQw5BA==",
"optional": true,
"requires": {
"@napi-rs/snappy-android-arm-eabi": "7.1.1",
"@napi-rs/snappy-android-arm64": "7.1.1",
"@napi-rs/snappy-darwin-arm64": "7.1.1",
"@napi-rs/snappy-darwin-x64": "7.1.1",
"@napi-rs/snappy-freebsd-x64": "7.1.1",
"@napi-rs/snappy-linux-arm-gnueabihf": "7.1.1",
"@napi-rs/snappy-linux-arm64-gnu": "7.1.1",
"@napi-rs/snappy-linux-arm64-musl": "7.1.1",
"@napi-rs/snappy-linux-x64-gnu": "7.1.1",
"@napi-rs/snappy-linux-x64-musl": "7.1.1",
"@napi-rs/snappy-win32-arm64-msvc": "7.1.1",
"@napi-rs/snappy-win32-ia32-msvc": "7.1.1",
"@napi-rs/snappy-win32-x64-msvc": "7.1.1"
}
},
"socks": { "socks": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
@@ -19202,6 +19523,11 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true "dev": true
}, },
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="
},
"stack-utils": { "stack-utils": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -19234,14 +19560,6 @@
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
}, },
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"string-length": { "string-length": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
@@ -19263,6 +19581,14 @@
"strip-ansi": "^6.0.1" "strip-ansi": "^6.0.1"
} }
}, },
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"strip-ansi": { "strip-ansi": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -19367,6 +19693,11 @@
"minimatch": "^3.0.4" "minimatch": "^3.0.4"
} }
}, },
"text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
},
"text-table": { "text-table": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -19422,6 +19753,11 @@
"punycode": "^2.1.1" "punycode": "^2.1.1"
} }
}, },
"triple-beam": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
},
"ts-jest": { "ts-jest": {
"version": "29.0.3", "version": "29.0.3",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz",
@@ -19572,8 +19908,7 @@
"uuid": { "uuid": {
"version": "8.3.2", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"devOptional": true
}, },
"v8-compile-cache-lib": { "v8-compile-cache-lib": {
"version": "3.0.1", "version": "3.0.1",
@@ -19663,6 +19998,46 @@
"isexe": "^2.0.0" "isexe": "^2.0.0"
} }
}, },
"winston": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz",
"integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==",
"requires": {
"@colors/colors": "1.5.0",
"@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3",
"is-stream": "^2.0.0",
"logform": "^2.4.0",
"one-time": "^1.0.0",
"readable-stream": "^3.4.0",
"safe-stable-stringify": "^2.3.1",
"stack-trace": "0.0.x",
"triple-beam": "^1.3.0",
"winston-transport": "^4.5.0"
}
},
"winston-loki": {
"version": "6.0.6",
"resolved": "https://registry.npmjs.org/winston-loki/-/winston-loki-6.0.6.tgz",
"integrity": "sha512-cll+nv5T/b9uJXqca0N2WKL1JJNuJND9E6WOOAuSGkZ44L9VQ/QK9F+/5VKbv6LIP9p0nvPSOYxtACCDb/9iWw==",
"requires": {
"async-exit-hook": "2.0.1",
"btoa": "^1.2.1",
"protobufjs": "^6.8.8",
"snappy": "7.1.1",
"winston-transport": "^4.3.0"
}
},
"winston-transport": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
"integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
"requires": {
"logform": "^2.3.2",
"readable-stream": "^3.6.0",
"triple-beam": "^1.3.0"
}
},
"word-wrap": { "word-wrap": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+3 -1
View File
@@ -28,7 +28,9 @@
"stripe": "^10.7.0", "stripe": "^10.7.0",
"tweetnacl": "^1.0.3", "tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1", "tweetnacl-util": "^0.15.1",
"typescript": "^4.9.3" "typescript": "^4.9.3",
"winston": "^3.8.2",
"winston-loki": "^6.0.6"
}, },
"name": "infisical-api", "name": "infisical-api",
"version": "1.0.0", "version": "1.0.0",
+19 -2
View File
@@ -1,5 +1,5 @@
/* eslint-disable no-console */
import { patchRouterParam } from './utils/patchAsyncRoutes';
import express from 'express'; import express from 'express';
import helmet from 'helmet'; import helmet from 'helmet';
import cors from 'cors'; import cors from 'cors';
@@ -29,6 +29,12 @@ import {
integration as integrationRouter, integration as integrationRouter,
integrationAuth as integrationAuthRouter integrationAuth as integrationAuthRouter
} from './routes'; } from './routes';
import { getLogger } from './utils/logger';
import { RouteNotFoundError } from './utils/errors';
import { requestErrorHandler } from './middleware/requestErrorHandler';
//* Patch Async route params to handle Promise Rejections
patchRouterParam()
export const app = express(); export const app = express();
@@ -69,6 +75,17 @@ app.use('/api/v1/stripe', stripeRouter);
app.use('/api/v1/integration', integrationRouter); app.use('/api/v1/integration', integrationRouter);
app.use('/api/v1/integration-auth', integrationAuthRouter); app.use('/api/v1/integration-auth', integrationAuthRouter);
//* Handle unrouted requests and respond with proper error message as well as status code
app.use((req, res, next)=>{
if(res.headersSent) return next();
next(RouteNotFoundError({message: `The requested source '(${req.method})${req.url}' was not found`}))
})
//* Error Handling Middleware (must be after all routing logic)
app.use(requestErrorHandler)
export const server = app.listen(PORT, () => { export const server = app.listen(PORT, () => {
console.log(`Listening on PORT ${[PORT]}`); getLogger("backend-main").info(`Server started listening at port ${PORT}`)
}); });
+4
View File
@@ -10,6 +10,8 @@ const JWT_SIGNUP_LIFETIME = process.env.JWT_SIGNUP_LIFETIME! || '15m';
const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!; const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!;
const MONGO_URL = process.env.MONGO_URL!; const MONGO_URL = process.env.MONGO_URL!;
const NODE_ENV = process.env.NODE_ENV! || 'production'; const NODE_ENV = process.env.NODE_ENV! || 'production';
const VERBOSE_ERROR_OUTPUT = process.env.VERBOSE_ERROR_OUTPUT! === 'true' && true;
const LOKI_HOST = process.env.LOKI_HOST || undefined;
const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!; const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!;
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!; const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!;
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!; const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
@@ -53,6 +55,8 @@ export {
JWT_SIGNUP_SECRET, JWT_SIGNUP_SECRET,
MONGO_URL, MONGO_URL,
NODE_ENV, NODE_ENV,
VERBOSE_ERROR_OUTPUT,
LOKI_HOST,
CLIENT_ID_HEROKU, CLIENT_ID_HEROKU,
CLIENT_ID_VERCEL, CLIENT_ID_VERCEL,
CLIENT_ID_NETLIFY, CLIENT_ID_NETLIFY,
+15 -7
View File
@@ -2,18 +2,18 @@ import * as Sentry from '@sentry/node';
import { import {
Bot, Bot,
Integration, Integration,
IIntegration,
IntegrationAuth, IntegrationAuth,
IIntegrationAuth
} from '../models'; } from '../models';
import { exchangeCode, exchangeRefresh, syncSecrets } from '../integrations'; import { exchangeCode, exchangeRefresh, syncSecrets } from '../integrations';
import { BotService, IntegrationService } from '../services'; import { BotService } from '../services';
import { import {
ENV_DEV, ENV_DEV,
EVENT_PUSH_SECRETS, EVENT_PUSH_SECRETS,
INTEGRATION_VERCEL, INTEGRATION_VERCEL,
INTEGRATION_NETLIFY INTEGRATION_NETLIFY
} from '../variables'; } from '../variables';
import { UnauthorizedRequestError } from '../utils/errors';
import RequestError from '../utils/requestError';
interface Update { interface Update {
workspace: string; workspace: string;
@@ -176,12 +176,13 @@ const syncIntegrationsHelper = async ({
*/ */
const getIntegrationAuthRefreshHelper = async ({ integrationAuthId }: { integrationAuthId: string }) => { const getIntegrationAuthRefreshHelper = async ({ integrationAuthId }: { integrationAuthId: string }) => {
let refreshToken; let refreshToken;
try { try {
const integrationAuth = await IntegrationAuth const integrationAuth = await IntegrationAuth
.findById(integrationAuthId) .findById(integrationAuthId)
.select('+refreshCiphertext +refreshIV +refreshTag'); .select('+refreshCiphertext +refreshIV +refreshTag');
if (!integrationAuth) throw new Error('Failed to find integration auth'); if (!integrationAuth) throw UnauthorizedRequestError({message: 'Failed to locate Integration Authentication credentials'});
refreshToken = await BotService.decryptSymmetric({ refreshToken = await BotService.decryptSymmetric({
workspaceId: integrationAuth.workspace.toString(), workspaceId: integrationAuth.workspace.toString(),
@@ -193,7 +194,10 @@ const syncIntegrationsHelper = async ({
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error('Failed to get integration refresh token'); if(err instanceof RequestError)
throw err
else
throw new Error('Failed to get integration refresh token');
} }
return refreshToken; return refreshToken;
@@ -209,12 +213,13 @@ const syncIntegrationsHelper = async ({
*/ */
const getIntegrationAuthAccessHelper = async ({ integrationAuthId }: { integrationAuthId: string }) => { const getIntegrationAuthAccessHelper = async ({ integrationAuthId }: { integrationAuthId: string }) => {
let accessToken; let accessToken;
try { try {
const integrationAuth = await IntegrationAuth const integrationAuth = await IntegrationAuth
.findById(integrationAuthId) .findById(integrationAuthId)
.select('workspace integration +accessCiphertext +accessIV +accessTag +accessExpiresAt + refreshCiphertext'); .select('workspace integration +accessCiphertext +accessIV +accessTag +accessExpiresAt + refreshCiphertext');
if (!integrationAuth) throw new Error('Failed to find integration auth'); if (!integrationAuth) throw UnauthorizedRequestError({message: 'Failed to locate Integration Authentication credentials'});
accessToken = await BotService.decryptSymmetric({ accessToken = await BotService.decryptSymmetric({
workspaceId: integrationAuth.workspace.toString(), workspaceId: integrationAuth.workspace.toString(),
@@ -240,7 +245,10 @@ const getIntegrationAuthAccessHelper = async ({ integrationAuthId }: { integrati
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error('Failed to get integration access token'); if(err instanceof RequestError)
throw err
else
throw new Error('Failed to get integration access token');
} }
return accessToken; return accessToken;
+1
View File
@@ -21,6 +21,7 @@ const validateMembership = async ({
}) => { }) => {
let membership; let membership;
//TODO: Refactor code to take advantage of using RequestError. It's possible to create new types of errors for more detailed errors
try { try {
membership = await Membership.findOne({ membership = await Membership.findOne({
user: userId, user: userId,
+1
View File
@@ -53,6 +53,7 @@ const exchangeRefreshHeroku = async ({
refreshToken: string; refreshToken: string;
}) => { }) => {
let accessToken; let accessToken;
//TODO: Refactor code to take advantage of using RequestError. It's possible to create new types of errors for more detailed errors
try { try {
const res = await axios.post( const res = await axios.post(
INTEGRATION_HEROKU_TOKEN_URL, INTEGRATION_HEROKU_TOKEN_URL,
@@ -0,0 +1,29 @@
import { ErrorRequestHandler } from "express";
import * as Sentry from '@sentry/node';
import { InternalServerError } from "../utils/errors";
import { getLogger } from "../utils/logger";
import RequestError, { LogLevel } from "../utils/requestError";
export const requestErrorHandler: ErrorRequestHandler = (error: RequestError|Error, req, res, next) => {
if(res.headersSent) return next();
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
if(!(error instanceof RequestError)){
error = InternalServerError({context: {exception: error.message}, stack: error.stack})
getLogger('backend-main').log((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
}
//* Set Sentry user identification if req.user is populated
if(req.user !== undefined && req.user !== null){
Sentry.setUser({ email: req.user.email })
}
//* Only sent error to Sentry if LogLevel is one of the following level 'ERROR', 'EMERGENCY' or 'CRITICAL'
//* with this we will eliminate false-positive errors like 'BadRequestError', 'UnauthorizedRequestError' and so on
if([LogLevel.ERROR, LogLevel.EMERGENCY, LogLevel.CRITICAL].includes((<RequestError>error).level)){
Sentry.captureException(error)
}
res.status((<RequestError>error).statusCode).json((<RequestError>error).format(req))
next()
}
+16 -23
View File
@@ -1,8 +1,8 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import * as Sentry from '@sentry/node';
import { User } from '../models'; import { User } from '../models';
import { JWT_AUTH_SECRET } from '../config'; import { JWT_AUTH_SECRET } from '../config';
import { AccountNotFoundError, BadRequestError, UnauthorizedRequestError } from '../utils/errors';
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -20,32 +20,25 @@ declare module 'jsonwebtoken' {
*/ */
const requireAuth = async (req: Request, res: Response, next: NextFunction) => { const requireAuth = async (req: Request, res: Response, next: NextFunction) => {
// JWT authentication middleware // JWT authentication middleware
try { const [ AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE ] = <[string, string]>req.headers['authorization']?.split(' ', 2) ?? [null, null]
if (!req.headers?.authorization) if(AUTH_TOKEN_TYPE === null) return next(BadRequestError({message: `Missing Authorization Header in the request header.`}))
throw new Error('Failed to locate authorization header'); if(AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') return next(BadRequestError({message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`}))
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
const token = req.headers.authorization.split(' ')[1]; const decodedToken = <jwt.UserIDJwtPayload>(
const decodedToken = <jwt.UserIDJwtPayload>( jwt.verify(AUTH_TOKEN_VALUE, JWT_AUTH_SECRET)
jwt.verify(token, JWT_AUTH_SECRET) );
);
const user = await User.findOne({ const user = await User.findOne({
_id: decodedToken.userId _id: decodedToken.userId
}).select('+publicKey'); }).select('+publicKey');
if (!user) throw new Error('Failed to authenticate unfound user'); if (!user) return next(AccountNotFoundError({message: 'Failed to locate User account'}))
if (!user?.publicKey) if (!user?.publicKey)
throw new Error('Failed to authenticate not fully set up account'); return next(UnauthorizedRequestError({message: 'Unable to authenticate due to partially set up account'}))
req.user = user; req.user = user;
return next(); return next();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(401).send({
error: 'Failed to authenticate user. Try logging in'
});
}
}; };
export default requireAuth; export default requireAuth;
+15 -23
View File
@@ -1,7 +1,7 @@
import * as Sentry from '@sentry/node';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { Bot } from '../models'; import { Bot } from '../models';
import { validateMembership } from '../helpers/membership'; import { validateMembership } from '../helpers/membership';
import { AccountNotFoundError } from '../utils/errors';
type req = 'params' | 'body' | 'query'; type req = 'params' | 'body' | 'query';
@@ -15,30 +15,22 @@ const requireBotAuth = ({
location?: req; location?: req;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { const bot = await Bot.findOne({ _id: req[location].botId });
const bot = await Bot.findOne({ _id: req[location].botId });
if (!bot) { if (!bot) {
throw new Error('Failed to find bot'); return next(AccountNotFoundError({message: 'Failed to locate Bot account'}))
}
await validateMembership({
userId: req.user._id.toString(),
workspaceId: bot.workspace.toString(),
acceptedRoles,
acceptedStatuses
});
req.bot = bot;
next();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(401).send({
error: 'Failed bot authorization'
});
} }
await validateMembership({
userId: req.user._id.toString(),
workspaceId: bot.workspace.toString(),
acceptedRoles,
acceptedStatuses
});
req.bot = bot;
next();
} }
} }
@@ -1,8 +1,8 @@
import * as Sentry from '@sentry/node';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { Bot, Integration, IntegrationAuth, Membership } from '../models'; import { Integration, IntegrationAuth } from '../models';
import { IntegrationService } from '../services'; import { IntegrationService } from '../services';
import { validateMembership } from '../helpers/membership'; import { validateMembership } from '../helpers/membership';
import { IntegrationNotFoundError, UnauthorizedRequestError } from '../utils/errors';
/** /**
* Validate if user on request is a member of workspace with proper roles associated * Validate if user on request is a member of workspace with proper roles associated
@@ -21,48 +21,40 @@ const requireIntegrationAuth = ({
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
// integration authorization middleware // integration authorization middleware
try { const { integrationId } = req.params;
const { integrationId } = req.params;
// validate integration accessibility // validate integration accessibility
const integration = await Integration.findOne({ const integration = await Integration.findOne({
_id: integrationId _id: integrationId
}); });
if (!integration) { if (!integration) {
throw new Error('Failed to find integration'); return next(IntegrationNotFoundError({message: 'Failed to locate Integration'}))
}
await validateMembership({
userId: req.user._id.toString(),
workspaceId: integration.workspace.toString(),
acceptedRoles,
acceptedStatuses
});
const integrationAuth = await IntegrationAuth.findOne({
_id: integration.integrationAuth
}).select(
'+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt'
);
if (!integrationAuth) {
throw new Error('Failed to find integration authorization');
}
req.integration = integration;
req.accessToken = await IntegrationService.getIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString()
});
return next();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(401).send({
error: 'Failed integration authorization'
});
} }
await validateMembership({
userId: req.user._id.toString(),
workspaceId: integration.workspace.toString(),
acceptedRoles,
acceptedStatuses
});
const integrationAuth = await IntegrationAuth.findOne({
_id: integration.integrationAuth
}).select(
'+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt'
);
if (!integrationAuth) {
return next(UnauthorizedRequestError({message: 'Failed to locate Integration Authentication credentials'}))
}
req.integration = integration;
req.accessToken = await IntegrationService.getIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString()
});
return next();
}; };
}; };
@@ -3,6 +3,7 @@ import { Request, Response, NextFunction } from 'express';
import { IntegrationAuth } from '../models'; import { IntegrationAuth } from '../models';
import { IntegrationService } from '../services'; import { IntegrationService } from '../services';
import { validateMembership } from '../helpers/membership'; import { validateMembership } from '../helpers/membership';
import { UnauthorizedRequestError } from '../utils/errors';
/** /**
* Validate if user on request is a member of workspace with proper roles associated * Validate if user on request is a member of workspace with proper roles associated
@@ -22,41 +23,33 @@ const requireIntegrationAuthorizationAuth = ({
attachAccessToken?: boolean; attachAccessToken?: boolean;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { const { integrationAuthId } = req.params;
const { integrationAuthId } = req.params;
const integrationAuth = await IntegrationAuth.findOne({ const integrationAuth = await IntegrationAuth.findOne({
_id: integrationAuthId _id: integrationAuthId
}).select( }).select(
'+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt' '+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt'
); );
if (!integrationAuth) { if (!integrationAuth) {
throw new Error('Failed to find integration authorization'); return next(UnauthorizedRequestError({message: 'Failed to locate Integration Authorization credentials'}))
} }
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: integrationAuth.workspace.toString(), workspaceId: integrationAuth.workspace.toString(),
acceptedRoles, acceptedRoles,
acceptedStatuses acceptedStatuses
}); });
req.integrationAuth = integrationAuth; req.integrationAuth = integrationAuth;
if (attachAccessToken) { if (attachAccessToken) {
req.accessToken = await IntegrationService.getIntegrationAuthAccess({ req.accessToken = await IntegrationService.getIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString() integrationAuthId: integrationAuth._id.toString()
});
}
return next();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(401).send({
error: 'Failed (authorization) integration authorizationt'
}); });
} }
return next();
}; };
}; };
@@ -1,6 +1,6 @@
import * as Sentry from '@sentry/node';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { IOrganization, MembershipOrg } from '../models'; import { IOrganization, MembershipOrg } from '../models';
import { UnauthorizedRequestError, ValidationError } from '../utils/errors';
/** /**
* Validate if user on request is a member with proper roles for organization * Validate if user on request is a member with proper roles for organization
@@ -19,35 +19,28 @@ const requireOrganizationAuth = ({
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
// organization authorization middleware // organization authorization middleware
try { // validate organization membership
// validate organization membership const membershipOrg = await MembershipOrg.findOne({
const membershipOrg = await MembershipOrg.findOne({ user: req.user._id,
user: req.user._id, organization: req.params.organizationId
organization: req.params.organizationId }).populate<{ organization: IOrganization }>('organization');
}).populate<{ organization: IOrganization }>('organization');
if (!membershipOrg) {
throw new Error('Failed to find organization membership');
}
if (!acceptedRoles.includes(membershipOrg.role)) { if (!membershipOrg) {
throw new Error('Failed to validate organization membership role'); return next(UnauthorizedRequestError({message: "You're not a member of this Organization."}))
}
if (!acceptedStatuses.includes(membershipOrg.status)) {
throw new Error('Failed to validate organization membership status');
}
req.membershipOrg = membershipOrg;
return next();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(401).send({
error: 'Failed organization authorization'
});
} }
//TODO is this important to validate? I mean is it possible to save wrong role to database or get wrong role from databse? - Zamion101
if (!acceptedRoles.includes(membershipOrg.role)) {
return next(ValidationError({message: 'Failed to validate Organization Membership Role'}))
}
if (!acceptedStatuses.includes(membershipOrg.status)) {
return next(ValidationError({message: 'Failed to validate Organization Membership Status'}))
}
req.membershipOrg = membershipOrg;
return next();
}; };
}; };
@@ -1,8 +1,8 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import * as Sentry from '@sentry/node';
import { ServiceToken } from '../models'; import { ServiceToken } from '../models';
import { JWT_SERVICE_SECRET } from '../config'; import { JWT_SERVICE_SECRET } from '../config';
import { BadRequestError, UnauthorizedRequestError } from '../utils/errors';
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -24,33 +24,27 @@ const requireServiceTokenAuth = async (
next: NextFunction next: NextFunction
) => { ) => {
// JWT service token middleware // JWT service token middleware
try {
if (!req.headers?.authorization)
throw new Error('Failed to locate authorization header');
const token = req.headers.authorization.split(' ')[1]; const [ AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE ] = <[string, string]>req.headers['authorization']?.split(' ', 2) ?? [null, null]
if(AUTH_TOKEN_TYPE === null) return next(BadRequestError({message: `Missing Authorization Header in the request header.`}))
//TODO: Determine what is the actual Token Type for Service Token Authentication (ex. Bearer)
//if(AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') return next(UnauthorizedRequestError({message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`}))
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(token, JWT_SERVICE_SECRET) jwt.verify(AUTH_TOKEN_VALUE, JWT_SERVICE_SECRET)
); );
const serviceToken = await ServiceToken.findOne({ const serviceToken = await ServiceToken.findOne({
_id: decodedToken.serviceTokenId _id: decodedToken.serviceTokenId
}) })
.populate('user', '+publicKey') .populate('user', '+publicKey')
.select('+encryptedKey +publicKey +nonce'); .select('+encryptedKey +publicKey +nonce');
if (!serviceToken) throw new Error('Failed to find service token'); if (!serviceToken) return next(UnauthorizedRequestError({message: 'The service token does not match the record in the database'}))
req.serviceToken = serviceToken; req.serviceToken = serviceToken;
return next(); return next();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(401).send({
error: 'Failed to authenticate service token'
});
}
}; };
export default requireServiceTokenAuth; export default requireServiceTokenAuth;
+15 -23
View File
@@ -1,8 +1,8 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import * as Sentry from '@sentry/node';
import { User } from '../models'; import { User } from '../models';
import { JWT_SIGNUP_SECRET } from '../config'; import { JWT_SIGNUP_SECRET } from '../config';
import { BadRequestError, UnauthorizedRequestError } from '../utils/errors';
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -21,32 +21,24 @@ const requireSignupAuth = async (
) => { ) => {
// JWT (temporary) authentication middleware for complete signup // JWT (temporary) authentication middleware for complete signup
try { const [ AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE ] = <[string, string]>req.headers['authorization']?.split(' ', 2) ?? [null, null]
if (!req.headers?.authorization) if(AUTH_TOKEN_TYPE === null) return next(BadRequestError({message: `Missing Authorization Header in the request header.`}))
throw new Error('Failed to locate authorization header'); if(AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') return next(BadRequestError({message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`}))
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
const token = req.headers.authorization.split(' ')[1]; const decodedToken = <jwt.UserIDJwtPayload>(
const decodedToken = <jwt.UserIDJwtPayload>( jwt.verify(AUTH_TOKEN_VALUE, JWT_SIGNUP_SECRET)
jwt.verify(token, JWT_SIGNUP_SECRET) );
);
const user = await User.findOne({ const user = await User.findOne({
_id: decodedToken.userId _id: decodedToken.userId
}).select('+publicKey'); }).select('+publicKey');
if (!user) if (!user)
throw new Error('Failed to temporarily authenticate unfound user'); return next(UnauthorizedRequestError({message: 'Unable to authenticate for User account completion. Try logging in again'}))
req.user = user; req.user = user;
return next(); return next();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(401).send({
error:
'Failed to temporarily authenticate user for complete account. Try logging in'
});
}
}; };
export default requireSignupAuth; export default requireSignupAuth;
@@ -1,6 +1,6 @@
import * as Sentry from '@sentry/node';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { validateMembership } from '../helpers/membership'; import { validateMembership } from '../helpers/membership';
import { UnauthorizedRequestError } from '../utils/errors';
type req = 'params' | 'body' | 'query'; type req = 'params' | 'body' | 'query';
@@ -36,11 +36,7 @@ const requireWorkspaceAuth = ({
return next(); return next();
} catch (err) { } catch (err) {
Sentry.setUser(null); return next(UnauthorizedRequestError({message: 'Unable to authenticate workspace'}))
Sentry.captureException(err);
return res.status(401).send({
error: 'Failed workspace authorization'
});
} }
}; };
}; };
+3 -7
View File
@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import * as Sentry from '@sentry/node';
import { validationResult } from 'express-validator'; import { validationResult } from 'express-validator';
import { BadRequestError, UnauthorizedRequestError } from '../utils/errors';
/** /**
* Validate intended inputs on [req] via express-validator * Validate intended inputs on [req] via express-validator
@@ -15,16 +15,12 @@ const validate = (req: Request, res: Response, next: NextFunction) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() }); return next(BadRequestError({context: {errors: errors.array}}))
} }
return next(); return next();
} catch (err) { } catch (err) {
Sentry.setUser(null); return next(UnauthorizedRequestError({message: 'Unauthenticated requests are not allowed. Try logging in'}))
Sentry.captureException(err);
return res.status(401).send({
error: "Looks like you're unauthenticated . Try logging in"
});
} }
}; };
+9 -1
View File
@@ -5,8 +5,16 @@ import {
POSTHOG_PROJECT_API_KEY, POSTHOG_PROJECT_API_KEY,
TELEMETRY_ENABLED TELEMETRY_ENABLED
} from '../config'; } from '../config';
import { getLogger } from '../utils/logger';
console.log('TELEMETRY_ENABLED: ', TELEMETRY_ENABLED); if(TELEMETRY_ENABLED){
getLogger("backend-main").info([
"",
"Infisical collects telemetry data about general usage.",
"The data helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth for investors as we support Infisical as open-source software.",
"To opt out of telemetry, you can set `TELEMETRY_ENABLED=false` within the environment variables",
].join('\n'))
}
let postHogClient: any; let postHogClient: any;
if (NODE_ENV === 'production' && TELEMETRY_ENABLED) { if (NODE_ENV === 'production' && TELEMETRY_ENABLED) {
+3 -3
View File
@@ -1,10 +1,10 @@
/* eslint-disable no-console */
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { getLogger } from '../utils/logger';
export const initDatabase = (MONGO_URL: string) => { export const initDatabase = (MONGO_URL: string) => {
mongoose mongoose
.connect(MONGO_URL) .connect(MONGO_URL)
.then(() => console.log('Successfully connected to DB')) .then(() => getLogger("database").info("Database connection established"))
.catch((e) => console.log('Failed to connect to DB ', e)); .catch((e) => getLogger("database").error(`Unable to establish Database connection due to the error.\n${e}`));
return mongoose.connection; return mongoose.connection;
}; };
+2 -2
View File
@@ -1,10 +1,10 @@
/* eslint-disable no-console */
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { createTerminus } from '@godaddy/terminus'; import { createTerminus } from '@godaddy/terminus';
import { getLogger } from '../utils/logger';
export const setUpHealthEndpoint = <T>(server: T) => { export const setUpHealthEndpoint = <T>(server: T) => {
const onSignal = () => { const onSignal = () => {
console.log('Server is starting clean-up'); getLogger('backend-main').info('Server is starting clean-up');
return Promise.all([ return Promise.all([
new Promise((resolve) => { new Promise((resolve) => {
if (mongoose.connection && mongoose.connection.readyState == 1) { if (mongoose.connection && mongoose.connection.readyState == 1) {
+1
View File
@@ -1,5 +1,6 @@
import * as express from 'express'; import * as express from 'express';
// TODO: fix (any) types // TODO: fix (any) types
declare global { declare global {
namespace Express { namespace Express {
+116
View File
@@ -0,0 +1,116 @@
import RequestError, { LogLevel, RequestErrorContext } from "./requestError"
//* ----->[GENERAL HTTP ERRORS]<-----
export const RouteNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.INFO,
statusCode: error?.statusCode ?? 404,
type: error?.type ?? 'route_not_found',
message: error?.message ?? 'The requested source was not found',
context: error?.context,
stack: error?.stack
})
export const MethodNotAllowedError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.INFO,
statusCode: error?.statusCode ?? 405,
type: error?.type ?? 'method_not_allowed',
message: error?.message ?? 'The requested method is not allowed for the resource',
context: error?.context,
stack: error?.stack
})
export const UnauthorizedRequestError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.INFO,
statusCode: error?.statusCode ?? 401,
type: error?.type ?? 'unauthorized',
message: error?.message ?? 'You are not authorized to access this resource',
context: error?.context,
stack: error?.stack
})
export const ForbiddenRequestError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.INFO,
statusCode: error?.statusCode ?? 403,
type: error?.type ?? 'forbidden',
message: error?.message ?? 'You are not allowed to access this resource',
context: error?.context,
stack: error?.stack
})
export const BadRequestError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.INFO,
statusCode: error?.statusCode ?? 400,
type: error?.type ?? 'bad_request',
message: error?.message ?? 'The request is invalid or cannot be served',
context: error?.context,
stack: error?.stack
})
export const InternalServerError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 500,
type: error?.type ?? 'internal_server_error',
message: error?.message ?? 'The server encountered an error while processing the request',
context: error?.context,
stack: error?.stack
})
export const ServiceUnavailableError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 503,
type: error?.type ?? 'service_unavailable',
message: error?.message ?? 'The service is currently unavailable. Please try again later.',
context: error?.context,
stack: error?.stack
})
export const ValidationError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 400,
type: error?.type ?? 'validation_error',
message: error?.message ?? 'The request failed validation',
context: error?.context,
stack: error?.stack
})
//* ----->[INTEGRATION ERRORS]<-----
export const IntegrationNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 404,
type: error?.type ?? 'integration_not_found_error',
message: error?.message ?? 'The requested integration was not found',
context: error?.context,
stack: error?.stack
})
//* ----->[WORKSPACE ERRORS]<-----
export const WorkspaceNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 404,
type: error?.type ?? 'workspace_not_found_error',
message: error?.message ?? 'The requested workspace was not found',
context: error?.context,
stack: error?.stack
})
//* ----->[ORGANIZATION ERRORS]<-----
export const OrganizationNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 404,
type: error?.type ?? 'organization_not_found_error',
message: error?.message ?? 'The requested organization was not found',
context: error?.context,
stack: error?.stack
})
//* ----->[ACCOUNT ERRORS]<-----
export const AccountNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 404,
type: error?.type ?? 'account_not_found_error',
message: error?.message ?? 'The requested account was not found',
context: error?.context,
stack: error?.stack
})
//* ----->[MISC ERRORS]<-----
+65
View File
@@ -0,0 +1,65 @@
/* eslint-disable no-console */
import { createLogger, format, transports } from 'winston';
import LokiTransport from 'winston-loki';
import { LOKI_HOST, NODE_ENV } from '../config';
const { combine, colorize, label, printf, splat, timestamp } = format;
const logFormat = (prefix: string) => combine(
timestamp(),
splat(),
label({ label: prefix }),
printf((info) => `${info.timestamp} ${info.label} ${info.level}: ${info.message}`)
);
const createLoggerWithLabel = (level: string, label: string) => {
const _level = level.toLowerCase() || 'info'
//* Always add Console output to transports
const _transports: any[] = [
new transports.Console({
format: combine(
colorize(),
logFormat(label),
// format.json()
)
})
]
//* Add LokiTransport if it's enabled
if(LOKI_HOST !== undefined){
_transports.push(
new LokiTransport({
host: LOKI_HOST,
handleExceptions: true,
handleRejections: true,
batching: true,
level: _level,
timeout: 30000,
format: format.combine(
format.json()
),
labels: {app: process.env.npm_package_name, version: process.env.npm_package_version, environment: NODE_ENV},
onConnectionError: (err: Error)=> console.error('Connection error while connecting to Loki Server.\n', err)
})
)
}
return createLogger({
level: _level,
transports: _transports,
format: format.combine(
logFormat(label),
format.metadata({ fillExcept: ['message', 'level', 'timestamp', 'label'] })
)
});
}
const DEFAULT_LOGGERS = {
"backend-main": createLoggerWithLabel('info', '[IFSC:backend-main]'),
"database": createLoggerWithLabel('info', '[IFSC:database]'),
}
type LoggerNames = keyof typeof DEFAULT_LOGGERS
export const getLogger = (loggerName: LoggerNames) => {
return DEFAULT_LOGGERS[loggerName]
}
+65
View File
@@ -0,0 +1,65 @@
/*
Original work Copyright (c) 2016, Nikolay Nemshilov <nemshilov@gmail.com>
Modified work Copyright (c) 2016, David Banham <david@banham.id.au>
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-env node */
const Layer = require('express/lib/router/layer');
const Router = require('express/lib/router');
const last = (arr = []) => arr[arr.length - 1];
const noop = Function.prototype;
function copyFnProps(oldFn, newFn) {
Object.keys(oldFn).forEach((key) => {
newFn[key] = oldFn[key];
});
return newFn;
}
function wrap(fn) {
const newFn = function newFn(...args) {
const ret = fn.apply(this, args);
const next = (args.length === 5 ? args[2] : last(args)) || noop;
if (ret && ret.catch) ret.catch(err => next(err));
return ret;
};
Object.defineProperty(newFn, 'length', {
value: fn.length,
writable: false,
});
return copyFnProps(fn, newFn);
}
export function patchRouterParam() {
const originalParam = Router.prototype.constructor.param;
Router.prototype.constructor.param = function param(name, fn) {
fn = wrap(fn);
return originalParam.call(this, name, fn);
};
}
Object.defineProperty(Layer.prototype, 'handle', {
enumerable: true,
get() {
return this.__handle;
},
set(fn) {
fn = wrap(fn);
this.__handle = fn;
},
});
+113
View File
@@ -0,0 +1,113 @@
import { Request } from 'express'
import { VERBOSE_ERROR_OUTPUT } from '../config'
export enum LogLevel {
DEBUG = 100,
INFO = 200,
NOTICE = 250,
WARNING = 300,
ERROR = 400,
CRITICAL = 500,
ALERT = 550,
EMERGENCY = 600,
}
export type RequestErrorContext = {
logLevel?: LogLevel,
statusCode: number,
type: string,
message: string,
context?: Record<string, unknown>,
stack?: string|undefined
}
export default class RequestError extends Error{
private _logLevel: LogLevel
private _logName: string
statusCode: number
type: string
context: Record<string, unknown>
extra: Record<string, string|number|symbol>[]
private stacktrace: string|undefined|string[]
constructor(
{logLevel, statusCode, type, message, context, stack} : RequestErrorContext
){
super(message)
this._logLevel = logLevel || LogLevel.INFO
this._logName = LogLevel[this._logLevel]
this.statusCode = statusCode
this.type = type
this.context = context || {}
this.extra = []
if(stack) this.stack = stack
else Error.captureStackTrace(this, this.constructor)
this.stacktrace = this.stack?.split('\n')
}
static convertFrom(error: Error) {
//This error was not handled by error handler. Please report this incident to the staff.
return new RequestError({
logLevel: LogLevel.ERROR,
statusCode: 500,
type: 'internal_server_error',
message: 'This error was not handled by error handler. Please report this incident to the staff',
context: {
message: error.message,
name: error.name
},
stack: error.stack
})
}
get level(){ return this._logLevel }
get levelName(){ return this._logName }
withTags(...tags: string[]|number[]){
this.context['tags'] = Object.assign(tags, this.context['tags'])
return this
}
withExtras(...extras: Record<string, string|boolean|number>[]){
this.extra = Object.assign(extras, this.extra)
return this
}
private _omit(obj: any, keys: string[]): typeof obj{
const exclude = new Set(keys)
obj = Object.fromEntries(Object.entries(obj).filter(e => !exclude.has(e[0])))
return obj
}
public format(req: Request){
let _context = Object.assign({
stacktrace: this.stacktrace
}, this.context)
//* Omit sensitive information from context that can leak internal workings of this program if user is not developer
if(!VERBOSE_ERROR_OUTPUT){
_context = this._omit(_context, [
'stacktrace',
'exception',
])
}
const formatObject = {
type: this.type,
message: this.message,
context: _context,
level: this.level,
level_name: this.levelName,
status_code: this.statusCode,
datetime_iso: new Date().toISOString(),
application: process.env.npm_package_name || 'unknown',
request_id: req.headers["Request-Id"],
extra: this.extra
}
return formatObject
}
}