mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-16 21:40:23 +01:00
Compare commits
87 Commits
socket-not
...
v2.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
624738359a | ||
|
|
15c3abb6d5 | ||
|
|
7ead74fe9b | ||
|
|
cbbdc79357 | ||
|
|
e0d5f92b2a | ||
|
|
8228eeb468 | ||
|
|
fc09f6c4f9 | ||
|
|
7a26672872 | ||
|
|
de820ae198 | ||
|
|
961a193787 | ||
|
|
4ab31e3f79 | ||
|
|
338f89deb5 | ||
|
|
dc14d6a8d1 | ||
|
|
fb43f9ae10 | ||
|
|
638e098f30 | ||
|
|
2514aace4e | ||
|
|
9ec7ab4afc | ||
|
|
dd5ed9e507 | ||
|
|
8bc8cf1ba0 | ||
|
|
62e162cf1e | ||
|
|
a5d92da9dd | ||
|
|
2bd6eea2fa | ||
|
|
42b9fbc91c | ||
|
|
5c0bf7ccbe | ||
|
|
2ec81eff43 | ||
|
|
df08b47163 | ||
|
|
5b7c3671c8 | ||
|
|
73ff25887c | ||
|
|
57f14e419f | ||
|
|
bb725987b3 | ||
|
|
b331b9423b | ||
|
|
e45a6de24b | ||
|
|
3f8248d673 | ||
|
|
f4282c091b | ||
|
|
af6ce44737 | ||
|
|
c6681a1725 | ||
|
|
bff5ce2d79 | ||
|
|
4821b21e81 | ||
|
|
a46b2bbc45 | ||
|
|
ce924eca0d | ||
|
|
c20b20a7aa | ||
|
|
82eb55d77d | ||
|
|
050e43f8b4 | ||
|
|
9b6dad367d | ||
|
|
727f879e5b | ||
|
|
fe662f3a46 | ||
|
|
8e77673d39 | ||
|
|
3f950d5162 | ||
|
|
96cc0617c5 | ||
|
|
ccf8739344 | ||
|
|
7e52a7a574 | ||
|
|
21d9806ca9 | ||
|
|
e7fcf482f3 | ||
|
|
d80c80b618 | ||
|
|
dec0e7deac | ||
|
|
c7ff98a12d | ||
|
|
5836bf4a05 | ||
|
|
a5357812c6 | ||
|
|
c7bd7dbfe6 | ||
|
|
ec4dadabd4 | ||
|
|
3509ed9461 | ||
|
|
cb8d94563a | ||
|
|
e83260ca28 | ||
|
|
4bf1ce42e6 | ||
|
|
7e922936d0 | ||
|
|
3c8ce70c74 | ||
|
|
babcd17e6c | ||
|
|
ec6ffaad4e | ||
|
|
ce3aa95053 | ||
|
|
7aab01d87a | ||
|
|
01d276cbee | ||
|
|
9758b7af2c | ||
|
|
dd3e1a2861 | ||
|
|
2a97342035 | ||
|
|
d5525c873b | ||
|
|
e7c3634f9a | ||
|
|
9c647c6ce2 | ||
|
|
52fc05edfe | ||
|
|
3aa7b8552a | ||
|
|
36523c67b8 | ||
|
|
60cbd1480d | ||
|
|
f3e59508ae | ||
|
|
4834cde335 | ||
|
|
01da76e1dc | ||
|
|
d2425942a6 | ||
|
|
8d7475be7b | ||
|
|
046ea12022 |
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "2.8.11",
|
||||
"version": "2.8.20",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -55,7 +55,7 @@
|
||||
"cookie-parser": "1.4.6",
|
||||
"cron": "2.3.0",
|
||||
"cropperjs": "1.5.13",
|
||||
"csurf": "1.11.0",
|
||||
"csrf-sync": "4.0.1",
|
||||
"daemon": "1.1.0",
|
||||
"diff": "5.1.0",
|
||||
"esbuild": "0.16.10",
|
||||
@@ -90,7 +90,7 @@
|
||||
"@nodebb/bootswatch": "3.4.2",
|
||||
"nconf": "0.12.0",
|
||||
"nodebb-plugin-2factor": "5.1.2",
|
||||
"nodebb-plugin-composer-default": "9.2.5",
|
||||
"nodebb-plugin-composer-default": "9.2.6",
|
||||
"nodebb-plugin-dbsearch": "5.1.5",
|
||||
"nodebb-plugin-emoji": "4.0.6",
|
||||
"nodebb-plugin-emoji-android": "3.0.0",
|
||||
@@ -99,7 +99,7 @@
|
||||
"nodebb-plugin-spam-be-gone": "1.0.2",
|
||||
"nodebb-rewards-essentials": "0.2.1",
|
||||
"nodebb-theme-lavender": "6.0.1",
|
||||
"nodebb-theme-persona": "12.1.12",
|
||||
"nodebb-theme-persona": "12.1.18",
|
||||
"nodebb-theme-slick": "2.0.2",
|
||||
"nodebb-theme-vanilla": "12.1.19",
|
||||
"nodebb-widget-essentials": "6.0.1",
|
||||
@@ -192,4 +192,4 @@
|
||||
"url": "https://github.com/barisusakli"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,6 +374,20 @@ get:
|
||||
type: string
|
||||
postIndex:
|
||||
type: number
|
||||
author:
|
||||
type: object
|
||||
required: [username, uid]
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
userslug:
|
||||
type: string
|
||||
uid:
|
||||
type: number
|
||||
fullname:
|
||||
type: string
|
||||
displayname:
|
||||
type: string
|
||||
loggedInUser:
|
||||
$ref: ../../components/schemas/UserObject.yaml#/UserObject
|
||||
- type: object
|
||||
|
||||
@@ -24,7 +24,7 @@ define('forum/topic/images', [], function () {
|
||||
|
||||
if (!$this.parent().is('a')) {
|
||||
$this.wrap('<a href="' + src + '" ' +
|
||||
(!srcExt && altExt ? ' download="' + altFilename + '" ' : '') +
|
||||
(!srcExt && altExt ? ' download="' + utils.escapeHTML(altFilename) + '" ' : '') +
|
||||
' target="_blank" rel="noopener">');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -15,6 +15,9 @@ app = window.app || {};
|
||||
reconnectionDelay: config.reconnectionDelay,
|
||||
transports: config.socketioTransports,
|
||||
path: config.relative_path + '/socket.io',
|
||||
query: {
|
||||
_csrf: config.csrf_token,
|
||||
},
|
||||
};
|
||||
|
||||
window.socket = io(config.websocketAddress, ioParams);
|
||||
|
||||
@@ -237,23 +237,26 @@ Analytics.getDailyStatsForSet = async function (set, day, numDays) {
|
||||
set = `analytics:${set}`;
|
||||
}
|
||||
|
||||
const daysArr = [];
|
||||
day = new Date(day);
|
||||
// set the date to tomorrow, because getHourlyStatsForSet steps *backwards* 24 hours to sum up the values
|
||||
day.setDate(day.getDate() + 1);
|
||||
day.setHours(0, 0, 0, 0);
|
||||
|
||||
while (numDays > 0) {
|
||||
/* eslint-disable no-await-in-loop */
|
||||
async function getHourlyStats(hour) {
|
||||
const dayData = await Analytics.getHourlyStatsForSet(
|
||||
set,
|
||||
day.getTime() - (1000 * 60 * 60 * 24 * (numDays - 1)),
|
||||
hour,
|
||||
24
|
||||
);
|
||||
daysArr.push(dayData.reduce((cur, next) => cur + next));
|
||||
return dayData.reduce((cur, next) => cur + next);
|
||||
}
|
||||
const hours = [];
|
||||
while (numDays > 0) {
|
||||
hours.push(day.getTime() - (1000 * 60 * 60 * 24 * (numDays - 1)));
|
||||
numDays -= 1;
|
||||
}
|
||||
return daysArr;
|
||||
|
||||
return await Promise.all(hours.map(getHourlyStats));
|
||||
};
|
||||
|
||||
Analytics.getUnwrittenPageviews = function () {
|
||||
|
||||
@@ -128,12 +128,13 @@ async function getStats() {
|
||||
}
|
||||
|
||||
let results = await Promise.all([
|
||||
getStatsForSet('ip:recent', 'uniqueIPCount'),
|
||||
getStatsFromAnalytics('uniquevisitors', 'uniqueIPCount'),
|
||||
getStatsFromAnalytics('logins', 'loginCount'),
|
||||
getStatsForSet('users:joindate', 'userCount'),
|
||||
getStatsForSet('posts:pid', 'postCount'),
|
||||
getStatsForSet('topics:tid', 'topicCount'),
|
||||
]);
|
||||
|
||||
results[0].name = '[[admin/dashboard:unique-visitors]]';
|
||||
|
||||
results[1].name = '[[admin/dashboard:logins]]';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const validator = require('validator');
|
||||
const db = require('../../database');
|
||||
const events = require('../../events');
|
||||
const pagination = require('../../pagination');
|
||||
@@ -39,6 +40,12 @@ eventsController.get = async function (req, res) {
|
||||
events: eventData,
|
||||
pagination: pagination.create(page, pageCount, req.query),
|
||||
types: types,
|
||||
query: req.query,
|
||||
query: {
|
||||
start: validator.escape(String(req.query.start)),
|
||||
end: validator.escape(String(req.query.end)),
|
||||
username: validator.escape(String(req.query.username)),
|
||||
group: validator.escape(String(req.query.group)),
|
||||
perPage: validator.escape(String(req.query.perPage)),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -119,11 +119,49 @@ uploadsController.uploadCategoryPicture = async function (req, res, next) {
|
||||
}
|
||||
|
||||
if (validateUpload(res, uploadedFile, allowedImageTypes)) {
|
||||
if (uploadedFile.path.endsWith('.svg')) {
|
||||
await sanitizeSvg(uploadedFile.path);
|
||||
}
|
||||
const filename = `category-${params.cid}${path.extname(uploadedFile.name)}`;
|
||||
await uploadImage(filename, 'category', uploadedFile, req, res, next);
|
||||
}
|
||||
};
|
||||
|
||||
async function sanitizeSvg(filePath) {
|
||||
const dirty = await fs.promises.readFile(filePath, 'utf8');
|
||||
const clean = sanitizeHtml(dirty, {
|
||||
allowedTags: [
|
||||
'svg', 'g', 'defs', 'linearGradient', 'radialGradient', 'stop',
|
||||
'circle', 'ellipse', 'polygon', 'polyline', 'path', 'rect',
|
||||
'line', 'text', 'tspan', 'use', 'symbol', 'clipPath', 'mask', 'pattern',
|
||||
'filter', 'feGaussianBlur', 'feOffset', 'feBlend', 'feColorMatrix', 'feMerge', 'feMergeNode',
|
||||
],
|
||||
allowedAttributes: {
|
||||
'*': [
|
||||
// Geometry
|
||||
'x', 'y', 'x1', 'x2', 'y1', 'y2', 'cx', 'cy', 'r', 'rx', 'ry',
|
||||
'width', 'height', 'd', 'points', 'viewBox', 'transform',
|
||||
|
||||
// Presentation
|
||||
'fill', 'stroke', 'stroke-width', 'opacity',
|
||||
'stop-color', 'stop-opacity', 'offset', 'style', 'class',
|
||||
|
||||
// Text
|
||||
'text-anchor', 'font-size', 'font-family',
|
||||
|
||||
// Misc
|
||||
'id', 'clip-path', 'mask', 'filter', 'gradientUnits', 'gradientTransform',
|
||||
'xmlns', 'preserveAspectRatio',
|
||||
],
|
||||
},
|
||||
parser: {
|
||||
lowerCaseTags: false,
|
||||
lowerCaseAttributeNames: false,
|
||||
},
|
||||
});
|
||||
await fs.promises.writeFile(filePath, clean);
|
||||
}
|
||||
|
||||
uploadsController.uploadFavicon = async function (req, res, next) {
|
||||
const uploadedFile = req.files.files[0];
|
||||
const allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon'];
|
||||
@@ -184,10 +222,6 @@ uploadsController.uploadMaskableIcon = async function (req, res, next) {
|
||||
}
|
||||
};
|
||||
|
||||
uploadsController.uploadLogo = async function (req, res, next) {
|
||||
await upload('site-logo', req, res, next);
|
||||
};
|
||||
|
||||
uploadsController.uploadFile = async function (req, res, next) {
|
||||
const uploadedFile = req.files.files[0];
|
||||
let params;
|
||||
@@ -208,6 +242,10 @@ uploadsController.uploadFile = async function (req, res, next) {
|
||||
}
|
||||
};
|
||||
|
||||
uploadsController.uploadLogo = async function (req, res, next) {
|
||||
await upload('site-logo', req, res, next);
|
||||
};
|
||||
|
||||
uploadsController.uploadDefaultAvatar = async function (req, res, next) {
|
||||
await upload('avatar-default', req, res, next);
|
||||
};
|
||||
@@ -220,6 +258,9 @@ async function upload(name, req, res, next) {
|
||||
const uploadedFile = req.files.files[0];
|
||||
|
||||
if (validateUpload(res, uploadedFile, allowedImageTypes)) {
|
||||
if (uploadedFile.path.endsWith('.svg')) {
|
||||
await sanitizeSvg(uploadedFile.path);
|
||||
}
|
||||
const filename = name + path.extname(uploadedFile.name);
|
||||
await uploadImage(filename, 'system', uploadedFile, req, res, next);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ const categories = require('../categories');
|
||||
const plugins = require('../plugins');
|
||||
const translator = require('../translator');
|
||||
const languages = require('../languages');
|
||||
const { generateToken } = require('../middleware/csrf');
|
||||
|
||||
const apiController = module.exports;
|
||||
|
||||
@@ -64,7 +65,7 @@ apiController.loadConfig = async function (req) {
|
||||
'cache-buster': meta.config['cache-buster'] || '',
|
||||
topicPostSort: meta.config.topicPostSort || 'oldest_to_newest',
|
||||
categoryTopicSort: meta.config.categoryTopicSort || 'newest_to_oldest',
|
||||
csrf_token: req.uid >= 0 && req.csrfToken && req.csrfToken(),
|
||||
csrf_token: req.uid >= 0 ? generateToken(req) : undefined,
|
||||
searchEnabled: plugins.hooks.hasListeners('filter:search.query'),
|
||||
searchDefaultInQuick: meta.config.searchDefaultInQuick || 'titles',
|
||||
bootswatchSkin: meta.config.bootswatchSkin || '',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const validator = require('validator');
|
||||
|
||||
const user = require('../user');
|
||||
const groups = require('../groups');
|
||||
@@ -41,9 +42,9 @@ modsController.flags.list = async function (req, res) {
|
||||
filters = filters.reduce((memo, cur) => {
|
||||
if (req.query.hasOwnProperty(cur)) {
|
||||
if (typeof req.query[cur] === 'string' && req.query[cur].trim() !== '') {
|
||||
memo[cur] = req.query[cur].trim();
|
||||
memo[cur] = validator.escape(String(req.query[cur].trim()));
|
||||
} else if (Array.isArray(req.query[cur]) && req.query[cur].length) {
|
||||
memo[cur] = req.query[cur];
|
||||
memo[cur] = req.query[cur].map(item => validator.escape(String(item).trim()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,8 +105,8 @@ topicsController.get = async function getTopic(req, res, next) {
|
||||
|
||||
topicData.postIndex = postIndex;
|
||||
|
||||
await Promise.all([
|
||||
buildBreadcrumbs(topicData),
|
||||
const [author] = await Promise.all([
|
||||
user.getUserFields(topicData.uid, ['username', 'userslug']),
|
||||
addOldCategory(topicData, userPrivileges),
|
||||
addTags(topicData, req, res),
|
||||
incrementViewCount(req, tid),
|
||||
@@ -114,6 +114,7 @@ topicsController.get = async function getTopic(req, res, next) {
|
||||
analytics.increment([`pageviews:byCid:${topicData.category.cid}`]),
|
||||
]);
|
||||
|
||||
topicData.author = author;
|
||||
topicData.pagination = pagination.create(currentPage, pageCount, req.query);
|
||||
topicData.pagination.rel.forEach((rel) => {
|
||||
rel.href = `${url}/topic/${topicData.slug}${rel.href}`;
|
||||
|
||||
@@ -629,9 +629,9 @@ SELECT z."value",
|
||||
ON o."_key" = z."_key"
|
||||
AND o."type" = z."type"
|
||||
WHERE o."_key" = $1::TEXT
|
||||
AND z."value" LIKE '${match}'
|
||||
AND z."value" LIKE $3
|
||||
LIMIT $2::INTEGER`,
|
||||
values: [params.key, params.limit],
|
||||
values: [params.key, params.limit, match],
|
||||
});
|
||||
if (!params.withScores) {
|
||||
return res.rows.map(r => r.value);
|
||||
|
||||
26
src/middleware/csrf.js
Normal file
26
src/middleware/csrf.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const { csrfSync } = require('csrf-sync');
|
||||
|
||||
const {
|
||||
generateToken,
|
||||
csrfSynchronisedProtection,
|
||||
isRequestValid,
|
||||
} = csrfSync({
|
||||
getTokenFromRequest: (req) => {
|
||||
if (req.headers['x-csrf-token']) {
|
||||
return req.headers['x-csrf-token'];
|
||||
} else if (req.body && req.body.csrf_token) {
|
||||
return req.body.csrf_token;
|
||||
} else if (req.query) {
|
||||
return req.query._csrf;
|
||||
}
|
||||
},
|
||||
size: 64,
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
generateToken,
|
||||
csrfSynchronisedProtection,
|
||||
isRequestValid,
|
||||
};
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
const async = require('async');
|
||||
const path = require('path');
|
||||
const csrf = require('csurf');
|
||||
const validator = require('validator');
|
||||
const nconf = require('nconf');
|
||||
const toobusy = require('toobusy-js');
|
||||
const util = require('util');
|
||||
const { csrfSynchronisedProtection } = require('./csrf');
|
||||
|
||||
const plugins = require('../plugins');
|
||||
const meta = require('../meta');
|
||||
@@ -34,7 +34,7 @@ middleware.regexes = {
|
||||
timestampedUpload: /^\d+-.+$/,
|
||||
};
|
||||
|
||||
const csrfMiddleware = csrf();
|
||||
const csrfMiddleware = csrfSynchronisedProtection;
|
||||
|
||||
middleware.applyCSRF = function (req, res, next) {
|
||||
if (req.uid >= 0) {
|
||||
|
||||
@@ -10,6 +10,7 @@ const meta = require('../meta');
|
||||
const controllers = require('../controllers');
|
||||
const helpers = require('../controllers/helpers');
|
||||
const plugins = require('../plugins');
|
||||
const { generateToken } = require('../middleware/csrf');
|
||||
|
||||
let loginStrategies = [];
|
||||
|
||||
@@ -108,7 +109,7 @@ Auth.reloadRoutes = async function (params) {
|
||||
};
|
||||
|
||||
if (strategy.checkState !== false) {
|
||||
req.session.ssoState = req.csrfToken && req.csrfToken();
|
||||
req.session.ssoState = generateToken(req, true);
|
||||
opts.state = req.session.ssoState;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,25 @@ Sockets.init = async function (server) {
|
||||
}
|
||||
}
|
||||
|
||||
io.use(authorize);
|
||||
|
||||
io.on('connection', onConnection);
|
||||
|
||||
const opts = {
|
||||
transports: nconf.get('socket.io:transports') || ['polling', 'websocket'],
|
||||
cookie: false,
|
||||
allowRequest: (req, callback) => {
|
||||
authorize(req, (err) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
const csrf = require('../middleware/csrf');
|
||||
const isValid = csrf.isRequestValid({
|
||||
session: req.session || {},
|
||||
query: req._query,
|
||||
headers: req.headers,
|
||||
});
|
||||
callback(null, isValid);
|
||||
});
|
||||
},
|
||||
};
|
||||
/*
|
||||
* Restrict socket.io listener to cookie domain. If none is set, infer based on url.
|
||||
@@ -62,7 +74,11 @@ Sockets.init = async function (server) {
|
||||
};
|
||||
|
||||
function onConnection(socket) {
|
||||
socket.ip = (socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress || '').split(',')[0];
|
||||
socket.uid = socket.request.uid;
|
||||
socket.ip = (
|
||||
socket.request.headers['x-forwarded-for'] ||
|
||||
socket.request.connection.remoteAddress || ''
|
||||
).split(',')[0];
|
||||
socket.request.ip = socket.ip;
|
||||
logger.io_one(socket, socket.uid);
|
||||
|
||||
@@ -231,9 +247,7 @@ async function validateSession(socket, errorMsg) {
|
||||
|
||||
const cookieParserAsync = util.promisify((req, callback) => cookieParser(req, {}, err => callback(err)));
|
||||
|
||||
async function authorize(socket, callback) {
|
||||
const { request } = socket;
|
||||
|
||||
async function authorize(request, callback) {
|
||||
if (!request) {
|
||||
return callback(new Error('[[error:not-authorized]]'));
|
||||
}
|
||||
@@ -246,15 +260,13 @@ async function authorize(socket, callback) {
|
||||
});
|
||||
|
||||
const sessionData = await getSessionAsync(sessionId);
|
||||
|
||||
request.session = sessionData;
|
||||
let uid = 0;
|
||||
if (sessionData && sessionData.passport && sessionData.passport.user) {
|
||||
request.session = sessionData;
|
||||
socket.uid = parseInt(sessionData.passport.user, 10);
|
||||
} else {
|
||||
socket.uid = 0;
|
||||
uid = parseInt(sessionData.passport.user, 10);
|
||||
}
|
||||
request.uid = socket.uid;
|
||||
callback();
|
||||
request.uid = uid;
|
||||
callback(null, uid);
|
||||
}
|
||||
|
||||
Sockets.in = function (room) {
|
||||
|
||||
@@ -40,6 +40,10 @@ Interstitials.email = async (data) => {
|
||||
issuePasswordChallenge: !!data.userData.uid && hasPassword,
|
||||
},
|
||||
callback: async (userData, formData) => {
|
||||
if (formData.email) {
|
||||
formData.email = String(formData.email).trim();
|
||||
}
|
||||
|
||||
// Validate and send email confirmation
|
||||
if (userData.uid) {
|
||||
const isSelf = parseInt(userData.uid, 10) === parseInt(data.req.uid, 10);
|
||||
|
||||
@@ -1 +1 @@
|
||||
data-index="{posts.index}" data-pid="{posts.pid}" data-uid="{posts.uid}" data-timestamp="{posts.timestamp}" data-username="{posts.user.username}" data-userslug="{posts.user.userslug}" itemscope itemtype="http://schema.org/Comment"
|
||||
data-index="{posts.index}" data-pid="{posts.pid}" data-uid="{posts.uid}" data-timestamp="{posts.timestamp}" data-username="{posts.user.username}" data-userslug="{posts.user.userslug}" itemprop="comment" itemtype="http://schema.org/Comment" itemscope
|
||||
@@ -89,6 +89,21 @@ describe('Sorted Set methods', () => {
|
||||
assert(data.includes('ddb'));
|
||||
assert(data.includes('adb'));
|
||||
});
|
||||
|
||||
it('should not error with invalid input', async () => {
|
||||
const query = `-3217'
|
||||
OR 1251=CAST((CHR(113)||CHR(98)||CHR(118)||CHR(98)||CHR(113))||(SELECT
|
||||
(CASE WHEN (1251=1251) THEN 1 ELSE 0
|
||||
END))::text||(CHR(113)||CHR(113)||CHR(118)||CHR(98)||CHR(113)) AS
|
||||
NUMERIC)-- WsPn&query[cid]=-1&parentCid=0&selectedCids[]=-1&privilege=topics:read&states[]=watching&states[]=tracking&states[]=notwatching&showLinks=`;
|
||||
const match = `*${query.toLowerCase()}*`;
|
||||
const data = await db.getSortedSetScan({
|
||||
key: 'categories:name',
|
||||
match: match,
|
||||
limit: 500,
|
||||
});
|
||||
assert.strictEqual(data.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortedSetAdd()', () => {
|
||||
|
||||
@@ -869,6 +869,11 @@ describe('Flags', () => {
|
||||
assert.strictEqual(flagData.reports[0].value, '"<script>alert('ok');</script>');
|
||||
});
|
||||
|
||||
it('should escape filters', async () => {
|
||||
const { body } = await request.get(`${nconf.get('url')}/api/flags?quick="<script>alert('foo');</script>`, { jar });
|
||||
assert.strictEqual(body.filters.quick, '"<script>alert('foo');</script>');
|
||||
});
|
||||
|
||||
it('should not allow flagging post in private category', async () => {
|
||||
const category = await Categories.create({ name: 'private category' });
|
||||
|
||||
@@ -1149,5 +1154,7 @@ describe('Flags', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,7 +95,7 @@ helpers.logoutUser = function (jar, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
helpers.connectSocketIO = function (res, callback) {
|
||||
helpers.connectSocketIO = function (res, csrf_token, callback) {
|
||||
const io = require('socket.io-client');
|
||||
let cookies = res.headers['set-cookie'];
|
||||
cookies = cookies.filter(c => /express.sid=[^;]+;/.test(c));
|
||||
@@ -106,6 +106,9 @@ helpers.connectSocketIO = function (res, callback) {
|
||||
Origin: nconf.get('url'),
|
||||
Cookie: cookie,
|
||||
},
|
||||
query: {
|
||||
_csrf: csrf_token,
|
||||
},
|
||||
});
|
||||
|
||||
socket.on('connect', () => {
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('socket.io', () => {
|
||||
}, (err, res) => {
|
||||
assert.ifError(err);
|
||||
|
||||
helpers.connectSocketIO(res, (err, _io) => {
|
||||
helpers.connectSocketIO(res, body.csrf_token, (err, _io) => {
|
||||
io = _io;
|
||||
assert.ifError(err);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user