CSRF protection for ImportingController

This commit is contained in:
Antonin Delpeuch 2019-10-11 13:53:43 +01:00
parent 70e37b9085
commit 91cead27f8
7 changed files with 221 additions and 175 deletions

View File

@ -56,6 +56,10 @@ public class ImportingControllerCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!checkCSRF(request)) {
respondCSRFError(response);
return;
}
ImportingController controller = getController(request);
if (controller != null) {
@ -92,4 +96,14 @@ public class ImportingControllerCommand extends Command {
}
return null;
}
/**
* Checks the validity of a CSRF token, without reading the whole POST body.
* See above for details.
*/
private boolean checkCSRF(HttpServletRequest request) {
Properties options = ParsingUtilities.parseUrlParameters(request);
String token = options.getProperty("csrf_token");
return token != null && csrfFactory.validToken(token);
}
}

View File

@ -0,0 +1,24 @@
package com.google.refine.commands.importing;
import com.google.refine.commands.CommandTestBase;
import java.io.IOException;
import javax.servlet.ServletException;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class ImportingControllerCommandTests extends CommandTestBase {
@BeforeMethod
public void setUpCommand() {
command = new ImportingControllerCommand();
}
@Test
public void testCSRFProtection() throws ServletException, IOException {
command.doPost(request, response);
assertCSRFCheckFailed();
}
}

View File

@ -157,15 +157,13 @@ ExpressionPreviewDialog.Widget.prototype.getExpression = function(commit) {
s = this._getLanguage() + ":" + s;
if (commit) {
Refine.wrapCSRF(function(token) {
$.post(
Refine.postCSRF(
"command/core/log-expression?" + $.param({ project: theProject.id }),
{ expression: s, csrf_token: token },
{ expression: s },
function(data) {
},
"json"
);
});
}
return s;
@ -286,12 +284,10 @@ ExpressionPreviewDialog.Widget.prototype._renderExpressionHistory = function(dat
.addClass(entry.starred ? "data-table-star-on" : "data-table-star-off")
.appendTo(tr.insertCell(0))
.click(function() {
Refine.wrapCSRF(function(token) {
$.post(
Refine.postCSRF(
"command/core/toggle-starred-expression",
{
expression: entry.code,
csrf_token: token
expression: entry.code
},
function(data) {
entry.starred = !entry.starred;
@ -301,7 +297,6 @@ ExpressionPreviewDialog.Widget.prototype._renderExpressionHistory = function(dat
"json"
);
});
});
$('<a href="javascript:{}">'+$.i18n('core-dialogs/reuse')+'</a>').appendTo(tr.insertCell(1)).click(function() {
self._elmts.expressionPreviewTextarea[0].value = o.expression;
@ -355,10 +350,9 @@ ExpressionPreviewDialog.Widget.prototype._renderStarredExpressions = function(da
var o = Scripting.parse(entry.code);
$('<a href="javascript:{}">'+$.i18n('core-dialogs/remove')+'</a>').appendTo(tr.insertCell(0)).click(function() {
Refine.wrapCSRF(function(token) {
$.post(
Refine.postCSRF(
"command/core/toggle-starred-expression",
{ expression: entry.code, returnList: true, csrf_token: token },
{ expression: entry.code, returnList: true },
function(data) {
self._renderStarredExpressions(data);
self._renderExpressionHistoryTab();
@ -366,7 +360,6 @@ ExpressionPreviewDialog.Widget.prototype._renderStarredExpressions = function(da
"json"
);
});
});
$('<a href="javascript:{}">Reuse</a>').appendTo(tr.insertCell(1)).click(function() {
self._elmts.expressionPreviewTextarea[0].value = o.expression;

View File

@ -92,7 +92,8 @@ Refine.DefaultImportingController.prototype.startImportJob = function(form, prog
.attr("action", "command/core/importing-controller?" + $.param({
"controller": "core/default-importing-controller",
"jobID": jobID,
"subCommand": "load-raw-data"
"subCommand": "load-raw-data",
"csrf_token": token
}));
form[0].submit();
@ -182,12 +183,14 @@ Refine.DefaultImportingController.prototype._ensureFormatParserUIHasInitializati
if (!(format in this._parserOptions)) {
var self = this;
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting'));
Refine.wrapCSRF(function(token) {
$.post(
"command/core/importing-controller?" + $.param({
"controller": "core/default-importing-controller",
"jobID": this._jobID,
"subCommand": "initialize-parser-ui",
"format": format
"format": format,
"csrf_token": token
}),
null,
function(data) {
@ -204,6 +207,7 @@ Refine.DefaultImportingController.prototype._ensureFormatParserUIHasInitializati
dismissBusy();
alert($.i18n('core-views/check-format'));
});
});
} else {
onDone();
}
@ -211,11 +215,13 @@ Refine.DefaultImportingController.prototype._ensureFormatParserUIHasInitializati
Refine.DefaultImportingController.prototype.updateFormatAndOptions = function(options, callback, finallyCallBack) {
var self = this;
Refine.wrapCSRF(function(token) {
$.post(
"command/core/importing-controller?" + $.param({
"controller": "core/default-importing-controller",
"jobID": this._jobID,
"subCommand": "update-format-and-options"
"subCommand": "update-format-and-options",
"csrf_token": token
}),
{
"format" : this._format,
@ -237,6 +243,7 @@ Refine.DefaultImportingController.prototype.updateFormatAndOptions = function(op
},
"json"
);
});
};
Refine.DefaultImportingController.prototype.getPreviewData = function(callback, numRows) {
@ -286,11 +293,13 @@ Refine.DefaultImportingController.prototype._createProject = function() {
var options = this._formatParserUI.getOptions();
options.projectName = projectName;
options.projectTags = projectTags;
Refine.wrapCSRF(function(token) {
$.post(
"command/core/importing-controller?" + $.param({
"controller": "core/default-importing-controller",
"jobID": this._jobID,
"subCommand": "create-project"
"subCommand": "create-project",
"csrf_token": token
}),
{
"format" : this._format,
@ -336,6 +345,7 @@ Refine.DefaultImportingController.prototype._createProject = function() {
},
"json"
);
});
}
};

View File

@ -328,11 +328,13 @@ Refine.DefaultImportingController.prototype._commitFileSelection = function() {
var self = this;
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting-files'));
Refine.wrapCSRF(function(token) {
$.post(
"command/core/importing-controller?" + $.param({
"controller": "core/default-importing-controller",
"jobID": this._jobID,
"subCommand": "update-file-selection"
"subCommand": "update-file-selection",
"csrf_token": token
}),
{
"fileSelection" : JSON.stringify(this._job.config.fileSelection)
@ -354,4 +356,5 @@ Refine.DefaultImportingController.prototype._commitFileSelection = function() {
},
"json"
);
});
};

View File

@ -388,19 +388,12 @@ Refine.postProcess = function(moduleName, command, params, body, updateOptions,
Refine.setAjaxInProgress();
Refine.wrapCSRF(
function(token) {
// Add it to the body and submit it as a POST request
body['csrf_token'] = token;
$.post(
Refine.postCSRF(
"command/" + moduleName + "/" + command + "?" + $.param(params),
body,
onDone,
"json"
);
}
);
window.setTimeout(function() {
if (!done) {
@ -422,6 +415,17 @@ Refine.wrapCSRF = function(onCSRF) {
);
};
// Performs a POST request where an additional CSRF token
// is supplied in the POST data. The arguments match those
// of $.post().
Refine.postCSRF = function(url, data, success, dataType) {
Refine.wrapCSRF(function(token) {
var fullData = data || {};
data['csrf_token'] = token;
$.post(url, fulldata, success, dataType);
});
};
Refine.setAjaxInProgress = function() {
$(document.body).attr("ajax_in_progress", "true");
};

View File

@ -124,17 +124,15 @@ ProcessPanel.prototype.undo = function() {
ProcessPanel.prototype._cancelAll = function() {
var self = this;
Refine.wrapCSRF(function(token) {
$.post(
Refine.postCSRF(
"command/core/cancel-processes?" + $.param({ project: theProject.id }),
{ csrf_token: token },
{ },
function(o) {
self._data = null;
self._runOnDones();
},
"json"
);
});
};
ProcessPanel.prototype._render = function(newData) {