From 22e4033b82333a55a4e6759ac59efc59b3cc541b Mon Sep 17 00:00:00 2001 From: fatchan Date: Sun, 4 Aug 2019 13:35:45 +0000 Subject: [PATCH] add more options for filters, filter mode and auto ban configurable duration --- controllers/forms.js | 22 ++++++++++++---- gulp/res/css/style.css | 3 +++ gulpfile.js | 1 + helpers/paramconverter.js | 2 +- models/forms/changeboardsettings.js | 4 ++- models/forms/create.js | 2 +- models/forms/makepost.js | 39 ++++++++++++++++++++++------- views/pages/manage.pug | 9 +++++++ views/pages/register.pug | 2 +- 9 files changed, 66 insertions(+), 18 deletions(-) diff --git a/controllers/forms.js b/controllers/forms.js index d9307b17..ea878946 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -9,6 +9,7 @@ const express = require('express') , { remove } = require('fs-extra') , upload = require('express-fileupload') , path = require('path') + , alphaNumericRegex = /^[a-zA-Z0-9]+$/ , postFiles = upload({ createParentPath: true, safeFileNames: /[^\w-]+/g, @@ -170,12 +171,12 @@ router.post('/create', csrf, isLoggedIn, verifyCaptcha, (req, res, next) => { errors.push('Missing description'); } - //check exist + //other validation if (req.body.uri) { if (req.body.uri.length > 50) { errors.push('URI must be 50 characters or less'); } - if (!req.body.uri.match(/^[a-zA-Z0-9]+$/)) { + if (alphaNumericRegex.test(req.body.uri) !== true) { errors.push('URI must contain a-z 0-9 only'); } } @@ -214,9 +215,14 @@ router.post('/register', verifyCaptcha, (req, res, next) => { errors.push('Missing password confirmation'); } - //check too long - if (req.body.username && req.body.username.length > 50) { - errors.push('Username must be 50 characters or less'); + //check + if (req.body.username) { + if (req.body.username.length > 50) { + errors.push('Username must be 50 characters or less'); + } + if (alphaNumericRegex.test(req.body.username) !== true) { + errors.push('Username must contain a-z 0-9 only'); + } } if (req.body.password && req.body.password.length > 100) { errors.push('Password must be 100 characters or less'); @@ -361,6 +367,12 @@ router.post('/board/:board/settings', csrf, Boards.exists, isLoggedIn, checkPerm if (typeof req.body.captcha_trigger_mode === 'number' && (req.body.captcha_trigger_mode < 0 || req.body.captcha_trigger_mode > 2)) { errors.push('Invalid captcha trigger mode.') } + if (typeof req.body.filter_mode === 'number' && (req.body.filter_mode < 0 || req.body.filter_mode > 2)) { + errors.push('Invalid filter mode.'); + } + if (typeof req.body.ban_duration === 'number' && req.body.ban_duration <= 0) { + errors.push('Invalid filter auto ban duration.') + } if (errors.length > 0) { return res.status(400).render('message', { diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index 1fa0f53e..2e4337d0 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -108,6 +108,9 @@ pre { .mv-10 { margin: 10px 0; } +.mv-0 { + margin: 0 auto; +} .mb-10 { margin-bottom: 10px; } diff --git a/gulpfile.js b/gulpfile.js index f45f6091..d5ca73ba 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -73,6 +73,7 @@ async function wipe() { 'markdown':null }, 'filters':[] + 'filterMode': 0, } }) //add indexes - should profiled and changed at some point if necessary diff --git a/helpers/paramconverter.js b/helpers/paramconverter.js index 11d0db10..7a383b98 100644 --- a/helpers/paramconverter.js +++ b/helpers/paramconverter.js @@ -3,7 +3,7 @@ const Mongo = require(__dirname+'/../db/db.js') , allowedArrays = new Set(['checkedposts', 'globalcheckedposts', 'checkedbans', 'checkedbanners']) //only these can be arrays, since express bodyparser will output arrays , trimFields = ['uri', 'filters', 'announcement', 'description', 'message', 'name', 'subject', 'email', 'password', 'default_name', 'report_reason', 'ban_reason'] //trim if we dont want filed with whitespace - , numberFields = ['captcha_mode', 'captcha_trigger', 'captcha_trigger_mode', 'reply_limit', 'max_files', 'thread_limit', 'thread', 'min_message_length'] //convert these to numbers before they hit our routes + , numberFields = ['filter_mode', 'captcha_mode', 'captcha_trigger', 'captcha_trigger_mode', 'reply_limit', 'max_files', 'thread_limit', 'thread', 'min_message_length'] //convert these to numbers before they hit our routes , banDurationRegex = /^(?[\d]+y)?(?[\d]+m)?(?[\d]+w)?(?[\d]+d)?(?[\d]+h)?$/ , msTime = require(__dirname+'/mstime.js') diff --git a/models/forms/changeboardsettings.js b/models/forms/changeboardsettings.js index 51382fa6..92963b25 100644 --- a/models/forms/changeboardsettings.js +++ b/models/forms/changeboardsettings.js @@ -52,7 +52,9 @@ module.exports = async (req, res, next) => { raw: req.body.announcement !== null ? req.body.announcement : oldSettings.announcement.raw, markdown: markdownAnnouncement || oldSettings.announcement.markdown }, - filters: req.body.filters !== null ? req.body.filters.split('\n').filter(n => n) /*prevents empty*/ : oldSettings.filters + filters: req.body.filters !== null ? req.body.filters.split('\n').filter(n => n) /*prevents empty*/ : oldSettings.filters, + filterMode: typeof req.body.filter_mode === 'number' && req.body.filter_mode !== oldSettings.filterMode ? req.body.filter_mode : oldSettings.filterMode, + filterBanDuration: typeof req.body.ban_duration === 'number' && req.body.ban_duration !== oldSettings.filterBanDuration ? req.body.ban_duration : oldSettings.filterBanDuration }; //settings changed in the db diff --git a/models/forms/create.js b/models/forms/create.js index 35c321d6..b49604cf 100644 --- a/models/forms/create.js +++ b/models/forms/create.js @@ -43,7 +43,7 @@ module.exports = async (req, res, next) => { 'userPostUnlink': true, 'threadLimit': 200, 'replyLimit': 500, - 'maxFiles': 0, + 'maxFiles': 3, 'forceOPSubject': false, 'forceOPMessage': true, 'forceOPFile': false, diff --git a/models/forms/makepost.js b/models/forms/makepost.js index d2d14099..cb978688 100644 --- a/models/forms/makepost.js +++ b/models/forms/makepost.js @@ -8,6 +8,7 @@ const path = require('path') , Posts = require(__dirname+'/../../db/posts.js') , Boards = require(__dirname+'/../../db/boards.js') , Files = require(__dirname+'/../../db/files.js') + , Bans = require(__dirname+'/../../db/bans.js') , getTripCode = require(__dirname+'/../../helpers/posting/tripcode.js') , linkQuotes = require(__dirname+'/../../helpers/posting/quotes.js') , simpleMarkdown = require(__dirname+'/../../helpers/posting/markdown.js') @@ -42,7 +43,7 @@ module.exports = async (req, res, next) => { let salt = null; let thread = null; const permLevel = permsCheck(req, res); - const { filters, maxFiles, forceAnon, replyLimit, threadLimit, ids, userPostSpoiler, defaultName, captchaTrigger, captchaTriggerMode, captchaMode } = res.locals.board.settings; + const { filters, filterBanDuration, filterMode, maxFiles, forceAnon, replyLimit, threadLimit, ids, userPostSpoiler, defaultName, captchaTrigger, captchaTriggerMode, captchaMode } = res.locals.board.settings; if (req.body.thread) { thread = await Posts.getPost(req.params.board, req.body.thread, true); if (!thread || thread.thread != null) { @@ -81,15 +82,35 @@ module.exports = async (req, res, next) => { }); } //filters - if (filters && filters.length > 0) { - const containsFilter = filters.some(filter => { return req.body.message.includes(filter) }); - if (containsFilter) { + if (permLevel >= 4 && filterMode > 0 && filters && filters.length > 0) { + const allContents = req.body.name+req.body.message+req.body.subject+req.body.email; + const containsFilter = filters.some(filter => { return allContents.includes(filter) }); + if (containsFilter === true) { await deleteTempFiles(req).catch(e => console.error); - return res.status(400).render('message', { //is this a 400? - 'title': 'Bad request', - 'message': `Your message was blocked by a filter.`, - 'redirect': redirect - }); + if (filterMode === 1) { + return res.status(400).render('message', { + 'title': 'Bad request', + 'message': 'Your post was blocked by a word filter', + 'redirect': redirect + }); + } else if (filterMode === 2) { + const banDate = new Date(); + const banExpiry = new Date(filterBanDuration + banDate.getTime()); + const ban = { + 'ip': res.locals.ip, + 'reason': 'post word filter auto ban', + 'board': res.locals.board._id, + 'post': null, + 'issuer': 'system', //what should i call this + 'date': banDate, + 'expireAt': banExpiry + }; + await Bans.insertOne(ban); + const bans = await Bans.find(res.locals.ip, res.locals.board._id); + return res.status(403).render('ban', { + bans: bans + }); + } } } let files = []; diff --git a/views/pages/manage.pug b/views/pages/manage.pug index adda7e88..48dce712 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -86,6 +86,15 @@ block content section.row .label Filters textarea(name='filters' placeholder='newline separated') #{board.settings.filters.join('\n')} + section.row + .label Filter Mode + select(name='filter_mode' checked=board.settings.filterMode) + option(value='0', selected=board.settings.filterMode === 0) Do nothing + option(value='1', selected=board.settings.filterMode === 1) Block post + option(value='2', selected=board.settings.filterMode === 2) Auto ban + section.row + .label Filter Auto Ban Duration + input(type='text' name='ban_duration' placeholder='e.g. 1w' value=board.settings.filterBanDuration) input(type='submit', value='save settings') hr(size=1) h4.no-m-p Add Banners: diff --git a/views/pages/register.pug b/views/pages/register.pug index a6f6b77c..9b40def9 100644 --- a/views/pages/register.pug +++ b/views/pages/register.pug @@ -9,7 +9,7 @@ block content form.form-post(action='/forms/register' method='POST') section.row .label Username - input(type='text', name='username', maxlength='50' required) + input(type='text', name='username', maxlength='50' pattern='[a-zA-Z0-9]+' required title='alphanumeric only') section.row .label Password input(type='password', name='password', maxlength='100' required)