Skip to content

Commit

Permalink
CSRF protection for project and recon commands
Browse files Browse the repository at this point in the history
  • Loading branch information
wetneb committed Oct 15, 2019
1 parent a340c13 commit 3559eeb
Show file tree
Hide file tree
Showing 31 changed files with 256 additions and 47 deletions.
15 changes: 15 additions & 0 deletions main/src/com/google/refine/commands/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,21 @@ protected boolean hasValidCSRFToken(HttpServletRequest request) throws ServletEx
throw new ServletException("Can't find CSRF token: missing or bad URL parameter");
}


/**
* Checks the validity of a CSRF token, without reading the whole POST body.
* Useful when we need to control how the POST body is read (for instance if it
* contains files).
*/
protected boolean hasValidCSRFTokenAsGET(HttpServletRequest request) {
if (request == null) {
throw new IllegalArgumentException("parameter 'request' should not be null");
}
Properties options = ParsingUtilities.parseUrlParameters(request);
String token = options.getProperty("csrf_token");
return token != null && csrfFactory.validToken(token);
}

protected static class HistoryEntryResponse {
@JsonProperty("code")
protected String getCode() { return "ok"; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class ImportingControllerCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!checkCSRF(request)) {
if(!hasValidCSRFTokenAsGET(request)) {
respondCSRFError(response);
return;
}
Expand Down Expand Up @@ -96,14 +96,4 @@ private ImportingController getController(HttpServletRequest request) {
}
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public class CreateProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFTokenAsGET(request)) {
respondCSRFError(response);
return;
}

ProjectManager.singleton.setBusy(true);
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public class DeleteProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

response.setHeader("Content-Type", "application/json");
try {
long projectID = Long.parseLong(request.getParameter("project"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
import com.google.refine.model.Project;

public class ExportProjectCommand extends Command {

/**
* This command uses POST but is left CSRF-unprotected as it does not incur a state change.
*/

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

public class ExportRowsCommand extends Command {
private static final Logger logger = LoggerFactory.getLogger("ExportRowsCommand");

/**
* This command uses POST but is left CSRF-unprotected as it does not incur a state change.
*/

@SuppressWarnings("unchecked")
static public Properties getRequestParameters(HttpServletRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
import com.google.refine.model.RecordModel;

public class GetModelsCommand extends Command {

/**
* This command uses POST but is left CSRF-unprotected as it does not incur a state change.
*/

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public class ImportProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFTokenAsGET(request)) {
respondCSRFError(response);
return;
}

ProjectManager.singleton.setBusy(true);
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ public class RenameProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

try {
String name = request.getParameter("name");
ProjectMetadata pm = getProjectMetadata(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public class SetProjectMetadataCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

Project project = request.getParameter("project") != null ? getProject(request) : null;
String metaName = request.getParameter("name");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public class SetProjectTagsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

response.setHeader("Content-Type", "application/json");

Project project;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ protected TypesResponse(
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

try {
Project project = getProject(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public PreviewResponse(List<ColumnInfo> columns2, List<List<Object>> rows2) {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

try {
Project project = getProject(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ protected CellResponse(HistoryEntry historyEntry, Cell newCell, Pool newPool) {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

try {
Project project = getProject(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ public class ReconJudgeOneCellCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(!hasValidCSRFToken(request)) {
respondCSRFError(response);
return;
}

try {
request.setCharacterEncoding("UTF-8");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.google.refine.commands.project;

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 ImportProjectCommandTests extends CommandTestBase {

@BeforeMethod
public void setUpCommand() {
command = new ImportProjectCommand();
}

@Test
public void testCSRFProtection() throws ServletException, IOException {
command.doPost(request, response);
assertCSRFCheckFailed();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.google.refine.commands.project;

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 RenameProjectCommandTests extends CommandTestBase {

@BeforeMethod
public void setUpCommand() {
command = new RenameProjectCommand();
}

@Test
public void testCSRFProtection() throws ServletException, IOException {
command.doPost(request, response);
assertCSRFCheckFailed();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
import com.google.refine.ProjectManager;
import com.google.refine.ProjectMetadata;
import com.google.refine.RefineTest;
import com.google.refine.commands.Command;
import com.google.refine.commands.project.SetProjectMetadataCommand;
import com.google.refine.model.Project;
import com.google.refine.util.ParsingUtilities;
Expand Down Expand Up @@ -101,6 +102,7 @@ public void SetUp() throws IOException {

// mock dependencies
when(request.getParameter("project")).thenReturn(PROJECT_ID);
when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
when(projMan.getProject(anyLong())).thenReturn(proj);
when(proj.getMetadata()).thenReturn(metadata);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.google.refine.commands.project;

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 SetProjectTagsCommandTests extends CommandTestBase {

@BeforeMethod
public void setUpCommand() {
command = new SetProjectTagsCommand();
}

@Test
public void testCSRFProtection() throws ServletException, IOException {
command.doPost(request, response);
assertCSRFCheckFailed();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.google.refine.commands.recon;

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 GuessTypesOfColumnCommandTests extends CommandTestBase {

@BeforeMethod
public void setUpCommand() {
command = new GuessTypesOfColumnCommand();
}

@Test
public void testCSRFProtection() throws ServletException, IOException {
command.doPost(request, response);
assertCSRFCheckFailed();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.google.refine.commands.recon;

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 PreviewExtendDataCommandTests extends CommandTestBase {

@BeforeMethod
public void setUpCommand() {
command = new PreviewExtendDataCommand();
}

@Test
public void testCSRFProtection() throws ServletException, IOException {
command.doPost(request, response);
assertCSRFCheckFailed();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.google.refine.commands.recon;

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 ReconClearOneCellCommandTests extends CommandTestBase {

@BeforeMethod
public void setUpCommand() {
command = new ReconClearOneCellCommand();
}

@Test
public void testCSRFProtection() throws ServletException, IOException {
command.doPost(request, response);
assertCSRFCheckFailed();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public void setUp() {
response = mock(HttpServletResponse.class);

when(request.getParameter("project")).thenReturn(String.valueOf(project.id));
when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());

writer = mock(PrintWriter.class);
try {
Expand Down
2 changes: 1 addition & 1 deletion main/webapp/modules/core/MOD-INF/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function registerCommands() {

RS.registerCommand(module, "get-project-metadata", new Packages.com.google.refine.commands.project.GetProjectMetadataCommand());
RS.registerCommand(module, "get-all-project-metadata", new Packages.com.google.refine.commands.workspace.GetAllProjectMetadataCommand());
RS.registerCommand(module, "set-metaData", new Packages.com.google.refine.commands.project.SetProjectMetadataCommand());
RS.registerCommand(module, "set-project-metadata", new Packages.com.google.refine.commands.project.SetProjectMetadataCommand());
RS.registerCommand(module, "get-all-project-tags", new Packages.com.google.refine.commands.workspace.GetAllProjectTagsCommand());
RS.registerCommand(module, "set-project-tags", new Packages.com.google.refine.commands.project.SetProjectTagsCommand());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ ExtendReconciledDataPreviewDialog.prototype._update = function() {
this._elmts.previewContainer.empty();
} else {
// otherwise, refresh the preview
$.post(
Refine.postCSRF(
"command/core/preview-extend-data?" + $.param(params),
{
rowIndices: JSON.stringify(this._rowIndices),
Expand All @@ -194,10 +194,10 @@ ExtendReconciledDataPreviewDialog.prototype._update = function() {
function(data) {
self._renderPreview(data);
},
"json"
).fail(function(data) {
alert($.i18n('core-views/internal-err'));
});
"json",
function(data) {
alert($.i18n('core-views/internal-err'));
});
}
};

Expand Down

0 comments on commit 3559eeb

Please sign in to comment.