🎨 Body Parser error

This commit is contained in:
Flam3rboy 2021-02-02 00:51:00 +01:00
parent 7998b39757
commit 8505d9279b
14 changed files with 143 additions and 36 deletions

73
package-lock.json generated
View File

@ -70,6 +70,15 @@
"integrity": "sha512-2uEQFb7bsx68rqD4F8q95wZq6LTLOyexjv6BnvJogCO4jStkyc6IDEkODPQcWfovI6g6M3uPQ2/uD/oedJKkNw==", "integrity": "sha512-2uEQFb7bsx68rqD4F8q95wZq6LTLOyexjv6BnvJogCO4jStkyc6IDEkODPQcWfovI6g6M3uPQ2/uD/oedJKkNw==",
"dev": true "dev": true
}, },
"@types/i18next-node-fs-backend": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/i18next-node-fs-backend/-/i18next-node-fs-backend-2.1.0.tgz",
"integrity": "sha512-bOOeT89UO/bYLJoQHdN5S3pggj7mMmFfQMBpDdUQOQIQkENGpnTwhNsIM/kjl1NE2HEihjlRZUNVV60Ze86UZA==",
"dev": true,
"requires": {
"i18next": ">=17.0.11"
}
},
"@types/jsonwebtoken": { "@types/jsonwebtoken": {
"version": "8.5.0", "version": "8.5.0",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
@ -202,6 +211,14 @@
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true "dev": true
}, },
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"array-flatten": { "array-flatten": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@ -468,6 +485,11 @@
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
}, },
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"etag": { "etag": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@ -663,6 +685,11 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
}, },
"helmet": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-4.4.1.tgz",
"integrity": "sha512-G8tp0wUMI7i8wkMk2xLcEvESg5PiCitFMYgGRc/PwULB0RVhTP5GFdxOwvJwp9XVha8CuS8mnhmE8I/8dx/pbw=="
},
"http-errors": { "http-errors": {
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@ -712,6 +739,15 @@
"resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.0.tgz", "resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.0.tgz",
"integrity": "sha512-65rP8bi5b7znBzfgIUy0KE00SWg1X6mL5XEkassgTrjAeLSfSb4vQ2bs9cN3qwHCynKIpmHjmNDu5c8NylTVmw==" "integrity": "sha512-65rP8bi5b7znBzfgIUy0KE00SWg1X6mL5XEkassgTrjAeLSfSb4vQ2bs9cN3qwHCynKIpmHjmNDu5c8NylTVmw=="
}, },
"i18next-node-fs-backend": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/i18next-node-fs-backend/-/i18next-node-fs-backend-2.1.3.tgz",
"integrity": "sha512-CreMFiVl3ChlMc5ys/e0QfuLFOZyFcL40Jj6jaKD6DxZ/GCUMxPI9BpU43QMWUgC7r+PClpxg2cGXAl0CjG04g==",
"requires": {
"js-yaml": "3.13.1",
"json5": "2.0.0"
}
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -775,6 +811,23 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
}, },
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json5": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.0.0.tgz",
"integrity": "sha512-0EdQvHuLm7yJ7lyG5dp7Q3X2ku++BG5ZHaJ5FTnaXpKqDrw4pMxel5Bt3oAYMthnrthFBdnZ1FcsXTPyrQlV0w==",
"requires": {
"minimist": "^1.2.0"
}
},
"jsonwebtoken": { "jsonwebtoken": {
"version": "8.5.1", "version": "8.5.1",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
@ -855,14 +908,15 @@
} }
}, },
"lambert-server": { "lambert-server": {
"version": "1.0.9", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.0.9.tgz", "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.0.10.tgz",
"integrity": "sha512-5Msp/wBxUUK/PeM7YwyZSPMS/MfbfNNiphUB4uEHiYUDMxE/b4jr3ek0SSRuWNBm3+3FwlUO6qkNYMixEoeqvA==", "integrity": "sha512-hhikhfjbU4w8D8J7Jif5DO3caqFCjtrJ4+coyOkQooz39cHM7ghHMIGSNvvQ7aee1pL71vBjuBRS/0HKO3wagA==",
"requires": { "requires": {
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"express": "^4.17.1", "express": "^4.17.1",
"express-async-errors": "^3.1.1", "express-async-errors": "^3.1.1",
"missing-native-js-functions": "^1.1.6" "helmet": "^4.4.1",
"missing-native-js-functions": "^1.1.8"
} }
}, },
"locate-path": { "locate-path": {
@ -1032,9 +1086,9 @@
} }
}, },
"missing-native-js-functions": { "missing-native-js-functions": {
"version": "1.1.9", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.1.9.tgz", "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.0.tgz",
"integrity": "sha512-lMhbzzN1pKHQ4HzqooBkO6Yf4/krT0YaYwJLuAaHExF1l49qVw8xmYSxOOgWCRk/i8veyHin0pCI9rwBXWh+qA==" "integrity": "sha512-PpmpGJA3n3t3qwetmCXUwmLPi9Hd2d7mHbHw6+BwsxXNOVyQTBv397x+FwbY3mJBVWPhSEGzR51egmx4ZZm2Xg=="
}, },
"mkdirp": { "mkdirp": {
"version": "1.0.4", "version": "1.0.4",
@ -1640,6 +1694,11 @@
"memory-pager": "^1.0.2" "memory-pager": "^1.0.2"
} }
}, },
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"statuses": { "statuses": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",

View File

@ -21,18 +21,20 @@
"@types/express": "^4.17.9", "@types/express": "^4.17.9",
"@types/node-fetch": "^2.5.7", "@types/node-fetch": "^2.5.7",
"bcrypt": "^5.0.0", "bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
"express": "^4.17.1", "express": "^4.17.1",
"express-cache-middleware": "^1.0.1", "express-cache-middleware": "^1.0.1",
"express-validator": "^6.9.2", "express-validator": "^6.9.2",
"faker": "^5.1.0", "faker": "^5.1.0",
"i18next": "^19.8.5", "i18next": "^19.8.5",
"i18next-http-middleware": "^3.1.0", "i18next-http-middleware": "^3.1.0",
"i18next-node-fs-backend": "^2.1.3",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"jwa": "^2.0.0", "jwa": "^2.0.0",
"jws": "^4.0.0", "jws": "^4.0.0",
"lambert-db": "^1.1.3", "lambert-db": "^1.1.3",
"lambert-server": "^1.0.9", "lambert-server": "^1.0.10",
"missing-native-js-functions": "^1.1.9", "missing-native-js-functions": "^1.2.0",
"mongoose": "^5.11.14", "mongoose": "^5.11.14",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"rethinkdb-ts": "^2.4.5" "rethinkdb-ts": "^2.4.5"
@ -40,6 +42,7 @@
"devDependencies": { "devDependencies": {
"@types/bcrypt": "^3.0.0", "@types/bcrypt": "^3.0.0",
"@types/faker": "^5.1.5", "@types/faker": "^5.1.5",
"@types/i18next-node-fs-backend": "^2.1.0",
"@types/jsonwebtoken": "^8.5.0", "@types/jsonwebtoken": "^8.5.0",
"@types/jws": "^3.2.3", "@types/jws": "^3.2.3",
"@types/node": "^14.14.22", "@types/node": "^14.14.22",

View File

@ -4,14 +4,17 @@ import { Authentication, GlobalRateLimit } from "./middlewares/";
import Config from "./util/Config"; import Config from "./util/Config";
import db from "./util/Database"; import db from "./util/Database";
import i18next from "i18next"; import i18next from "i18next";
import i18nextMiddleware from "i18next-http-middleware"; import i18nextMiddleware, { I18next } from "i18next-http-middleware";
import { Request } from "express"; import i18nextBackend from "i18next-node-fs-backend";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { BodyParser } from "./middlewares/BodyParser";
export interface DiscordServerOptions extends ServerOptions {} export interface DiscordServerOptions extends ServerOptions {}
declare global { declare global {
namespace Express { namespace Express {
interface Request { interface Request {
// @ts-ignore
server: DiscordServer; server: DiscordServer;
} }
} }
@ -21,7 +24,8 @@ export class DiscordServer extends Server {
public options: DiscordServerOptions; public options: DiscordServerOptions;
constructor(opts?: Partial<DiscordServerOptions>) { constructor(opts?: Partial<DiscordServerOptions>) {
super({ ...opts, errorHandler: false }); // @ts-ignore
super({ ...opts, errorHandler: false, jsonBody: false });
} }
async start() { async start() {
@ -31,20 +35,28 @@ export class DiscordServer extends Server {
this.app.use(GlobalRateLimit); this.app.use(GlobalRateLimit);
this.app.use(Authentication); this.app.use(Authentication);
const namespaces = await fs.readdir(__dirname + "/locales/de/"); this.app.use(BodyParser({ inflate: true }));
const languages = await fs.readdir(__dirname + "/../locales/");
const namespaces = await fs.readdir(__dirname + "/../locales/en/");
const ns = namespaces.filter((x) => x.endsWith(".json")).map((x) => x.slice(0, x.length - 5)); const ns = namespaces.filter((x) => x.endsWith(".json")).map((x) => x.slice(0, x.length - 5));
i18next.use(i18nextMiddleware.LanguageDetector).init({ await i18next
preload: ["en", "de"], .use(i18nextBackend)
.use(i18nextMiddleware.LanguageDetector)
.init({
preload: languages,
// debug: true,
fallbackLng: "en", fallbackLng: "en",
ns, ns,
backend: { backend: {
loadPath: "locales/{{lng}}/{{ns}}.json", loadPath: __dirname + "/../locales/{{lng}}/{{ns}}.json",
}, },
load: "all",
}); });
this.app.use(i18nextMiddleware.handle(i18next, {})); this.app.use(i18nextMiddleware.handle(i18next, {}));
this.routes = await this.registerRoutes(__dirname + "/routes/"); this.routes = await this.registerRoutes(__dirname + "/routes/");
this.app.use(ErrorHandler);
const indexHTML = await fs.readFile(__dirname + "/../client_test/index.html"); const indexHTML = await fs.readFile(__dirname + "/../client_test/index.html");
this.app.get("*", (req, res) => { this.app.get("*", (req, res) => {

View File

@ -6,3 +6,6 @@ import { DiscordServer } from "./Server";
const server = new DiscordServer({ port: 3000 }); const server = new DiscordServer({ port: 3000 });
server.start().catch(console.error); server.start().catch(console.error);
// @ts-ignore
global.server = server;

View File

@ -19,7 +19,7 @@ export function Authentication(req: Request, res: Response, next: NextFunction)
if (NO_AUTHORIZATION_ROUTES.includes(req.url)) return next(); if (NO_AUTHORIZATION_ROUTES.includes(req.url)) return next();
if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401)); if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401));
return jwt.verify(req.headers.authorization, Config.get().server.jwtSecret, JWTOptions, (err, decoded: any) => { return jwt.verify(req.headers.authorization, Config.get().security.jwtSecret, JWTOptions, (err, decoded: any) => {
if (err || !decoded) return next(new HTTPError("Invalid Token", 401)); if (err || !decoded) return next(new HTTPError("Invalid Token", 401));
req.token = decoded; req.token = decoded;

View File

@ -0,0 +1,17 @@
import bodyParser, { OptionsJson } from "body-parser";
import { NextFunction, Request, Response } from "express";
import { HTTPError } from "lambert-server";
export function BodyParser(opts?: OptionsJson) {
const jsonParser = bodyParser.json(opts);
return (req: Request, res: Response, next: NextFunction) => {
jsonParser(req, res, (err) => {
if (err) {
// TODO: different errors for body parser (request size limit, wrong body type, invalid body, ...)
return next(new HTTPError("Invalid Body", 400));
}
next();
});
};
}

View File

@ -3,16 +3,16 @@ import Config from "../util/Config";
import db from "../util/Database"; import db from "../util/Database";
export async function GlobalRateLimit(req: Request, res: Response, next: NextFunction) { export async function GlobalRateLimit(req: Request, res: Response, next: NextFunction) {
if (!Config.get().server.ipRateLimit.enabled) return next(); if (!Config.get().limits.rate.ip.enabled) return next();
const ip = getIpAdress(req); const ip = getIpAdress(req);
let limit = (await db.data.ratelimit.global[ip].get()) || { start: Date.now(), count: 0 }; let limit = (await db.data.ratelimit.global[ip].get()) || { start: Date.now(), count: 0 };
if (limit.start < Date.now() - Config.get().server.ipRateLimit.timespan) { if (limit.start < Date.now() - Config.get().limits.rate.ip.timespan) {
limit.start = Date.now(); limit.start = Date.now();
limit.count = 0; limit.count = 0;
} }
if (limit.count > Config.get().server.ipRateLimit.count) { if (limit.count > Config.get().limits.rate.ip.count) {
const timespan = Date.now() - limit.start; const timespan = Date.now() - limit.start;
return res return res
@ -37,7 +37,7 @@ export async function GlobalRateLimit(req: Request, res: Response, next: NextFun
} }
export function getIpAdress(req: Request): string { export function getIpAdress(req: Request): string {
const { forwadedFor } = Config.get().server; const { forwadedFor } = Config.get().security;
const ip = forwadedFor ? <string>req.headers[forwadedFor] : req.ip; const ip = forwadedFor ? <string>req.headers[forwadedFor] : req.ip;
return ip.replaceAll(".", "_").replaceAll(":", "_"); return ip.replaceAll(".", "_").replaceAll(":", "_");
} }

View File

@ -1,4 +1,6 @@
import mongoose from "mongoose"; import mongoose from "mongoose";
import { Long } from "mongodb";
import { Snowflake } from "../util/Snowflake";
async function main() { async function main() {
const conn = await mongoose.createConnection( const conn = await mongoose.createConnection(
@ -9,10 +11,7 @@ async function main() {
} }
); );
console.log("connected"); console.log("connected");
const result = await conn const result = await conn.collection("users").insertOne({ test: Long.fromString(Snowflake.generate().toString()) });
.collection("users")
.find({ $or: [{ email: "samuel.scheit@gmail.com" }, { phone: "samuel.scheit@gmail.com" }] })
.toArray();
// .project(undefined) // .project(undefined)
console.log(result); console.log(result);

View File

@ -1,6 +1,20 @@
export const WHITE_SPACE = /\s\s+/g; import { Request } from "express";
import { FieldError, FieldErrors } from "./instanceOf";
export const DOUBLE_WHITE_SPACE = /\s\s+/g;
export const SPECIAL_CHAR = /[@#`:\r\n\t\f\v\p{C}]/gu; export const SPECIAL_CHAR = /[@#`:\r\n\t\f\v\p{C}]/gu;
export function trim(str: string) { export function trimSpecial(str: string) {
return str.replace(SPECIAL_CHAR, "").replace(WHITE_SPACE, " ").trim(); return str.replace(SPECIAL_CHAR, "").replace(DOUBLE_WHITE_SPACE, " ").trim();
}
export function checkLength(str: string, min: number, max: number, key: string, req: Request) {
if (str.length < min || str.length > max) {
throw FieldErrors({
[key]: {
code: "BASE_TYPE_BAD_LENGTH",
message: req.t("common:field.BASE_TYPE_BAD_LENGTH", { length: `${min} - ${max}` }),
},
});
}
} }

View File

@ -95,7 +95,7 @@ export function instanceOf(
return true; return true;
throw new FieldError("BASE_TYPE_CHOICES", t("common:field.BASE_TYPE_CHOICES", { types: type.types })); throw new FieldError("BASE_TYPE_CHOICES", t("common:field.BASE_TYPE_CHOICES", { types: type.types }));
case Email: case Email:
if ((<Email>type).check()) return true; if (new Email(value).check()) return true;
throw new FieldError("EMAIL_TYPE_INVALID_EMAIL", t("common:field.EMAIL_TYPE_INVALID_EMAIL")); throw new FieldError("EMAIL_TYPE_INVALID_EMAIL", t("common:field.EMAIL_TYPE_INVALID_EMAIL"));
case Date: case Date:
value = new Date(value); value = new Date(value);
@ -143,13 +143,13 @@ export function instanceOf(
let newKey = key; let newKey = key;
const OPTIONAL = key.startsWith(OPTIONAL_PREFIX); const OPTIONAL = key.startsWith(OPTIONAL_PREFIX);
if (OPTIONAL) newKey = newKey.slice(OPTIONAL_PREFIX.length); if (OPTIONAL) newKey = newKey.slice(OPTIONAL_PREFIX.length);
errors[key] = {}; errors[newKey] = {};
return ( return (
instanceOf(type[key], value[newKey], { instanceOf(type[key], value[newKey], {
path: `${path}.${newKey}`, path: `${path}.${newKey}`,
optional: OPTIONAL, optional: OPTIONAL,
errors: errors[key], errors: errors[newKey],
t, t,
ref: { key: newKey, obj: value }, ref: { key: newKey, obj: value },
}) === true }) === true