Sanitize pad names
This commit is contained in:
parent
ddf1cd345c
commit
e8ef99fb72
5 changed files with 145 additions and 70 deletions
|
@ -38,6 +38,14 @@ var globalPads = {
|
|||
remove: function (name) { delete this[':'+name]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* An array of padId transformations. These represent changes in pad name policy over
|
||||
* time, and allow us to "play back" these changes so legacy padIds can be found.
|
||||
*/
|
||||
var padIdTransforms = [
|
||||
[/\s+/g, '_']
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns a Pad Object with the callback
|
||||
* @param id A String with the id of the pad
|
||||
|
@ -110,6 +118,39 @@ exports.doesPadExists = function(padId, callback)
|
|||
});
|
||||
}
|
||||
|
||||
//returns a sanitized padId, respecting legacy pad id formats
|
||||
exports.sanitizePadId = function(padId, callback) {
|
||||
var transform_index = arguments[2] || 0;
|
||||
//we're out of possible transformations, so just return it
|
||||
if(transform_index >= padIdTransforms.length)
|
||||
{
|
||||
callback(padId);
|
||||
}
|
||||
//check if padId exists
|
||||
else
|
||||
{
|
||||
exports.doesPadExists(padId, function(junk, exists)
|
||||
{
|
||||
if(exists)
|
||||
{
|
||||
callback(padId);
|
||||
}
|
||||
else
|
||||
{
|
||||
//get the next transformation *that's different*
|
||||
var transformedPadId = padId;
|
||||
while(transformedPadId == padId && transform_index < padIdTransforms.length)
|
||||
{
|
||||
transformedPadId = padId.replace(padIdTransforms[transform_index][0], padIdTransforms[transform_index][1]);
|
||||
transform_index += 1;
|
||||
}
|
||||
//check the next transform
|
||||
exports.sanitizePadId(transformedPadId, callback, transform_index);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exports.isValidPadId = function(padId)
|
||||
{
|
||||
return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
var ERR = require("async-stacktrace");
|
||||
var fs = require("fs");
|
||||
var api = require("../db/API");
|
||||
var padManager = require("../db/PadManager");
|
||||
|
||||
//ensure we have an apikey
|
||||
var apikey = null;
|
||||
|
@ -95,7 +96,33 @@ exports.handle = function(functionName, fields, req, res)
|
|||
res.send({code: 3, message: "no such function", data: null});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//sanitize any pad id's before continuing
|
||||
if(fields["padID"])
|
||||
{
|
||||
padManager.sanitizePadId(fields["padID"], function(padId)
|
||||
{
|
||||
fields["padID"] = padId;
|
||||
callAPI(functionName, fields, req, res);
|
||||
});
|
||||
}
|
||||
else if(fields["padName"])
|
||||
{
|
||||
padManager.sanitizePadId(fields["padName"], function(padId)
|
||||
{
|
||||
fields["padName"] = padId;
|
||||
callAPI(functionName, fields, req, res);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
callAPI(functionName, fields, req, res);
|
||||
}
|
||||
}
|
||||
|
||||
//calls the api function
|
||||
function callAPI(functionName, fields, req, res)
|
||||
{
|
||||
//put the function parameters in an array
|
||||
var functionParams = [];
|
||||
for(var i=0;i<functions[functionName].length;i++)
|
||||
|
|
141
node/server.js
141
node/server.js
|
@ -232,101 +232,108 @@ async.waterfall([
|
|||
res.send(html);
|
||||
});
|
||||
});
|
||||
|
||||
//serve pad.html under /p
|
||||
app.get('/p/:pad', function(req, res, next)
|
||||
{
|
||||
|
||||
//redirects browser to the pad's sanitized url if needed. otherwise, renders the html
|
||||
function goToPad(req, res, render) {
|
||||
//ensure the padname is valid and the url doesn't end with a /
|
||||
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
|
||||
{
|
||||
res.send('Such a padname is forbidden', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
res.header("Server", serverName);
|
||||
var filePath = path.normalize(__dirname + "/../static/pad.html");
|
||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||
else
|
||||
{
|
||||
padManager.sanitizePadId(req.params.pad, function(padId) {
|
||||
//the pad id was sanitized, so we redirect to the sanitized version
|
||||
if(padId != req.params.pad)
|
||||
{
|
||||
var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + padId);
|
||||
res.header('Location', real_path);
|
||||
res.send('You should be redirected to <a href="' + real_path + '">' + real_path + '</a>', 302);
|
||||
}
|
||||
//the pad id was fine, so just render it
|
||||
else
|
||||
{
|
||||
render();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//serve pad.html under /p
|
||||
app.get('/p/:pad', function(req, res, next)
|
||||
{
|
||||
goToPad(req, res, function() {
|
||||
res.header("Server", serverName);
|
||||
var filePath = path.normalize(__dirname + "/../static/pad.html");
|
||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||
});
|
||||
});
|
||||
|
||||
//serve timeslider.html under /p/$padname/timeslider
|
||||
app.get('/p/:pad/timeslider', function(req, res, next)
|
||||
{
|
||||
//ensure the padname is valid and the url doesn't end with a /
|
||||
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
|
||||
{
|
||||
res.send('Such a padname is forbidden', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
res.header("Server", serverName);
|
||||
var filePath = path.normalize(__dirname + "/../static/timeslider.html");
|
||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||
goToPad(req, res, function() {
|
||||
res.header("Server", serverName);
|
||||
var filePath = path.normalize(__dirname + "/../static/timeslider.html");
|
||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||
});
|
||||
});
|
||||
|
||||
//serve timeslider.html under /p/$padname/timeslider
|
||||
app.get('/p/:pad/export/:type', function(req, res, next)
|
||||
{
|
||||
//ensure the padname is valid and the url doesn't end with a /
|
||||
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
|
||||
{
|
||||
res.send('Such a padname is forbidden', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
||||
//send a 404 if we don't support this filetype
|
||||
if(types.indexOf(req.params.type) == -1)
|
||||
{
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
||||
if(settings.abiword == null &&
|
||||
["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
|
||||
{
|
||||
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
||||
return;
|
||||
}
|
||||
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Server", serverName);
|
||||
|
||||
hasPadAccess(req, res, function()
|
||||
{
|
||||
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
||||
goToPad(req, res, function() {
|
||||
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
||||
//send a 404 if we don't support this filetype
|
||||
if(types.indexOf(req.params.type) == -1)
|
||||
{
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
||||
if(settings.abiword == null &&
|
||||
["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
|
||||
{
|
||||
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
||||
return;
|
||||
}
|
||||
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Server", serverName);
|
||||
|
||||
hasPadAccess(req, res, function()
|
||||
{
|
||||
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//handle import requests
|
||||
app.post('/p/:pad/import', function(req, res, next)
|
||||
{
|
||||
//ensure the padname is valid and the url doesn't end with a /
|
||||
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
|
||||
{
|
||||
res.send('Such a padname is forbidden', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
//if abiword is disabled, skip handling this request
|
||||
if(settings.abiword == null)
|
||||
{
|
||||
next();
|
||||
return;
|
||||
}
|
||||
goToPad(req, res, function() {
|
||||
//if abiword is disabled, skip handling this request
|
||||
if(settings.abiword == null)
|
||||
{
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
res.header("Server", serverName);
|
||||
|
||||
hasPadAccess(req, res, function()
|
||||
{
|
||||
importHandler.doImport(req, res, req.params.pad);
|
||||
res.header("Server", serverName);
|
||||
|
||||
hasPadAccess(req, res, function()
|
||||
{
|
||||
importHandler.doImport(req, res, req.params.pad);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var apiLogger = log4js.getLogger("API");
|
||||
|
||||
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
||||
var apiCaller = function(req, res, fields) {
|
||||
var apiCaller = function(req, res, fields)
|
||||
{
|
||||
res.header("Server", serverName);
|
||||
res.header("Content-Type", "application/json; charset=utf-8");
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ function handshake()
|
|||
padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces
|
||||
|
||||
if(!isReconnect)
|
||||
document.title = document.title + " | " + padId;
|
||||
document.title = document.title + " | " + padId.replace(/_+/g, ' ');
|
||||
|
||||
var token = readCookie("token");
|
||||
if (token == null)
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
padId = decodeURIComponent(urlParts[urlParts.length-2]);
|
||||
|
||||
//set the title
|
||||
document.title = document.title + " | " + padId;
|
||||
document.title = document.title + " | " + padId.replace(/_+/g, ' ');
|
||||
|
||||
//ensure we have a token
|
||||
token = readCookie("token");
|
||||
|
|
Loading…
Reference in a new issue