Merge branch 'ip-cloaking' into develop

indiachan-spamvector
Thomas Lynch 2 years ago
commit 6d01d90753
  1. 3
      CHANGELOG.md
  2. 2
      controllers/forms/boardsettings.js
  3. 2
      controllers/forms/editpost.js
  4. 12
      db/bans.js
  5. 66
      db/posts.js
  6. 2
      gulpfile.js
  7. 2
      helpers/captcha/verify.js
  8. 4
      helpers/checks/spamcheck.js
  9. 5
      helpers/decodequeryip.js
  10. 17
      helpers/processip.js
  11. 77
      migrations/0.4.0.js
  12. 6
      models/forms/actionhandler.js
  13. 2
      models/forms/appeal.js
  14. 20
      models/forms/banposter.js
  15. 4
      models/forms/editpost.js
  16. 12
      models/forms/makepost.js
  17. 2
      models/forms/reportpost.js
  18. 2
      models/pages/captcha.js
  19. 11
      models/pages/globalmanage/logs.js
  20. 2
      models/pages/manage/recent.js
  21. 594
      package-lock.json
  22. 4
      package.json
  23. 8
      schedules/tasks/ips.js
  24. 7
      views/mixins/ban.pug
  25. 4
      views/mixins/post.pug
  26. 2
      views/mixins/report.pug
  27. 2
      views/pages/globalmanagelogs.pug
  28. 2
      views/pages/managelogs.pug
  29. 2
      views/pages/managerecent.pug

@ -1,3 +1,6 @@
### 0.4.0
- Hashed IPs now use an irc-style "cloaking".
### 0.3.3 ### 0.3.3
- Minor bugfix to filenames of expanded images being incorrectly truncated in some circumstances, when "image loading bars" is enabled. - Minor bugfix to filenames of expanded images being incorrectly truncated in some circumstances, when "image loading bars" is enabled.

@ -85,7 +85,7 @@ module.exports = {
if (res.locals.permLevel > 1) { //if not global staff or above if (res.locals.permLevel > 1) { //if not global staff or above
const ratelimitBoard = await Ratelimits.incrmentQuota(req.params.board, 'settings', rateLimitCost.boardSettings); //2 changes a minute const ratelimitBoard = await Ratelimits.incrmentQuota(req.params.board, 'settings', rateLimitCost.boardSettings); //2 changes a minute
const ratelimitIp = res.locals.anonymizer ? 0 : (await Ratelimits.incrmentQuota(res.locals.ip.single, 'settings', rateLimitCost.boardSettings)); const ratelimitIp = res.locals.anonymizer ? 0 : (await Ratelimits.incrmentQuota(res.locals.ip.cloak, 'settings', rateLimitCost.boardSettings));
if (ratelimitBoard > 100 || ratelimitIp > 100) { if (ratelimitBoard > 100 || ratelimitIp > 100) {
return dynamicResponse(req, res, 429, 'message', { return dynamicResponse(req, res, 429, 'message', {
'title': 'Ratelimited', 'title': 'Ratelimited',

@ -43,7 +43,7 @@ module.exports = {
if (res.locals.permLevel > 1) { //if not global staff or above if (res.locals.permLevel > 1) { //if not global staff or above
const ratelimitUser = await Ratelimits.incrmentQuota(req.session.user, 'edit', rateLimitCost.editPost); const ratelimitUser = await Ratelimits.incrmentQuota(req.session.user, 'edit', rateLimitCost.editPost);
const ratelimitIp = res.locals.anonymizer ? 0 : (await Ratelimits.incrmentQuota(res.locals.ip.single, 'edit', rateLimitCost.editPost)); const ratelimitIp = res.locals.anonymizer ? 0 : (await Ratelimits.incrmentQuota(res.locals.ip.cloak, 'edit', rateLimitCost.editPost));
if (ratelimitUser > 100 || ratelimitIp > 100) { if (ratelimitUser > 100 || ratelimitIp > 100) {
return dynamicResponse(req, res, 429, 'message', { return dynamicResponse(req, res, 429, 'message', {
'title': 'Ratelimited', 'title': 'Ratelimited',

@ -10,15 +10,19 @@ module.exports = {
find: (ip, board) => { find: (ip, board) => {
let ipQuery; let ipQuery;
if (typeof ip === 'object') { //object with hash and ranges in bancheck if (typeof ip === 'object') {
ipQuery = { ipQuery = {
'$in': [ip.single, ip.qrange, ip.hrange] //gets single and range ban in 1 query '$in': [
ip.cloak, //full ip
ip.cloak.split('.').slice(0,2).join('.'), //qrange
ip.cloak.split('.').slice(0,1).join('.'), //hrange
],
} }
} else { } else {
ipQuery = ip; ipQuery = ip;
} }
return db.find({ return db.find({
'ip.single': ipQuery, 'ip.cloak': ipQuery,
'board': { 'board': {
'$in': [board, null] '$in': [board, null]
} }
@ -42,7 +46,7 @@ module.exports = {
'_id': { '_id': {
'$in': ids '$in': ids
}, },
'ip.single': ip, 'ip.cloak': ip,
'allowAppeal': true, 'allowAppeal': true,
'appeal': null 'appeal': null
}, { }, {

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const Mongo = require(__dirname+'/db.js') const Mongo = require(__dirname+'/db.js')
, { isIP } = require('net')
, Boards = require(__dirname+'/boards.js') , Boards = require(__dirname+'/boards.js')
, Stats = require(__dirname+'/stats.js') , Stats = require(__dirname+'/stats.js')
, db = Mongo.db.collection('posts') , db = Mongo.db.collection('posts')
@ -35,10 +36,12 @@ module.exports = {
} else { } else {
projection['globalreports'] = 0; projection['globalreports'] = 0;
} }
if (ip instanceof RegExp) { if (ip != null) {
query['ip.single'] = ip; if (isIP(ip)) {
} else if (typeof ip === 'string') { query['ip.raw'] = ip;
query['ip.raw'] = ip; } else {
query['ip.cloak'] = ip;
}
} }
if (permLevel > config.get.ipHashPermLevel) { if (permLevel > config.get.ipHashPermLevel) {
projection['ip.raw'] = 0; projection['ip.raw'] = 0;
@ -54,21 +57,6 @@ module.exports = {
}).sort({ }).sort({
'_id': -1 '_id': -1
}).skip(offset).limit(limit).toArray(); }).skip(offset).limit(limit).toArray();
posts.forEach(p => {
//kill me
p.ip.single = p.ip.single.slice(-10);
p.ip.qrange = p.ip.qrange.slice(-10);
p.ip.hrange = p.ip.hrange.slice(-10);
if (board) {
p.reports.forEach(r => {
r.ip.single = r.ip.single.slice(-10);
});
} else {
p.globalreports.forEach(r => {
r.ip.single = r.ip.single.slice(-10);
});
}
});
return posts; return posts;
}, },
@ -479,7 +467,7 @@ module.exports = {
//insert the post itself //insert the post itself
const postMongoId = await db.insertOne(data).then(result => result.insertedId); //_id of post const postMongoId = await db.insertOne(data).then(result => result.insertedId); //_id of post
const statsIp = (config.get.statsCountAnonymizers === false && res.locals.anonymizer === true) ? null : data.ip.single; const statsIp = (config.get.statsCountAnonymizers === false && res.locals.anonymizer === true) ? null : data.ip.cloak;
await Stats.updateOne(board._id, statsIp, data.thread == null); await Stats.updateOne(board._id, statsIp, data.thread == null);
//add backlinks to the posts this post quotes //add backlinks to the posts this post quotes
@ -545,14 +533,6 @@ module.exports = {
}, },
'board': board 'board': board
}, { projection }).toArray(); }, { projection }).toArray();
posts.forEach(p => {
p.ip.single = p.ip.single.slice(-10);
p.ip.qrange = p.ip.qrange.slice(-10);
p.ip.hrange = p.ip.hrange.slice(-10);
p.reports.forEach(r => {
r.ip.single = r.ip.single.slice(-10);
});
});
return posts; return posts;
}, },
@ -571,26 +551,20 @@ module.exports = {
'$exists': true '$exists': true
} }
} }
if (ip instanceof RegExp) { if (ip != null) {
query['$or'] = [ if (isIP(ip)) {
{ 'ip.single': ip }, query['$or'] = [
{ 'globalreports.ip.single': ip } { 'ip.raw': ip },
]; { 'globalreports.ip.raw': ip }
} else if (typeof ip === 'string') { ];
query['$or'] = [ } else {
{ 'ip.raw': ip }, query['$or'] = [
{ 'globalreports.ip.raw': ip } { 'ip.cloak': ip },
]; { 'globalreports.ip.cloak': ip }
];
}
} }
const posts = await db.find(query, { projection }).skip(offset).limit(limit).toArray(); const posts = await db.find(query, { projection }).skip(offset).limit(limit).toArray();
posts.forEach(p => {
p.ip.single = p.ip.single.slice(-10);
p.ip.qrange = p.ip.qrange.slice(-10);
p.ip.hrange = p.ip.hrange.slice(-10);
p.globalreports.forEach(r => {
r.ip.single = r.ip.single.slice(-10);
});
});
return posts; return posts;
}, },

@ -192,7 +192,7 @@ async function wipe() {
await CustomPages.db.createIndex({ 'board': 1, 'page': 1 }, { unique: true }) await CustomPages.db.createIndex({ 'board': 1, 'page': 1 }, { unique: true })
await Modlogs.db.createIndex({ 'board': 1 }) await Modlogs.db.createIndex({ 'board': 1 })
await Files.db.createIndex({ 'count': 1 }) await Files.db.createIndex({ 'count': 1 })
await Bans.db.createIndex({ 'ip.single': 1 , 'board': 1 }) await Bans.db.createIndex({ 'ip.cloak': 1 , 'board': 1 })
await Bans.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 }) //custom expiry, i.e. it will expire when current date > than this date await Bans.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 }) //custom expiry, i.e. it will expire when current date > than this date
await Bypass.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 }) await Bypass.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 })
await Captchas.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 300 }) //captchas valid for 5 minutes await Captchas.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 300 }) //captchas valid for 5 minutes

@ -50,7 +50,7 @@ module.exports = async (req, res, next) => {
//for builtin captchas, clear captchaid cookie, delete file and reset quota //for builtin captchas, clear captchaid cookie, delete file and reset quota
res.clearCookie('captchaid'); res.clearCookie('captchaid');
await Promise.all([ await Promise.all([
!res.locals.anonymizer && Ratelimits.resetQuota(res.locals.ip.single, 'captcha'), !res.locals.anonymizer && Ratelimits.resetQuota(res.locals.ip.cloak, 'captcha'),
remove(`${uploadDirectory}/captcha/${captchaId}.jpg`) remove(`${uploadDirectory}/captcha/${captchaId}.jpg`)
]); ]);
} }

@ -57,7 +57,7 @@ module.exports = async (req, res) => {
'_id': { '_id': {
'$gt': sameContentSameIpMongoId '$gt': sameContentSameIpMongoId
}, },
'ip.single': res.locals.ip.single, 'ip.cloak': res.locals.ip.cloak,
'$or': contentOr '$or': contentOr
}); });
} }
@ -69,7 +69,7 @@ module.exports = async (req, res) => {
'_id': { '_id': {
'$gt': anyContentSameIpMongoId '$gt': anyContentSameIpMongoId
}, },
'ip.single': res.locals.ip.single 'ip.cloak': res.locals.ip.cloak
}) })
} }

@ -8,10 +8,9 @@ module.exports = (query, permLevel) => {
const { ipHashPermLevel } = config.get; const { ipHashPermLevel } = config.get;
if (query.ip && typeof query.ip === 'string') { if (query.ip && typeof query.ip === 'string') {
const decoded = decodeURIComponent(query.ip); const decoded = decodeURIComponent(query.ip);
if (permLevel <= ipHashPermLevel && (isIP(decoded) || decoded.match(/[a-z0-9]{24}/i))) { //if perms to view raw ip or bypass, allow querying if (permLevel <= ipHashPermLevel || !isIP(decoded)) {
//if they have perm to view raw IP, or its NOT a raw ip, return
return decoded; return decoded;
} else if (decoded.length === 10) { //otherwise, only allow last 10 char substring
return new RegExp(`${escapeRegExp(decoded)}$`);
} }
} }
return null; //else, no ip filter return null; //else, no ip filter

@ -12,10 +12,8 @@ module.exports = (req, res, next) => {
if (res.locals.anonymizer) { if (res.locals.anonymizer) {
const pseudoIp = res.locals.preFetchedBypassId || req.signedCookies.bypassid; const pseudoIp = res.locals.preFetchedBypassId || req.signedCookies.bypassid;
res.locals.ip = { res.locals.ip = {
raw: pseudoIp, raw: `${pseudoIp}.BP`,
single: pseudoIp, cloak: `${pseudoIp}.BP`,
qrange: pseudoIp,
hrange: pseudoIp,
}; };
return next(); return next();
} }
@ -31,8 +29,8 @@ module.exports = (req, res, next) => {
zeroElide: false, zeroElide: false,
zeroPad: false, zeroPad: false,
}); });
let qrange = '' let qrange
, hrange = ''; , hrange;
if (ipKind === 'ipv4') { if (ipKind === 'ipv4') {
qrange = createCIDR(ipStr, 24).toString(); qrange = createCIDR(ipStr, 24).toString();
hrange = createCIDR(ipStr, 16).toString(); hrange = createCIDR(ipStr, 16).toString();
@ -40,11 +38,10 @@ module.exports = (req, res, next) => {
qrange = createCIDR(ipStr, 64).toString(); qrange = createCIDR(ipStr, 64).toString();
hrange = createCIDR(ipStr, 48).toString(); hrange = createCIDR(ipStr, 48).toString();
} }
const cloak = `${hashIp(hrange).substring(0,8)}.${hashIp(qrange).substring(0,7)}.${hashIp(ipStr).substring(0,7)}.IP`;
res.locals.ip = { res.locals.ip = {
raw: ipHashPermLevel === -1 ? hashIp(ipStr) : ipStr, raw: ipHashPermLevel === -1 ? cloak : ipStr,
single: hashIp(ipStr), cloak,
qrange: hashIp(qrange),
hrange: hashIp(hrange),
} }
next(); next();
} catch(e) { } catch(e) {

@ -0,0 +1,77 @@
'use strict';
const hashIp = require(__dirname+'/../helpers/haship.js')
, { createCIDR, parse } = require('ip6addr')
, config = require(__dirname+'/../config.js');
module.exports = async(db, redis) => {
const postIps = await db.collection('posts').distinct('ip.raw');
const logIps = await db.collection('modlog').distinct('ip.raw');
const banIps = await db.collection('bans').distinct('ip.raw');
const allDistinctIps = postIps.concat(logIps).concat(banIps);
const bulkWrites = allDistinctIps.map(ip => {
const ipSet = {};
try {
const ipParsed = parse(ip);
const ipKind = ipParsed.kind();
const ipStr = ipParsed.toString({
format: ipKind === 'ipv4' ? 'v4' : 'v6',
zeroElide: false,
zeroPad: false,
});
let qrange
, hrange;
if (ipKind === 'ipv4') {
qrange = createCIDR(ipStr, 24).toString();
hrange = createCIDR(ipStr, 16).toString();
} else {
qrange = createCIDR(ipStr, 64).toString();
hrange = createCIDR(ipStr, 48).toString();
}
ipSet['ip.cloak'] = `${hashIp(hrange).substring(0,8)}.${hashIp(qrange).substring(0,7)}.${hashIp(ipStr).substring(0,7)}.IP`;
} catch (e) {
//-1 old "iphashpermlevel" or bypass ids, just shorten them
const shortenedOldHash = `${hashIp(ip).slice(-10)}.IP`;
ipSet['ip.raw'] = shortenedOldHash;
ipSet['ip.cloak'] = shortenedOldHash;
}
return {
'updateMany': {
'filter': {
'ip.raw': ip
},
'update': {
'$unset': {
'ip.single': '',
'ip.qrange': '',
'ip.hrange': '',
},
'$set': ipSet,
}
}
};
});
console.log('adjusting ip in modlogs, bans and posts');
//the bulkwrites should work for ip, bans, and logs
await db.collection('posts').bulkWrite(bulkWrites);
await db.collection('modlog').bulkWrite(bulkWrites);
await db.collection('bans').bulkWrite(bulkWrites);
console.log('removing saved posts inside bans');
await db.collection('bans').updateMany({}, {
'$set':{
'posts': null,
}
});
console.log('clearing reports')
await db.collection('posts').updateMany({}, {
'$set':{
'reports': [],
'globalreports': [],
}
});
//drop old ban indexes that indexed ip.single, then recreate
console.log('recreating bans indexes');
await db.collection('bans').dropIndexes();
await db.collection('bans').createIndex({ 'ip.cloak': 1 , 'board': 1 });
await db.collection('bans').createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 });
}

@ -118,13 +118,13 @@ module.exports = async (req, res, next) => {
} }
const postsBefore = res.locals.posts.length; const postsBefore = res.locals.posts.length;
if (req.body.delete_ip_board || req.body.delete_ip_global || req.body.delete_ip_thread) { if (req.body.delete_ip_board || req.body.delete_ip_global || req.body.delete_ip_thread) {
const deletePostIps = res.locals.posts.map(x => x.ip.single); const deletePostIps = res.locals.posts.map(x => x.ip.cloak);
const deletePostMongoIds = res.locals.posts.map(x => x._id) const deletePostMongoIds = res.locals.posts.map(x => x._id)
let query = { let query = {
'_id': { '_id': {
'$nin': deletePostMongoIds '$nin': deletePostMongoIds
}, },
'ip.single': { 'ip.cloak': {
'$in': deletePostIps '$in': deletePostIps
} }
}; };
@ -314,7 +314,7 @@ module.exports = async (req, res, next) => {
message: message, message: message,
user: logUser, user: logUser,
ip: { ip: {
single: res.locals.ip.single, cloak: res.locals.ip.cloak,
raw: res.locals.ip.raw raw: res.locals.ip.raw
} }
}; };

@ -4,6 +4,6 @@ const { Bans } = require(__dirname+'/../../db/');
module.exports = async (req, res, next) => { module.exports = async (req, res, next) => {
return Bans.appeal(res.locals.ip.single, req.body.checkedbans, req.body.message).then(r => r.modifiedCount); return Bans.appeal(res.locals.ip.cloak, req.body.checkedbans, req.body.message).then(r => r.modifiedCount);
} }

@ -16,25 +16,31 @@ module.exports = async (req, res, next) => {
if (req.body.ban || req.body.global_ban) { if (req.body.ban || req.body.global_ban) {
const banBoard = req.body.global_ban ? null : req.params.board; const banBoard = req.body.global_ban ? null : req.params.board;
const ipPosts = res.locals.posts.reduce((acc, post) => { const ipPosts = res.locals.posts.reduce((acc, post) => {
if (!acc[post.ip.single]) { if (!acc[post.ip.cloak]) {
acc[post.ip.single] = []; acc[post.ip.cloak] = [];
} }
acc[post.ip.single].push(post); acc[post.ip.cloak].push(post);
return acc; return acc;
}, {}); }, {});
for (let ip in ipPosts) { for (let ip in ipPosts) {
const thisIpPosts = ipPosts[ip]; const thisIpPosts = ipPosts[ip];
let type = 'single'; let type = 'single';
let banIp = { let banIp = {
single: ip, cloak: thisIpPosts[0].ip.cloak,
raw: thisIpPosts[0].ip.raw raw: thisIpPosts[0].ip.raw,
}; };
if (req.body.ban_h) { if (req.body.ban_h) {
type = 'half'; type = 'half';
banIp.single = thisIpPosts[0].ip.hrange; banIp.cloak = thisIpPosts[0].ip.cloak
.split('.')
.slice(0,1)
.join('.');
} else if (req.body.ban_q) { } else if (req.body.ban_q) {
type = 'quarter'; type = 'quarter';
banIp.single = thisIpPosts[0].ip.qrange; banIp.cloak = thisIpPosts[0].ip.cloak
.split('.')
.slice(0,2)
.join('.');
} }
bans.push({ bans.push({
type, type,

@ -51,7 +51,7 @@ todo: handle some more situations
const banExpiry = new Date(globalSettings.filterBanDuration + banDate.getTime()); const banExpiry = new Date(globalSettings.filterBanDuration + banDate.getTime());
const ban = { const ban = {
'ip': { 'ip': {
'single': res.locals.ip.single, 'cloak': res.locals.ip.cloak,
'raw': res.locals.ip.raw, 'raw': res.locals.ip.raw,
}, },
'type': 'single', 'type': 'single',
@ -163,7 +163,7 @@ todo: handle some more situations
message: req.body.log_message || null, message: req.body.log_message || null,
user: req.session.user, user: req.session.user,
ip: { ip: {
single: res.locals.ip.single, cloak: res.locals.ip.cloak,
raw: res.locals.ip.raw, raw: res.locals.ip.raw,
} }
}); });

@ -144,7 +144,7 @@ ${res.locals.numFiles > 0 ? req.files.file.map(f => f.name+'|'+(f.phash || '')).
const banExpiry = new Date(useFilterBanDuration + banDate.getTime()); const banExpiry = new Date(useFilterBanDuration + banDate.getTime());
const ban = { const ban = {
'ip': { 'ip': {
'single': res.locals.ip.single, 'cloak': res.locals.ip.cloak,
'raw': res.locals.ip.raw, 'raw': res.locals.ip.raw,
}, },
'type': 'single', 'type': 'single',
@ -612,15 +612,15 @@ ${res.locals.numFiles > 0 ? req.files.file.map(f => f.name+'|'+(f.phash || '')).
//dont emit thread to this socket, because the room onyl exists when the thread is open //dont emit thread to this socket, because the room onyl exists when the thread is open
Socketio.emitRoom(`${res.locals.board._id}-${data.thread}`, 'newPost', projectedPost); Socketio.emitRoom(`${res.locals.board._id}-${data.thread}`, 'newPost', projectedPost);
} }
const { raw, single } = data.ip; const { raw, cloak } = data.ip;
//but emit it to manage pages because they need to get all posts through socket including thread //but emit it to manage pages because they need to get all posts through socket including thread
Socketio.emitRoom('globalmanage-recent-hashed', 'newPost', { ...projectedPost, ip: { single: single.slice(-10), raw: null } }); Socketio.emitRoom('globalmanage-recent-hashed', 'newPost', { ...projectedPost, ip: { cloak, raw: null } });
Socketio.emitRoom(`${res.locals.board._id}-manage-recent-hashed`, 'newPost', { ...projectedPost, ip: { single: single.slice(-10), raw: null } }); Socketio.emitRoom(`${res.locals.board._id}-manage-recent-hashed`, 'newPost', { ...projectedPost, ip: { cloak, raw: null } });
if (ipHashPermLevel > -1) { if (ipHashPermLevel > -1) {
//small optimisation for boards where this is manually set to -1 for privacy, no need to emit to rooms that cant be accessed //small optimisation for boards where this is manually set to -1 for privacy, no need to emit to rooms that cant be accessed
//even if they are empty it will create extra communication noise in redis, socket adapter, etc. //even if they are empty it will create extra communication noise in redis, socket adapter, etc.
Socketio.emitRoom('globalmanage-recent-raw', 'newPost', { ...projectedPost, ip: { single: single.slice(-10), raw } }); Socketio.emitRoom('globalmanage-recent-raw', 'newPost', { ...projectedPost, ip: { cloak, raw } });
Socketio.emitRoom(`${res.locals.board._id}-manage-recent-raw`, 'newPost', { ...projectedPost, ip: { single: single.slice(-10), raw } }); Socketio.emitRoom(`${res.locals.board._id}-manage-recent-raw`, 'newPost', { ...projectedPost, ip: { cloak, raw } });
} }
//now add other pages to be built in background //now add other pages to be built in background

@ -9,7 +9,7 @@ module.exports = (req, res) => {
'reason': req.body.report_reason, 'reason': req.body.report_reason,
'date': new Date(), 'date': new Date(),
'ip': { 'ip': {
'single': res.locals.ip.single, 'cloak': res.locals.ip.cloak,
'raw': res.locals.ip.raw 'raw': res.locals.ip.raw
} }
} }

@ -21,7 +21,7 @@ module.exports = async (req, res, next) => {
let maxAge = 5*60*1000; let maxAge = 5*60*1000;
try { try {
if (!res.locals.anonymizer) { if (!res.locals.anonymizer) {
const ratelimit = await Ratelimits.incrmentQuota(res.locals.ip.single, 'captcha', rateLimitCost.captcha); const ratelimit = await Ratelimits.incrmentQuota(res.locals.ip.cloak, 'captcha', rateLimitCost.captcha);
if (ratelimit > 100) { if (ratelimit > 100) {
return res.status(429).redirect('/file/ratelimit.png'); return res.status(429).redirect('/file/ratelimit.png');
} }

@ -3,6 +3,7 @@
const { Modlogs } = require(__dirname+'/../../../db/') const { Modlogs } = require(__dirname+'/../../../db/')
, pageQueryConverter = require(__dirname+'/../../../helpers/pagequeryconverter.js') , pageQueryConverter = require(__dirname+'/../../../helpers/pagequeryconverter.js')
, decodeQueryIP = require(__dirname+'/../../../helpers/decodequeryip.js') , decodeQueryIP = require(__dirname+'/../../../helpers/decodequeryip.js')
, { isIP } = require('net')
, limit = 50; , limit = 50;
module.exports = async (req, res, next) => { module.exports = async (req, res, next) => {
@ -19,10 +20,12 @@ module.exports = async (req, res, next) => {
filter.board = uri; filter.board = uri;
} }
const ipMatch = decodeQueryIP(req.query, res.locals.permLevel); const ipMatch = decodeQueryIP(req.query, res.locals.permLevel);
if (ipMatch instanceof RegExp) { if (ipMatch != null) {
filter['ip.single'] = ipMatch; if (isIP(ipMatch)) {
} else if (typeof ipMatch === 'string') { filter['ip.raw'] = ipMatch;
filter['ip.raw'] = ipMatch; } else {
filter['ip.cloak'] = ipMatch;
}
} }
let logs, maxPage; let logs, maxPage;

@ -13,7 +13,7 @@ module.exports = async (req, res, next) => {
if (postId && +postId === parseInt(postId) && Number.isSafeInteger(+postId)) { if (postId && +postId === parseInt(postId) && Number.isSafeInteger(+postId)) {
const fetchedPost = await Posts.getPost(req.params.board, +postId, true); const fetchedPost = await Posts.getPost(req.params.board, +postId, true);
if (fetchedPost) { if (fetchedPost) {
ip = decodeQueryIP({ ip: fetchedPost.ip.single.slice(-10) }, res.locals.permlevel); ip = decodeQueryIP({ ip: fetchedPost.ip.cloak }, res.locals.permlevel);
} }
} }

594
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
{ {
"name": "jschan", "name": "jschan",
"version": "0.3.3", "version": "0.4.0",
"migrateVersion": "0.2.0", "migrateVersion": "0.4.0",
"description": "", "description": "",
"main": "server.js", "main": "server.js",
"dependencies": { "dependencies": {

@ -22,7 +22,7 @@ module.exports = {
$ne: true $ne: true
} }
}).forEach(post => { }).forEach(post => {
const randomIP = createHash('sha256').update(tempIpHashSecret + post.ip.single).digest('base64'); const randomIP = createHash('sha256').update(tempIpHashSecret + post.ip.cloak).digest('base64');
bulkWrites.push({ bulkWrites.push({
updateOne: { updateOne: {
filter: { filter: {
@ -31,10 +31,8 @@ module.exports = {
update: { update: {
$set: { $set: {
'ip.pruned': true, 'ip.pruned': true,
'ip.raw': randomIP, 'ip.raw': `${randomIP.slice(-10)}.PRUNED`,
'ip.single': randomIP, 'ip.cloak': `${randomIP.slice(-10)}.PRUNED`,
'ip.qrange': randomIP,
'ip.hrange': randomIP,
} }
} }
} }

@ -10,8 +10,11 @@ mixin ban(ban, banpage)
else else
| Global | Global
td= ban.reason td= ban.reason
- const ip = permLevel > ipHashPermLevel ? ban.ip.single.slice(-10) : ban.ip.raw; - const ip = permLevel > ipHashPermLevel ? ban.ip.cloak : ban.ip.raw;
td #{ip} if permLevel > ipHashPermLevel
td #{ip}#{ban.type === 'half' ? '.*.*' : (ban.type === 'quarter' ? '.*' : '')}
else
td #{ip}
td #{ban.type} td #{ban.type}
td #{(!banpage || ban.showUser === true) ? ban.issuer : 'Hidden User'} td #{(!banpage || ban.showUser === true) ? ban.issuer : 'Hidden User'}
- const banDate = new Date(ban.date); - const banDate = new Date(ban.date);

@ -20,12 +20,12 @@ mixin post(post, truncate, manage=false, globalmanage=false, ban=false, overboar
input.post-check(type='checkbox', name='checkedposts' value=post.postId) input.post-check(type='checkbox', name='checkedposts' value=post.postId)
| |
if manage if manage
- const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.raw; - const ip = permLevel > ipHashPermLevel ? post.ip.cloak : post.ip.raw;
a.bold(href=`${upLevel ? '../' : ''}recent.html?ip=${encodeURIComponent(ip)}`) [#{ip}] a.bold(href=`${upLevel ? '../' : ''}recent.html?ip=${encodeURIComponent(ip)}`) [#{ip}]
else if modview else if modview
a.bold(href=`${upLevel ? '../' : ''}recent.html?postid=${post.postId}`) [+] a.bold(href=`${upLevel ? '../' : ''}recent.html?postid=${post.postId}`) [+]
else if globalmanage else if globalmanage
- const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.raw; - const ip = permLevel > ipHashPermLevel ? post.ip.cloak : post.ip.raw;
a.bold(href=`?ip=${encodeURIComponent(ip)}`) [#{ip}] a.bold(href=`?ip=${encodeURIComponent(ip)}`) [#{ip}]
| |
if !post.thread if !post.thread

@ -2,7 +2,7 @@ mixin report(r, manage=false)
.reports.post-container .reports.post-container
input.post-check(type='checkbox', name='checkedreports' value=r.id) input.post-check(type='checkbox', name='checkedreports' value=r.id)
| |
- const ip = permLevel > ipHashPermLevel ? r.ip.single.slice(-10) : r.ip.raw; - const ip = permLevel > ipHashPermLevel ? r.ip.cloak : r.ip.raw;
a.bold(href=`${manage ? 'recent.html' : ''}?ip=${encodeURIComponent(ip)}`) [#{ip}] a.bold(href=`${manage ? 'recent.html' : ''}?ip=${encodeURIComponent(ip)}`) [#{ip}]
| |
- const reportDate = new Date(r.date); - const reportDate = new Date(r.date);

@ -52,7 +52,7 @@ block content
| |
a(href=`?username=${log.user}`) [+] a(href=`?username=${log.user}`) [+]
td td
- const logIp = permLevel > ipHashPermLevel ? log.ip.single.slice(-10) : log.ip.raw; - const logIp = permLevel > ipHashPermLevel ? log.ip.cloak : log.ip.raw;
a(href=`recent.html?ip=${encodeURIComponent(logIp)}`) #{logIp} a(href=`recent.html?ip=${encodeURIComponent(logIp)}`) #{logIp}
| |
a(href=`?ip=${encodeURIComponent(logIp)}`) [+] a(href=`?ip=${encodeURIComponent(logIp)}`) [+]

@ -40,7 +40,7 @@ block content
| |
a(href=`?username=${log.user}`) [+] a(href=`?username=${log.user}`) [+]
td td
- const logIp = permLevel > ipHashPermLevel ? log.ip.single.slice(-10) : log.ip.raw; - const logIp = permLevel > ipHashPermLevel ? log.ip.cloak : log.ip.raw;
| #{logIp} | #{logIp}
td #{log.actions} td #{log.actions}
td td

@ -23,7 +23,7 @@ block content
if posts.length === 0 if posts.length === 0
p No posts. p No posts.
else else
- const ip = permLevel > ipHashPermLevel ? posts[0].ip.single.slice(-10) : posts[0].ip.raw; - const ip = permLevel > ipHashPermLevel ? posts[0].ip.cloak : posts[0].ip.raw;
if postId || (queryIp && queryIp === ip) if postId || (queryIp && queryIp === ip)
h4.no-m-p Post history for #{ip} h4.no-m-p Post history for #{ip}
| |

Loading…
Cancel
Save