diff --git a/extensions/database/module/langs/translation-en.json b/extensions/database/module/langs/translation-en.json index 2feb01e3c..b4ffadf48 100644 --- a/extensions/database/module/langs/translation-en.json +++ b/extensions/database/module/langs/translation-en.json @@ -44,5 +44,10 @@ "database-parsing/limit-next": "Load at most", "database-parsing/limit": "row(s) of data", "database-parsing/store-row": "Store blank rows", - "database-parsing/store-cell": "Store blank cells as nulls" + "database-parsing/store-cell": "Store blank cells as nulls", + "database-source/alert-conn-name-invalid-character": "Connection Name Input Error: Illegal Character in Input. Only [a-zA-Z0-9._-] Allowed", + "database-source/alert-db-host-invalid-character": "Database Host Error: Illegal Character in Input. Only Alphanumeric characters allowed", + "database-source/alert-db-user-invalid-character": "Database User Error: Illegal Character in Input. Only Alphanumeric characters allowed", + "database-source/alert-db-port-invalid-character": "Database Port Error: Illegal Character in Input. Only Numeric values allowed." + } diff --git a/extensions/database/module/scripts/index/database-source-ui.js b/extensions/database/module/scripts/index/database-source-ui.js index 7b5932c2e..4a4e6e234 100644 --- a/extensions/database/module/scripts/index/database-source-ui.js +++ b/extensions/database/module/scripts/index/database-source-ui.js @@ -127,11 +127,11 @@ Refine.DatabaseSourceUI.prototype.attachUI = function(body) { this._elmts.saveConnectionButton.click(function(evt) { if(self._validateNewConnectionForm() == true){ - var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); + var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); if (connectionNameInput.length === 0) { window.alert($.i18n('database-source/alert-connection-name')); } else{ - self._saveConnection(self._getConnectionInfo()); + self._saveConnection(self._getConnectionInfo()); } } @@ -148,19 +148,11 @@ Refine.DatabaseSourceUI.prototype.attachUI = function(body) { jdbcQueryInfo.databasePassword = $( "#currentDatabasePasswordInput" ).val(); jdbcQueryInfo.initialDatabase = $( "#currentInitialDatabaseInput" ).val(); jdbcQueryInfo.query = $.trim($( "#queryTextArea" ).val()); - -// if(jdbcQueryInfo.query && jdbcQueryInfo.query.length > 0 ) { -// self._executeQuery(jdbcQueryInfo); -// }else{ -// window.alert($.i18n('database-source/alert-query')); -// } - + if(self.validateQuery(jdbcQueryInfo.query)) { self._executeQuery(jdbcQueryInfo); } - - - + }); @@ -249,15 +241,15 @@ Refine.DatabaseSourceUI.prototype._editConnection = function(connectionInfo) { success: function(settings) { if(settings){ $( "#menuListUl" ).empty(); + var menuList = $('#menuListUl'); var items = []; $.each(settings.savedConnections,function(index,savedConnection){ -// items.push('' -// + '' + savedConnection.connectionName + '' -// + ' '); - - items.push('
  • ' - + '' + savedConnection.connectionName + '' - + '
  • '); + + var li = $('
  • ').appendTo(menuList); + var a = $('').appendTo(li); + $('').text(savedConnection.connectionName) + .appendTo(a); + $(' ').appendTo(a); }) $( "#menuListUl" ).append(items.join('')); @@ -275,26 +267,23 @@ Refine.DatabaseSourceUI.prototype._executeQuery = function(jdbcQueryInfo) { //remove start line var dismiss = DialogSystem.showBusy($.i18n('database-import/checking')); - //$("#executeQueryBtn").text('Please wait ...').attr('disabled','disabled'); - + $.post( "command/database/test-query", jdbcQueryInfo, function(jdbcConnectionResult) { - // $("#executeQueryBtn").text('Preview Query Result').removeAttr('disabled'); + dismiss(); self._controller.startImportingDocument(jdbcQueryInfo); }, "json" ).fail(function( jqXhr, textStatus, errorThrown ){ - //$("#executeQueryBtn").text('Preview Query Result').removeAttr('disabled'); + dismiss(); alert( textStatus + ':' + errorThrown ); }); - //remove end line - - //self._controller.startImportingDocument(jdbcQueryInfo); + } Refine.DatabaseSourceUI.prototype._saveConnection = function(jdbcConnectionInfo) { @@ -304,16 +293,11 @@ Refine.DatabaseSourceUI.prototype._saveConnection = function(jdbcConnectionInfo) jdbcConnectionInfo, function(settings) { if(settings){ - -// self._elmts.scListGroupDiv.empty(); + self._elmts.menuListUl.empty(); var items = []; $.each(settings.savedConnections,function(index,savedConnection){ - -// items.push('' -// + '' + savedConnection.connectionName + '' -// + ' '); - + items.push('
  • ' + '' + savedConnection.connectionName + '' + '
  • '); @@ -339,14 +323,10 @@ Refine.DatabaseSourceUI.prototype._loadSavedConnections = function() { if(settings){ self._elmts.menuListUl.empty(); - //self._elmts.scListGroupDiv.empty(); + var items = []; $.each(settings.savedConnections,function(index,savedConnection){ -// items.push('' -// + '' + savedConnection.connectionName + '' -// + ' '); - items.push('
  • ' + '' + savedConnection.connectionName + '' + '
  • '); @@ -354,7 +334,7 @@ Refine.DatabaseSourceUI.prototype._loadSavedConnections = function() { }) self._elmts.menuListUl.append(items.join('')); - // self._elmts.scListGroupDiv.append(items.join('')); + } }, @@ -407,12 +387,10 @@ Refine.DatabaseSourceUI.prototype._connect = function(jdbcConnectionInfo) { + jdbcConnectionInfo.databasePort + "/" + jdbcConnectionInfo.initialDatabase; - //alert("connectionParam::" + connectionParam); + $( "#connectionParameterSpan" ).text(connectionParam); - // self._body.find('.newConnectionDiv').hide(); - // self._body.find('.sqlEditorDiv').show(); - $( "#newConnectionDiv" ).hide(); - $( "#sqlEditorDiv" ).show(); + $( "#newConnectionDiv" ).hide(); + $( "#sqlEditorDiv" ).show(); }else{ window.alert("Unable to establish connection to database"); @@ -428,20 +406,21 @@ Refine.DatabaseSourceUI.prototype._connect = function(jdbcConnectionInfo) { }; Refine.DatabaseSourceUI.prototype._getConnectionInfo = function() { - var self = this; - var jdbcConnectionInfo = {}; - jdbcConnectionInfo.connectionName = $.trim(self._elmts.connectionNameInput[0].value); - jdbcConnectionInfo.databaseType = $.trim(self._elmts.databaseTypeSelect[0].value); - jdbcConnectionInfo.databaseServer = $.trim(self._elmts.databaseHostInput[0].value); - jdbcConnectionInfo.databasePort = $.trim(self._elmts.databasePortInput[0].value); - jdbcConnectionInfo.databaseUser = $.trim(self._elmts.databaseUserInput[0].value); - jdbcConnectionInfo.databasePassword = $.trim(self._elmts.databasePasswordInput[0].value); - jdbcConnectionInfo.initialDatabase = $.trim(self._elmts.initialDatabaseInput[0].value); - jdbcConnectionInfo.initialSchema = $.trim(self._elmts.initialSchemaInput[0].value); - return jdbcConnectionInfo; + var self = this; + var jdbcConnectionInfo = {}; + jdbcConnectionInfo.connectionName = $.trim(self._elmts.connectionNameInput[0].value); + jdbcConnectionInfo.databaseType = $.trim(self._elmts.databaseTypeSelect[0].value); + jdbcConnectionInfo.databaseServer = $.trim(self._elmts.databaseHostInput[0].value); + jdbcConnectionInfo.databasePort = $.trim(self._elmts.databasePortInput[0].value); + jdbcConnectionInfo.databaseUser = $.trim(self._elmts.databaseUserInput[0].value); + jdbcConnectionInfo.databasePassword = $.trim(self._elmts.databasePasswordInput[0].value); + jdbcConnectionInfo.initialDatabase = $.trim(self._elmts.initialDatabaseInput[0].value); + jdbcConnectionInfo.initialSchema = $.trim(self._elmts.initialSchemaInput[0].value); + return jdbcConnectionInfo; } + Refine.DatabaseSourceUI.prototype._validateNewConnectionForm = function() { var self = this; @@ -454,21 +433,36 @@ Refine.DatabaseSourceUI.prototype._validateNewConnectionForm = function() { var initialDatabaseInput = $.trim(self._elmts.initialDatabaseInput[0].value); var initialSchemaInput = $.trim(self._elmts.initialSchemaInput[0].value); - if (databaseHostInput.length === 0) { + var alphaNumRE = /^[a-zA-Z0-9._-]*$/; + var numRE = /^[0-9]*$/; + + var alphaNumConnNameTestResult = alphaNumRE.test(connectionNameInput); + var databaseHostTestResult = alphaNumRE.test(databaseHostInput); + var databasePortTestResult = numRE.test(databasePortInput); + var databaseUserTestResult = alphaNumRE.test(databaseUserInput); + + if(alphaNumConnNameTestResult == false){ + window.alert($.i18n('database-source/alert-conn-name-invalid-character')); + return false; + }else if (databaseHostInput.length === 0) { window.alert($.i18n('database-source/alert-server')); return false; }else if(databasePortInput.length === 0){ - window.alert($.i18n('database-source/alert-port')); - return false; + window.alert($.i18n('database-source/alert-port')); + return false; }else if(databaseUserInput.length === 0){ - window.alert($.i18n('database-source/alert-user')); - return false; + window.alert($.i18n('database-source/alert-user')); + return false; }else if(initialDatabaseInput.length === 0){ - window.alert($.i18n('database-source/alert-initial-database')); - return false; + window.alert($.i18n('database-source/alert-initial-database')); + return false; + }else if(databasePortTestResult == false){ + window.alert($.i18n('database-source/alert-db-port-invalid-character')); + return false; + } else{ - return true; + return true; } diff --git a/extensions/database/pom.xml b/extensions/database/pom.xml index 04ec35734..846ace10c 100644 --- a/extensions/database/pom.xml +++ b/extensions/database/pom.xml @@ -166,6 +166,13 @@ commons-lang3 3.6 + + + org.owasp.encoder + encoder + 1.2.2 + + diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java b/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java index 0cb37354b..a5d0a06bd 100644 --- a/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java @@ -314,7 +314,7 @@ public class DatabaseUtils { String fileSep = System.getProperty("file.separator"); String filename = dir.getPath() + fileSep + DATABASE_EXTENSION_DIR + fileSep + SETTINGS_FILE_NAME; - // logger.info("** extension file name: {} **", filename); + logger.debug("** extension file name: {} **", filename); return filename; } public static String getExtensionFolder(){ diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java index 679090de1..a30e2d000 100644 --- a/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java +++ b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java @@ -31,12 +31,15 @@ package com.google.refine.extension.database.cmd; import java.io.IOException; import java.io.Writer; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpStatus; +import org.owasp.encoder.Encode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +53,9 @@ public class SavedConnectionCommand extends DatabaseCommand { private static final Logger logger = LoggerFactory.getLogger("SavedConnectionCommand"); + private static final Pattern CONN_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9._-]*"); + private static final Pattern DATABASE_PORT_PATTERN = Pattern.compile("^[0-9]*"); + @Override public void doGet(HttpServletRequest request, HttpServletResponse response) @@ -126,26 +132,24 @@ public class SavedConnectionCommand extends DatabaseCommand { writer.writeArrayFieldStart(DatabaseUtils.SAVED_CONNECTION_KEY); writer.writeStartObject(); - writer.writeStringField("connectionName", savedConnection.getConnectionName()); + + writer.writeStringField("connectionName", savedConnection.getConnectionName()); writer.writeStringField("databaseType", savedConnection.getDatabaseType()); - writer.writeStringField("databaseHost", savedConnection.getDatabaseHost()); - writer.writeNumberField("databasePort", savedConnection.getDatabasePort()); - writer.writeStringField("databaseName", savedConnection.getDatabaseName()); - + + String dbPasswd = savedConnection.getDatabasePassword(); if(dbPasswd != null && !dbPasswd.isEmpty()) { dbPasswd = DatabaseUtils.decrypt(savedConnection.getDatabasePassword()); } writer.writeStringField("databasePassword", dbPasswd); - - writer.writeStringField("databaseSchema", savedConnection.getDatabaseSchema()); - + writer.writeStringField("databaseSchema", savedConnection.getDatabaseSchema()); writer.writeStringField("databaseUser", savedConnection.getDatabaseUser()); + writer.writeEndObject(); writer.writeEndArray(); @@ -228,6 +232,7 @@ public class SavedConnectionCommand extends DatabaseCommand { if(logger.isDebugEnabled()) { logger.debug("doPost Connection: {}", request.getParameter("connectionName")); } + DatabaseConfiguration jdbcConfig = getJdbcConfiguration(request); @@ -240,6 +245,21 @@ public class SavedConnectionCommand extends DatabaseCommand { response.flushBuffer(); return; } + + if(!validateInput(jdbcConfig.getConnectionName(), CONN_NAME_PATTERN)) { + logger.warn("Invalid Connection Name: {}", jdbcConfig.getConnectionName()); + response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection Name is Invalid. Expecting [a-zA-Z0-9._-]"); + response.flushBuffer(); + return; + } + + if(!validateInput("" + jdbcConfig.getDatabasePort(), DATABASE_PORT_PATTERN)) { + logger.warn("Invalid Database Port: {}", jdbcConfig.getDatabasePort()); + response.sendError(HttpStatus.SC_BAD_REQUEST, "Database Port Invalid. Expecting Numeric values only"); + response.flushBuffer(); + return; + } + DatabaseConfiguration savedConn = DatabaseUtils.getSavedConnection(jdbcConfig.getConnectionName()); if(savedConn != null) { @@ -266,8 +286,14 @@ public class SavedConnectionCommand extends DatabaseCommand { } } - - + + private boolean validateInput(String input, Pattern pattern){ + Matcher matcher = pattern.matcher(input); + if(matcher.matches()){ + return true; + } + return false; + } @Override public void doPut(HttpServletRequest request, HttpServletResponse response) diff --git a/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java b/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java index d8a825100..32ea6fb27 100644 --- a/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java +++ b/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java @@ -1,6 +1,10 @@ package com.google.refine.extension.database.cmd; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; import java.io.File; import java.io.IOException; @@ -11,6 +15,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.http.HttpStatus; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.Assert; @@ -154,8 +159,11 @@ public class SavedConnectionCommandTest extends DBExtensionTests{ SUT.doPost(request, response); String result = sw.getBuffer().toString().trim(); - + assertNotNull(result); + assertFalse(result.isEmpty(), "Valid response Message expected!"); + ObjectNode json = ParsingUtilities.mapper.readValue(result, ObjectNode.class); + // System.out.println("json:" + json); ArrayNode savedConnections = (ArrayNode) json.get("savedConnections"); Assert.assertNotNull(savedConnections); @@ -229,7 +237,6 @@ public class SavedConnectionCommandTest extends DBExtensionTests{ Assert.assertEquals(savedConnections.size(), 1); ObjectNode sc = (ObjectNode)savedConnections.get(0); - System.out.println("sc" + sc); String newDbHost = sc.get("databaseHost").asText(); Assert.assertEquals(newDbHost, newHost); } @@ -276,8 +283,6 @@ public class SavedConnectionCommandTest extends DBExtensionTests{ SUT.doDelete(request, response); - // String result = sw.getBuffer().toString().trim(); - ObjectNode json = ParsingUtilities.mapper.createObjectNode(); Assert.assertNotNull(json); @@ -288,5 +293,34 @@ public class SavedConnectionCommandTest extends DBExtensionTests{ e.printStackTrace(); } } + + /** + * Added to check XSS invalid tokens + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoPostInvalidConnectionName() throws IOException, ServletException { + + when(request.getParameter("connectionName")).thenReturn(""); + when(request.getParameter("databaseType")).thenReturn(MySQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + + SUT.doPost(request, response); + + verify(response, times(1)).sendError(HttpStatus.SC_BAD_REQUEST, "Connection Name is Invalid. Expecting [a-zA-Z0-9._-]"); + } + + + }