Skip to content

Commit

Permalink
fix: validate router id parameters to be numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
drodil committed Feb 5, 2024
1 parent 7ac0029 commit f897010
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 53 deletions.
80 changes: 66 additions & 14 deletions plugins/qeta-backend/src/service/routes/answers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,17 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
return;
}

const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const created = await getCreated(request, options);
const questionId = Number.parseInt(request.params.id, 10);
// Act
const answer = await database.answerQuestion(
username,
Expand Down Expand Up @@ -84,11 +91,18 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
const globalEdit =
options.config.getOptionalBoolean('qeta.allowGlobalEdits') ?? false;

const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
if (Number.isNaN(questionId) || Number.isNaN(answerId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

// Act
const answer = await database.updateAnswer(
username,
Number.parseInt(request.params.id, 10),
Number.parseInt(request.params.answerId, 10),
questionId,
answerId,
request.body.answer,
request.body.images,
moderator || globalEdit,
Expand Down Expand Up @@ -119,10 +133,16 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
return;
}

const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
if (Number.isNaN(questionId) || Number.isNaN(answerId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const created = await getCreated(request, options);
const answerId = Number.parseInt(request.params.answerId, 10);
// Act
const answer = await database.commentAnswer(
answerId,
Expand All @@ -139,7 +159,6 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
mapAdditionalFields(username, answer, options, moderator);

if (eventBroker) {
const questionId = Number.parseInt(request.params.id, 10);
const question = await database.getQuestion(
username,
questionId,
Expand Down Expand Up @@ -170,11 +189,22 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
// Validation
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
const commentId = Number.parseInt(request.params.commentId, 10);
if (
Number.isNaN(questionId) ||
Number.isNaN(answerId) ||
Number.isNaN(commentId)
) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

// Act
const answer = await database.deleteAnswerComment(
Number.parseInt(request.params.answerId, 10),
Number.parseInt(request.params.commentId, 10),
answerId,
commentId,
username,
moderator,
);
Expand All @@ -199,10 +229,14 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
await checkPermissions(request, qetaReadPermission, options);
const answer = await database.getAnswer(
Number.parseInt(request.params.answerId, 10),
username,
);
const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
if (Number.isNaN(questionId) || Number.isNaN(answerId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

const answer = await database.getAnswer(answerId, username);

if (answer === null) {
response.sendStatus(404);
Expand All @@ -222,10 +256,14 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
// Validation
const moderator = await isModerator(request, options);
const username = await getUsername(request, options);
const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
if (Number.isNaN(questionId) || Number.isNaN(answerId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

if (eventBroker) {
const questionId = Number.parseInt(request.params.id, 10);
const question = await database.getQuestion(
username,
questionId,
Expand Down Expand Up @@ -261,11 +299,16 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
score: number,
) => {
// Validation
const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
if (Number.isNaN(questionId) || Number.isNaN(answerId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

// Act
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const answerId = Number.parseInt(request.params.answerId, 10);
const voted = await database.voteAnswer(username, answerId, score);

if (!voted) {
Expand All @@ -281,7 +324,6 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
}

if (eventBroker) {
const questionId = Number.parseInt(request.params.id, 10);
const question = await database.getQuestion(username, questionId, false);
await eventBroker.publish({
topic: 'qeta',
Expand Down Expand Up @@ -324,6 +366,11 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
options.config.getOptionalBoolean('qeta.allowGlobalEdits') ?? false;
const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
if (Number.isNaN(questionId) || Number.isNaN(answerId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

const marked = await database.markAnswerCorrect(
username,
questionId,
Expand Down Expand Up @@ -362,6 +409,11 @@ export const answersRoutes = (router: Router, options: RouterOptions) => {
options.config.getOptionalBoolean('qeta.allowGlobalEdits') ?? false;
const questionId = Number.parseInt(request.params.id, 10);
const answerId = Number.parseInt(request.params.answerId, 10);
if (Number.isNaN(questionId) || Number.isNaN(answerId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

const marked = await database.markAnswerIncorrect(
username,
questionId,
Expand Down
34 changes: 17 additions & 17 deletions plugins/qeta-backend/src/service/routes/attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,26 +90,26 @@ export const attachmentsRoutes = (router: Router, options: RouterOptions) => {
const { uuid } = request.params;

const attachment = await database.getAttachment(uuid);
if (!attachment) {
return response.status(404).send('Attachment not found');
}

if (attachment) {
let imageBuffer: Buffer;
if (attachment.locationType === 'database') {
imageBuffer = attachment.binaryImage;
} else {
imageBuffer = await fs.promises.readFile(attachment.path);
}
let imageBuffer: Buffer;
if (attachment.locationType === 'database') {
imageBuffer = attachment.binaryImage;
} else {
imageBuffer = await fs.promises.readFile(attachment.path);
}

if (!imageBuffer) {
response.status(500).send('Attachment buffer is undefined');
}
if (!imageBuffer) {
response.status(500).send('Attachment buffer is undefined');
}

response.writeHead(200, {
'Content-Type': attachment.mimeType,
'Content-Length': imageBuffer ? imageBuffer.byteLength : '',
});
response.writeHead(200, {
'Content-Type': attachment.mimeType,
'Content-Length': imageBuffer ? imageBuffer.byteLength : '',
});

return response.end(imageBuffer);
}
return response.status(404).send('Attachment not found');
return response.end(imageBuffer);
});
};
90 changes: 68 additions & 22 deletions plugins/qeta-backend/src/service/routes/questions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
await checkPermissions(request, qetaReadPermission, options);
const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

const question = await database.getQuestion(
username,
Number.parseInt(request.params.id, 10),
Expand Down Expand Up @@ -117,14 +125,22 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
const created = await getCreated(request, options);
await checkPermissions(request, qetaReadPermission, options);
const validateRequestBody = ajv.compile(CommentSchema);
const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

if (!validateRequestBody(request.body)) {
response
.status(400)
.send({ errors: validateRequestBody.errors, type: 'body' });
return;
}
const question = await database.commentQuestion(
Number.parseInt(request.params.id, 10),
questionId,
username,
request.body.content,
created,
Expand Down Expand Up @@ -165,9 +181,16 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
await checkPermissions(request, qetaReadPermission, options);
const questionId = Number.parseInt(request.params.id, 10);
const commentId = Number.parseInt(request.params.commentId, 10);
if (Number.isNaN(questionId) || Number.isNaN(commentId)) {
response.status(400).send({ errors: 'Invalid id', type: 'body' });
return;
}

const question = await database.deleteQuestionComment(
Number.parseInt(request.params.id, 10),
Number.parseInt(request.params.commentId, 10),
questionId,
commentId,
username,
moderator,
);
Expand Down Expand Up @@ -268,16 +291,24 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
.json({ errors: validateRequestBody.errors, type: 'body' });
return;
}
const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

const tags = getTags(request);
const entities = getEntities(request);
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const globalEdit =
options.config.getOptionalBoolean('qeta.allowGlobalEdits') ?? false;

// Act
const question = await database.updateQuestion(
Number.parseInt(request.params.id, 10),
questionId,
username,
request.body.title,
request.body.content,
Expand Down Expand Up @@ -314,6 +345,12 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
const moderator = await isModerator(request, options);
const username = await getUsername(request, options);
const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

if (eventBroker) {
const question = database.getQuestion(username, questionId, false);
Expand Down Expand Up @@ -344,26 +381,25 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
score: number,
) => {
// Validation
const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

// Act
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const voted = await database.voteQuestion(
username,
Number.parseInt(request.params.id, 10),
score,
);
const voted = await database.voteQuestion(username, questionId, score);

if (!voted) {
response.sendStatus(404);
return;
}

const question = await database.getQuestion(
username,
Number.parseInt(request.params.id, 10),
false,
);
const question = await database.getQuestion(username, questionId, false);

mapAdditionalFields(username, question, options, moderator);
if (question) {
Expand Down Expand Up @@ -400,10 +436,15 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
router.get(`/questions/:id/favorite`, async (request, response) => {
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const favorited = await database.favoriteQuestion(
username,
Number.parseInt(request.params.id, 10),
);
const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

const favorited = await database.favoriteQuestion(username, questionId);

if (!favorited) {
response.sendStatus(404);
Expand All @@ -426,10 +467,15 @@ export const questionsRoutes = (router: Router, options: RouterOptions) => {
router.get(`/questions/:id/unfavorite`, async (request, response) => {
const username = await getUsername(request, options);
const moderator = await isModerator(request, options);
const unfavorited = await database.unfavoriteQuestion(
username,
Number.parseInt(request.params.id, 10),
);
const questionId = Number.parseInt(request.params.id, 10);
if (Number.isNaN(questionId)) {
response
.status(400)
.send({ errors: 'Invalid question id', type: 'body' });
return;
}

const unfavorited = await database.unfavoriteQuestion(username, questionId);

if (!unfavorited) {
response.sendStatus(404);
Expand Down

0 comments on commit f897010

Please sign in to comment.