Skip to content

Commit

Permalink
Merge pull request filipedeschamps#1133 from filipedeschamps/validator
Browse files Browse the repository at this point in the history
fix(validator): invalidate blank characters and empty markdown
  • Loading branch information
aprendendofelipe authored Dec 15, 2022
2 parents 8571878 + 2932e65 commit 0bbb0f6
Show file tree
Hide file tree
Showing 13 changed files with 446 additions and 124 deletions.
2 changes: 1 addition & 1 deletion models/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ function getSlug(title) {
trim: true,
});

const truncatedSlug = generatedSlug.substring(0, 256);
const truncatedSlug = generatedSlug.substring(0, 255);

return truncatedSlug;
}
Expand Down
19 changes: 19 additions & 0 deletions models/remove-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

export default function removeMarkdown(md, options) {
options = options || {};
options.oneLine = options.hasOwnProperty('oneLine') ? options.oneLine : true;
options.listUnicodeChar = options.hasOwnProperty('listUnicodeChar') ? options.listUnicodeChar : false;
options.stripListLeaders = options.hasOwnProperty('stripListLeaders') ? options.stripListLeaders : true;
options.gfm = options.hasOwnProperty('gfm') ? options.gfm : true;
Expand Down Expand Up @@ -85,6 +86,24 @@ export default function removeMarkdown(md, options) {
// .replace(/(\S+)\n\s*(\S+)/g, '$1 $2')
// Replace strike through
.replace(/~(.*?)~/g, '$1');

if (options.oneLine) {
output = output.replace(/\s+/g, ' ');
}

if (output.length > options.maxLength) {
output = output
.substring(0, options.maxLength - 3)
.trim()
.concat('...');
}

if (options.trim) {
output = output.replace(
/^(\s|\p{C}|\u2800|\u034f|\u115f|\u1160|\u17b4|\u17b5|\u3164|\uffa0)+|(\s|\p{C}|\u2800|\u034f|\u115f|\u1160|\u17b4|\u17b5|\u3164|\uffa0)+$|\u0000/gsu,
''
);
}
} catch (e) {
console.error(e);
return md;
Expand Down
2 changes: 1 addition & 1 deletion models/rss.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function generateRss2(contentList) {
title: contentObject.title,
id: contentUrl,
link: contentUrl,
description: removeMarkdown(contentObject.body).replace(/\s+/g, ' ').substring(0, 190) + '...',
description: removeMarkdown(contentObject.body, { maxLength: 190 }),
content: renderToStaticMarkup(<Viewer value={contentObject.body} />).replace(/[\r\n]/gm, ''),
author: [
{
Expand Down
2 changes: 1 addition & 1 deletion models/thumbnail.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function parseContent(content, parentContent) {
let title = content.title;

if (!title) {
title = removeMarkdown(content.body).substring(0, 120).replace(/\s+/g, ' ');
title = removeMarkdown(content.body, { maxLength: 120 });
}

// Regex to wrap text: https://stackoverflow.com/a/51506718
Expand Down
28 changes: 19 additions & 9 deletions models/validator.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Joi from 'joi';
import { ValidationError } from 'errors/index.js';
import removeMarkdown from 'models/remove-markdown';

export default function validator(object, keys) {
// Force the clean up of "undefined" values since JSON
Expand Down Expand Up @@ -228,17 +229,17 @@ const schemas = {
return Joi.object({
slug: Joi.string()
.min(1)
.max(256)
.max(255, 'utf8')
.trim()
.truncate()
.invalid(null)
.pattern(/^[a-z0-9](-?[a-z0-9])*$/m)
.pattern(/^[a-z0-9](-?[a-z0-9])*$/)
.when('$required.slug', { is: 'required', then: Joi.required(), otherwise: Joi.optional() })
.messages({
'any.required': `"slug" é um campo obrigatório.`,
'string.empty': `"slug" não pode estar em branco.`,
'string.base': `"slug" deve ser do tipo String.`,
'string.min': `"slug" deve conter no mínimo {#limit} caracteres.`,
'string.max': `"slug" deve conter no máximo {#limit} caracteres.`,
'string.min': `"slug" deve conter no mínimo {#limit} caractere.`,
'string.pattern.base': `"slug" está no formato errado.`,
'any.invalid': `"slug" possui o valor inválido "null".`,
}),
Expand All @@ -248,11 +249,13 @@ const schemas = {
title: function () {
return Joi.object({
title: Joi.string()
.replace(/^\u200e|\u200e$|^\u200f|\u200f$|\u0000/g, '')
.replace(
/^(\s|\p{C}|\u2800|\u034f|\u115f|\u1160|\u17b4|\u17b5|\u3164|\uffa0)+|(\s|\p{C}|\u2800|\u034f|\u115f|\u1160|\u17b4|\u17b5|\u3164|\uffa0)+$|\u0000/gu,
''
)
.allow(null)
.min(1)
.max(256)
.trim()
.max(255)
.when('$required.title', { is: 'required', then: Joi.required(), otherwise: Joi.optional() })
.messages({
'any.required': `"title" é um campo obrigatório.`,
Expand All @@ -267,11 +270,12 @@ const schemas = {
body: function () {
return Joi.object({
body: Joi.string()
.replace(/^\u200e|\u200e$|^\u200f|\u200f$|\u0000/g, '')
.pattern(/^(\s|\p{C}|\u2800|\u034f|\u115f|\u1160|\u17b4|\u17b5|\u3164|\uffa0).*$/su, { invert: true })
.replace(/(\s|\p{C}|\u2800|\u034f|\u115f|\u1160|\u17b4|\u17b5|\u3164|\uffa0)+$|\u0000/gsu, '')
.min(1)
.max(20000)
.trim()
.invalid(null)
.custom(withoutMarkdown, 'check if is empty without markdown')
.when('$required.body', { is: 'required', then: Joi.required(), otherwise: Joi.optional() })
.messages({
'any.required': `"body" é um campo obrigatório.`,
Expand All @@ -280,6 +284,8 @@ const schemas = {
'string.min': `"body" deve conter no mínimo {#limit} caracteres.`,
'string.max': `"body" deve conter no máximo {#limit} caracteres.`,
'any.invalid': `"body" possui o valor inválido "null".`,
'string.pattern.invert.base': `"body" deve começar com caracteres visíveis.`,
'markdown.empty': `Markdown deve conter algum texto`,
}),
});
},
Expand Down Expand Up @@ -786,3 +792,7 @@ const schemas = {
});
},
};

const withoutMarkdown = (value, helpers) => {
return removeMarkdown(value, { trim: true }).length > 0 ? value : helpers.error('markdown.empty');
};
6 changes: 3 additions & 3 deletions pages/[username]/[slug]/index.public.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,15 +342,15 @@ export async function getStaticProps(context) {

const secureChildrenList = authorization.filterOutput(userTryingToGet, 'read:content:list', childrenFound);

const oneLineBody = removeMarkdown(secureContentFound.body).replace(/\s+/g, ' ');
const oneLineBody = removeMarkdown(secureContentFound.body, { maxLength: 190 });

const webserverHost = webserver.getHost();

const contentMetadata = {
title: `${secureContentFound.title ?? oneLineBody.substring(0, 80)} · ${secureContentFound.owner_username}`,
image: `${webserverHost}/api/v1/contents/${secureContentFound.owner_username}/${secureContentFound.slug}/thumbnail`,
url: `${webserverHost}/${secureContentFound.owner_username}/${secureContentFound.slug}`,
description: oneLineBody.substring(0, 190),
description: oneLineBody,
published_time: secureContentFound.published_at,
modified_time: secureContentFound.updated_at,
author: secureContentFound.owner_username,
Expand All @@ -375,7 +375,7 @@ export async function getStaticProps(context) {
},
});

parentContentFound.body = removeMarkdown(parentContentFound.body).replace(/\s+/g, ' ').substring(0, 50);
parentContentFound.body = removeMarkdown(parentContentFound.body, { maxLength: 50 });
secureParentContentFound = authorization.filterOutput(userTryingToGet, 'read:content', parentContentFound);
}

Expand Down
11 changes: 1 addition & 10 deletions pages/[username]/index.public.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ export async function getStaticProps(context) {

for (const content of secureContentListFound) {
if (content.parent_id) {
content.body = shortenAndCleanBody(content.body);
content.body = removeMarkdown(content.body, { maxLength: 255 });
} else {
delete content.body;
}
Expand All @@ -235,12 +235,3 @@ export async function getStaticProps(context) {
revalidate: 10,
};
}

function shortenAndCleanBody(body) {
const titleLength = 256;
const bodyLength = titleLength - '...'.length;
const cleanBody = removeMarkdown(body).replace(/\s+/g, ' ');

const shortenedBody = cleanBody.substring(0, bodyLength).trim();
return cleanBody.length < bodyLength ? shortenedBody : shortenedBody + '...';
}
11 changes: 1 addition & 10 deletions pages/[username]/pagina/[page]/index.public.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export async function getStaticProps(context) {

for (const content of secureContentListFound) {
if (content.parent_id) {
content.body = shortenAndCleanBody(content.body);
content.body = removeMarkdown(content.body, { maxLength: 255 });
} else {
delete content.body;
}
Expand All @@ -90,12 +90,3 @@ export async function getStaticProps(context) {
revalidate: 10,
};
}

function shortenAndCleanBody(body) {
const titleLength = 256;
const bodyLength = titleLength - '...'.length;
const cleanBody = removeMarkdown(body).replace(/\s+/g, ' ');

const shortenedBody = cleanBody.substring(0, bodyLength).trim();
return cleanBody.length < bodyLength ? shortenedBody : shortenedBody + '...';
}
11 changes: 1 addition & 10 deletions pages/api/v1/contents/[username]/index.public.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async function getHandler(request, response) {

for (const content of secureOutputValues) {
if (content.parent_id) {
content.body = shortenAndCleanBody(content.body);
content.body = removeMarkdown(content.body, { maxLength: 255 });
} else {
delete content.body;
}
Expand All @@ -56,12 +56,3 @@ async function getHandler(request, response) {
controller.injectPaginationHeaders(results.pagination, `/api/v1/contents/${request.query.username}`, response);
return response.status(200).json(secureOutputValues);
}

function shortenAndCleanBody(body) {
const titleLength = 256;
const bodyLength = titleLength - '...'.length;
const cleanBody = removeMarkdown(body).replace(/\s+/g, ' ');

const shortenedBody = cleanBody.substring(0, bodyLength).trim();
return cleanBody.length < bodyLength ? shortenedBody : shortenedBody + '...';
}
Loading

0 comments on commit 0bbb0f6

Please sign in to comment.