From 2f50d80a7525ecde94d5ece8724bb31d1ab5bceb Mon Sep 17 00:00:00 2001 From: Michael C Date: Sat, 26 Jun 2021 23:02:52 -0400 Subject: [PATCH] add explit param --- src/routes/getUserID.ts | 70 +++++++++++++++++----------- test/cases/getUserID.ts | 101 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 26 deletions(-) diff --git a/src/routes/getUserID.ts b/src/routes/getUserID.ts index b450e47..f4c9986 100644 --- a/src/routes/getUserID.ts +++ b/src/routes/getUserID.ts @@ -1,37 +1,55 @@ import {db} from '../databases/databases'; -import {Logger} from '../utils/logger'; import {Request, Response} from 'express'; +import {UserID} from '../types/user.model'; -export async function getUserID(req: Request, res: Response) { - let userName = req.query.username as string; - - if (userName == undefined || userName.length > 64 || userName.length < 3) { - //invalid request - res.sendStatus(400); - return; - } - +function getFuzzyUserID(userName: String): Promise<[{userName: String, userID: UserID }]> { // escape [_ % \] to avoid ReDOS userName = userName.replace(/\\/g, '\\\\') .replace(/_/g, '\\_') .replace(/%/g, '\\%'); - - // add wildcard to variable - userName = `%${userName}%`; - // LIMIT to reduce overhead - // ESCAPE to escape LIKE wildcards + userName = `%${userName}%`; // add wildcard to username + // LIMIT to reduce overhead | ESCAPE to escape LIKE wildcards try { - let rows = await db.prepare('all', `SELECT "userName", "userID" FROM "userNames" - WHERE "userName" LIKE ? ESCAPE '\\' LIMIT 10`, [userName]); - if (rows.length === 0) { - res.sendStatus(404); - return; - } else { - res.send(rows); - } + return db.prepare('all', `SELECT "userName", "userID" FROM "userNames" WHERE "userName" + LIKE ? ESCAPE '\\' LIMIT 10`, [userName]) } catch (err) { - Logger.error(err); - res.sendStatus(500); - return; + return null; + } +} + +function getExactUserID(userName: String): Promise<[{userName: String, userID: UserID }]> { + try { + return db.prepare('all', `SELECT "userName", "userID" from "userNames" WHERE "userName" = ? LIMIT 10`, [userName]); + } catch (err) { + return null; + } +} + +export async function getUserID(req: Request, res: Response) { + let userName = req.query.username as string; + const exactSearch = req.query.exact + ? req.query.exact == "true" + : false as Boolean; + + // if not exact and length is 1, also skip + if (userName == undefined || userName.length > 64 || + (!exactSearch && userName.length < 3)) { + // invalid request + res.sendStatus(400); + return false; + } + const results = exactSearch + ? await getExactUserID(userName) + : await getFuzzyUserID(userName); + + if (results === undefined || results === null) { + res.sendStatus(500); + return false; + } else if (results.length as number === 0) { + res.sendStatus(404); + return false; + } else { + res.send(results); + return false; } } diff --git a/test/cases/getUserID.ts b/test/cases/getUserID.ts index 21c27f8..7fe5d70 100644 --- a/test/cases/getUserID.ts +++ b/test/cases/getUserID.ts @@ -17,6 +17,7 @@ describe('getUserID', () => { await db.prepare("run", insertUserNameQuery, [getHash("getuserid_user_09"), '_redos_']); await db.prepare("run", insertUserNameQuery, [getHash("getuserid_user_10"), 'redos\\%']); await db.prepare("run", insertUserNameQuery, [getHash("getuserid_user_11"), '\\\\\\']); + await db.prepare("run", insertUserNameQuery, [getHash("getuserid_user_12"), 'a']); }); it('Should be able to get a 200', (done: Done) => { @@ -201,6 +202,31 @@ describe('getUserID', () => { .catch(err => ("couldn't call endpoint")); }); + it('Should be able to get repeating fuzzy username', (done: Done) => { + fetch(getbaseURL() + '/api/userID?username=peat') + .then(async res => { + if (res.status !== 200) { + done("non 200"); + } else { + const data = await res.json(); + if (data.length !== 2) { + done('Returned incorrect number of users "' + data.length + '"'); + } else if (data[0].userName !== "repeating") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[0].userID !== getHash("getuserid_user_04")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else if (data[1].userName !== "repeating") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[1].userID !== getHash("getuserid_user_05")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else { + done(); // pass + } + } + }) + .catch(err => ("couldn't call endpoint")); + }); + it('should avoid ReDOS with _', (done: Done) => { fetch(getbaseURL() + '/api/userID?username=_redos_') .then(async res => { @@ -299,4 +325,79 @@ describe('getUserID', () => { }) .catch(err => ("couldn't call endpoint")); }); + + it('should allow exact match', (done: Done) => { + fetch(getbaseURL() + '/api/userID?username=a&exact=true') + .then(async res => { + if (res.status !== 200) { + done("non 200"); + } else { + const data = await res.json(); + if (data.length !== 1) { + done('Returned incorrect number of users "' + data.length + '"'); + } else if (data[0].userName !== "a") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[0].userID !== getHash("getuserid_user_12")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else { + done(); // pass + } + } + }) + .catch(err => ("couldn't call endpoint")); + }); + + it('Should be able to get repeating username with exact username', (done: Done) => { + fetch(getbaseURL() + '/api/userID?username=repeating&exact=true') + .then(async res => { + if (res.status !== 200) { + done("non 200"); + } else { + const data = await res.json(); + if (data.length !== 2) { + done('Returned incorrect number of users "' + data.length + '"'); + } else if (data[0].userName !== "repeating") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[0].userID !== getHash("getuserid_user_04")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else if (data[1].userName !== "repeating") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[1].userID !== getHash("getuserid_user_05")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else { + done(); // pass + } + } + }) + .catch(err => ("couldn't call endpoint")); + }); + + it('Should not get exact unless explicitly set to true', (done: Done) => { + fetch(getbaseURL() + '/api/userID?username=user&exact=1') + .then(async res => { + if (res.status !== 200) { + done("non 200"); + } else { + const data = await res.json(); + if (data.length !== 3) { + done('Returned incorrect number of users "' + data.length + '"'); + } else if (data[0].userName !== "fuzzy user 01") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[0].userID !== getHash("getuserid_user_01")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else if (data[1].userName !== "fuzzy user 02") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[1].userID !== getHash("getuserid_user_02")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else if (data[2].userName !== "specific user 03") { + done('Returned incorrect username "' + data.userName + '"'); + } else if (data[2].userID !== getHash("getuserid_user_03")) { + done('Returned incorrect userID "' + data.userID + '"'); + } else { + done(); // pass + } + } + }) + .catch(err => ("couldn't call endpoint")); + }); });