Add XSS filter

This commit is contained in:
calzoneman 2014-01-23 11:45:08 -06:00
parent feca68538e
commit 551d5b2c36
3 changed files with 44 additions and 25 deletions

View file

@ -8,6 +8,7 @@ var AsyncQueue = require("./asyncqueue");
var MakeEmitter = require("./emitter");
var InfoGetter = require("./get-info");
var ChatCommand = require("./chatcommand");
var XSS = require("./xss");
var fs = require("fs");
var path = require("path");
@ -572,16 +573,6 @@ Channel.prototype.part = function (user) {
}
};
/**
* Set the MOTD and broadcast it to connected users
*/
Channel.prototype.setMOTD = function (message) {
this.motd.motd = message;
// TODO XSS filter
this.motd.html = message.replace(/\n/g, "<br>");
this.sendMOTD(this.users);
};
/**
* Send the MOTD to the given users
*/
@ -2194,9 +2185,9 @@ Channel.prototype.validateChatFilter = function (f) {
}
f.replace = f.replace.substring(0, 1000);
f.replace = XSS.sanitizeHTML(f.replace);
f.flags = f.flags.substring(0, 4);
// TODO XSS prevention
try {
new RegExp(f.source, f.flags);
} catch (e) {
@ -2493,7 +2484,7 @@ Channel.prototype.handleSetJS = function (user, data) {
* Sets the MOTD
*/
Channel.prototype.setMotd = function (motd) {
// TODO XSS
motd = XSS.sanitizeHTML(motd);
var html = motd.replace(/\n/g, "<br>");
this.motd = {
motd: motd,
@ -2557,7 +2548,7 @@ Channel.prototype.handleChat = function (user, data) {
}
if (smuted) {
// TODO XSS
msg = XSS.sanitizeText(msg);
msg = this.filterMessage(msg);
var msgobj = {
username: user.name,
@ -2629,7 +2620,7 @@ Channel.prototype.filterMessage = function (msg) {
* Sends a chat message
*/
Channel.prototype.sendMessage = function (user, msg, meta) {
// TODO HTML escape
msg = XSS.sanitizeText(msg);
msg = this.filterMessage(msg);
var msgobj = {
username: user.name,
@ -2645,7 +2636,7 @@ Channel.prototype.sendMessage = function (user, msg, meta) {
}
this.logger.log("<" + user.name + (meta.addClass ? "." + meta.addClass : "") + "> " +
msg);
XSS.decodeText(msg));
};
/**

View file

@ -46,6 +46,16 @@ TagParser.prototype.readLiteral = function (regexp) {
str += this.text[this.i];
this.i++;
}
str = str.replace(/&#([0-9]{2,7});?/g, function (m, p1) {
return String.fromCharCode(parseInt(p1));
});
str = str.replace(/&#x([0-9a-fA-F]{2,7});?/g, function (m, p1) {
return String.fromCharCode(parseInt(p1, 16));
});
str = str.replace(/[\x00-\x1f]/g, "");
return str;
};
@ -53,7 +63,7 @@ TagParser.prototype.readLiteral = function (regexp) {
a string. Otherwise, read a literal
*/
TagParser.prototype.readLiteralOrString = function (regexp) {
if (this.text[this.i].match(/["']/)) {
if (this.text[this.i].match(/["'`]/)) {
return this.readString();
}
return this.readLiteral(regexp);
@ -78,6 +88,16 @@ TagParser.prototype.readString = function () {
this.i++;
}
this.i++;
str = str.replace(/&#([0-9]{2,7});?/g, function (m, p1) {
return String.fromCharCode(parseInt(p1));
});
str = str.replace(/&#x([0-9a-fA-F]{2,7});?/g, function (m, p1) {
return String.fromCharCode(parseInt(p1, 16));
});
str = str.replace(/[\x00-\x1f]/g, "");
return str;
};
@ -120,7 +140,7 @@ TagParser.prototype.parse = function () {
}
this.i++;
this.skipWhitespace();
//this.skipWhitespace();
var value = this.readLiteralOrString();
if (key.trim().length > 0) {
attrs[key] = value;
@ -181,7 +201,8 @@ const badTags = new RegExp([
*/
const badAttrs = new RegExp([
"\\bon\\S*",
"\\bformaction"
"\\bformaction",
"\\baction"
].join("|"), "i");
/* These are things commonly used in the values of HTML attributes of
@ -226,7 +247,7 @@ function sanitizeHTML(str) {
// If it's an evil attribute, just nuke it entirely
if (k.match(badAttrs)) {
delete t.attributes[k];
} else {
} else {
if (t.attributes[k].match(badAttrValues)) {
// As above, replacing with a nonempty string is important.
t.attributes[k] = t.attributes[k].replace(badAttrValues, "[removed]");
@ -240,7 +261,14 @@ function sanitizeHTML(str) {
if (k.trim().length > 0) {
fmt += " " + k;
if (t.attributes[k].trim().length > 0) {
fmt += '="' + t.attributes[k] + '"';
var delim = '"';
if (t.attributes[k].match(/[^\\]"/)) {
delim = "'";
if (t.attributes[k].match(/[^\\]'/)) {
delim = "`";
}
}
fmt += "=" + delim + t.attributes[k] + delim;
}
}
}
@ -264,10 +292,10 @@ function sanitizeText(str) {
}
function decodeText(str) {
str = str.replace(/&#([0-9]{2,4});?/g, function (m, p1) {
str = str.replace(/&#([0-9]{2,7});?/g, function (m, p1) {
return String.fromCharCode(parseInt(p1));
});
str = str.replace(/&#x([0-9a-f]{2,4});?/ig, function (m, p1) {
str = str.replace(/&#x([0-9a-f]{2,7});?/ig, function (m, p1) {
return String.fromCharCode(parseInt(p1, 16));
});
str = str.replace(/&lt;/g, "<")