Improve filter handling code

This commit is contained in:
Calvin Montgomery 2014-12-28 11:12:37 -05:00
parent aa5e50f1d2
commit 25eba6ab2b
3 changed files with 77 additions and 66 deletions

View file

@ -1,26 +1,34 @@
var FilterList = require('cytubefilters'); var FilterList = require("cytubefilters");
var ChannelModule = require("./module"); var ChannelModule = require("./module");
var XSS = require("../xss"); var XSS = require("../xss");
var Logger = require("../logger"); var Logger = require("../logger");
/*
* Converts JavaScript-style replacements ($1, $2, etc.) with
* PCRE-style (\1, \2, etc.)
*/
function fixReplace(replace) {
return replace.replace(/\$(\d)/g, "\\$1");
}
function validateFilter(f) { function validateFilter(f) {
if (typeof f.source !== "string" || typeof f.flags !== "string" || if (typeof f.source !== "string" || typeof f.flags !== "string" ||
typeof f.replace !== "string") { typeof f.replace !== "string") {
return false; return null;
} }
if (typeof f.name !== "string") { if (typeof f.name !== "string") {
f.name = f.source; f.name = f.source;
} }
f.replace = f.replace.substring(0, 1000); f.replace = fixReplace(f.replace.substring(0, 1000));
f.replace = XSS.sanitizeHTML(f.replace); f.replace = XSS.sanitizeHTML(f.replace);
f.flags = f.flags.substring(0, 4); f.flags = f.flags.substring(0, 4);
try { try {
new RegExp(f.source, f.flags); FilterList.checkValidRegex(f.source);
} catch (e) { } catch (e) {
return false; return null;
} }
var filter = { var filter = {
@ -35,58 +43,47 @@ function validateFilter(f) {
return filter; return filter;
} }
function fixReplace(replace) {
return replace.replace(/\$(\d)/g, '\\$1');
}
function makeDefaultFilter(name, source, flags, replace) { function makeDefaultFilter(name, source, flags, replace) {
return { return {
name: name, name: name,
source: source, source: source,
flags: flags, flags: flags,
replace: fixReplace(replace), replace: replace,
active: true, active: true,
filterlinks: false filterlinks: false
}; };
} }
const DEFAULT_FILTERS = [ const DEFAULT_FILTERS = [
makeDefaultFilter("monospace", "`(.+?)`", "g", "<code>$1</code>"), makeDefaultFilter("monospace", "`(.+?)`", "g", "<code>\\1</code>"),
makeDefaultFilter("bold", "\\*(.+?)\\*", "g", "<strong>$1</strong>"), makeDefaultFilter("bold", "\\*(.+?)\\*", "g", "<strong>\\1</strong>"),
makeDefaultFilter("italic", "_(.+?)_", "g", "<em>$1</em>"), makeDefaultFilter("italic", "_(.+?)_", "g", "<em>\\1</em>"),
makeDefaultFilter("strike", "~~(.+?)~~", "g", "<s>$1</s>"), makeDefaultFilter("strike", "~~(.+?)~~", "g", "<s>\\1</s>"),
makeDefaultFilter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig", "<span class=\"spoiler\">$1</span>") makeDefaultFilter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig",
"<span class=\"spoiler\">\\1</span>")
]; ];
function ChatFilterModule(channel) { function ChatFilterModule(channel) {
ChannelModule.apply(this, arguments); ChannelModule.apply(this, arguments);
this.filters = new FilterList(DEFAULT_FILTERS); this.filters = new FilterList();
} }
ChatFilterModule.prototype = Object.create(ChannelModule.prototype); ChatFilterModule.prototype = Object.create(ChannelModule.prototype);
ChatFilterModule.prototype.load = function (data) { ChatFilterModule.prototype.load = function (data) {
if ("filters" in data) { if ("filters" in data) {
for (var i = 0; i < data.filters.length; i++) { var filters = data.filters.map(validateFilter).filter(function (f) {
var f = validateFilter(data.filters[i]); return f !== null;
if (f) { });
try { try {
this.filters.updateFilter(f); this.filters = new FilterList(filters);
} catch (e) { } catch (e) {
if (e.message.match(/does not exist/i)) { Logger.errlog.log("Filter load failed: " + e + " (channel:" +
try { this.channel.name);
this.filters.addFilter(f); this.channel.logger.log("Failed to load filters: " + e);
} catch (e) {
Logger.errlog.log("Filter load failed: " +
JSON.stringify(f) + " c:" + this.channel.name);
}
} else {
Logger.errlog.log("Filter load failed: " +
JSON.stringify(f) + " c:" + this.channel.name);
}
}
}
} }
} else {
this.filters = new FilterList(DEFAULT_FILTERS);
} }
}; };
@ -128,6 +125,16 @@ ChatFilterModule.prototype.handleAddFilter = function (user, data) {
return; return;
} }
try {
FilterList.checkValidRegex(data.source);
} catch (e) {
user.socket.emit("errorMsg", {
msg: "Invalid regex: " + e.message,
alert: true
});
return;
}
data = validateFilter(data); data = validateFilter(data);
if (!data) { if (!data) {
return; return;
@ -142,6 +149,9 @@ ChatFilterModule.prototype.handleAddFilter = function (user, data) {
}); });
return; return;
} }
user.socket.emit("addFilterSuccess");
var chan = this.channel; var chan = this.channel;
chan.users.forEach(function (u) { chan.users.forEach(function (u) {
if (chan.modules.permissions.canEditFilters(u)) { if (chan.modules.permissions.canEditFilters(u)) {
@ -150,8 +160,8 @@ ChatFilterModule.prototype.handleAddFilter = function (user, data) {
}); });
chan.logger.log("[mod] " + user.getName() + " added filter: " + data.name + " -> " + chan.logger.log("[mod] " + user.getName() + " added filter: " + data.name + " -> " +
"s/" + data.source + "/" + data.replace + "/" + data.flags + " active: " + "s/" + data.source + "/" + data.replace + "/" + data.flags +
data.active + ", filterlinks: " + data.filterlinks); " active: " + data.active + ", filterlinks: " + data.filterlinks);
}; };
ChatFilterModule.prototype.handleUpdateFilter = function (user, data) { ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
@ -163,6 +173,16 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
return; return;
} }
try {
FilterList.checkValidRegex(data.source);
} catch (e) {
user.socket.emit("errorMsg", {
msg: "Invalid regex: " + e.message,
alert: true
});
return;
}
data = validateFilter(data); data = validateFilter(data);
if (!data) { if (!data) {
return; return;
@ -171,14 +191,10 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
try { try {
this.filters.updateFilter(data); this.filters.updateFilter(data);
} catch (e) { } catch (e) {
if (e.message.match(/filter to be updated does not exist/i)) { user.socket.emit("errorMsg", {
this.handleAddFilter(user, data); msg: "Filter update failed: " + e.message,
} else { alert: true
user.socket.emit("errorMsg", { });
msg: "Filter update failed: " + e.message,
alert: true
});
}
return; return;
} }
@ -190,8 +206,8 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
}); });
chan.logger.log("[mod] " + user.getName() + " updated filter: " + data.name + " -> " + chan.logger.log("[mod] " + user.getName() + " updated filter: " + data.name + " -> " +
"s/" + data.source + "/" + data.replace + "/" + data.flags + " active: " + "s/" + data.source + "/" + data.replace + "/" + data.flags +
data.active + ", filterlinks: " + data.filterlinks); " active: " + data.active + ", filterlinks: " + data.filterlinks);
}; };
ChatFilterModule.prototype.handleImportFilters = function (user, data) { ChatFilterModule.prototype.handleImportFilters = function (user, data) {
@ -207,7 +223,7 @@ ChatFilterModule.prototype.handleImportFilters = function (user, data) {
try { try {
this.filters = new FilterList(data.map(validateFilter).filter(function (f) { this.filters = new FilterList(data.map(validateFilter).filter(function (f) {
return f !== false; return f !== null;
})); }));
} catch (e) { } catch (e) {
user.socket.emit("errorMsg", { user.socket.emit("errorMsg", {
@ -249,6 +265,7 @@ ChatFilterModule.prototype.handleRemoveFilter = function (user, data) {
u.socket.emit("deleteChatFilter", data); u.socket.emit("deleteChatFilter", data);
} }
}); });
this.channel.logger.log("[mod] " + user.getName() + " removed filter: " + data.name); this.channel.logger.log("[mod] " + user.getName() + " removed filter: " + data.name);
}; };

View file

@ -621,14 +621,7 @@ $("#cs-chatfilters-newsubmit").click(function () {
"match."); "match.");
} }
try { socket.emit("addFilter", {
new RegExp(regex, flags);
} catch (e) {
alert("Regex error: " + e);
return;
}
socket.emit("updateFilter", {
name: name, name: name,
source: regex, source: regex,
flags: flags, flags: flags,
@ -636,10 +629,13 @@ $("#cs-chatfilters-newsubmit").click(function () {
active: true active: true
}); });
$("#cs-chatfilters-newname").val(""); socket.once("addFilterSuccess", function () {
$("#cs-chatfilters-newregex").val(""); console.log("addFilterSuccess");
$("#cs-chatfilters-newflags").val(""); $("#cs-chatfilters-newname").val("");
$("#cs-chatfilters-newreplace").val(""); $("#cs-chatfilters-newregex").val("");
$("#cs-chatfilters-newflags").val("");
$("#cs-chatfilters-newreplace").val("");
});
}); });
$("#cs-emotes-newsubmit").click(function () { $("#cs-emotes-newsubmit").click(function () {

View file

@ -2304,14 +2304,12 @@ function formatCSChatFilterList() {
f.flags = flags.val(); f.flags = flags.val();
f.replace = replace.val(); f.replace = replace.val();
f.filterlinks = filterlinks.prop("checked"); f.filterlinks = filterlinks.prop("checked");
try {
new RegExp(f.source, f.flags);
} catch (e) {
alert("Invalid regex: " + e);
}
socket.emit("updateFilter", f); socket.emit("updateFilter", f);
reset(); socket.once("updateFilterSuccess", function () {
console.log("updateFilterSuccess");
reset();
});
}); });
control.data("editor", tr2); control.data("editor", tr2);