Merge pull request #2151 from tcbuzor/tcbuzor_orefine_xss_tr

Database Extension: input validation for database connection data
This commit is contained in:
Antonin Delpeuch 2019-09-27 10:37:26 +02:00 committed by GitHub
commit 1a79176942
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 79 deletions

View File

@ -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."
}

View File

@ -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());
}
}
@ -149,19 +149,11 @@ Refine.DatabaseSourceUI.prototype.attachUI = function(body) {
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('<a href="#" class="list-group-item list-group-item-action">'
// + '<span class="context-menu-one context-menu-text" >' + savedConnection.connectionName + '</span>'
// + '<span class="sc-context-more-vert pull-right"> </span> </a>');
items.push('<li class="pure-menu-item sc-list"><a href="#" class="pure-menu-link context-menu-one">'
+ '<span class="context-menu-text" >' + savedConnection.connectionName + '</span>'
+ '<span class="sc-context-more-vert pull-right"> </span></a></li>');
var li = $('<li class="pure-menu-item sc-list"></li>').appendTo(menuList);
var a = $('<a href="#" class="pure-menu-link context-menu-one"></a>').appendTo(li);
$('<span class="context-menu-text"></span>').text(savedConnection.connectionName)
.appendTo(a);
$('<span class="sc-context-more-vert pull-right"> </span>').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) {
@ -305,15 +294,10 @@ Refine.DatabaseSourceUI.prototype._saveConnection = function(jdbcConnectionInfo)
function(settings) {
if(settings){
// self._elmts.scListGroupDiv.empty();
self._elmts.menuListUl.empty();
var items = [];
$.each(settings.savedConnections,function(index,savedConnection){
// items.push('<a href="#" class="list-group-item list-group-item-action">'
// + '<span class="context-menu-one context-menu-text" >' + savedConnection.connectionName + '</span>'
// + '<span class="sc-context-more-vert pull-right"> </span> </a>');
items.push('<li class="pure-menu-item sc-list"><a href="#" class="pure-menu-link context-menu-one">'
+ '<span class="context-menu-text" >' + savedConnection.connectionName + '</span>'
+ '<span class="sc-context-more-vert pull-right"> </span></a></li>');
@ -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('<a href="#" class="list-group-item list-group-item-action context-menu-one">'
// + '<span class="context-menu-text" >' + savedConnection.connectionName + '</span>'
// + '<span class="sc-context-more-vert pull-right"> </span> </a>');
items.push('<li class="pure-menu-item sc-list"><a href="#" class="pure-menu-link context-menu-one">'
+ '<span class="context-menu-text" >' + savedConnection.connectionName + '</span>'
+ '<span class="sc-context-more-vert pull-right"> </span></a></li>');
@ -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;
}

View File

@ -167,6 +167,13 @@
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>

View File

@ -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(){

View File

@ -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("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("databaseUser", savedConnection.getDatabaseUser());
writer.writeEndObject();
writer.writeEndArray();
@ -229,6 +233,7 @@ public class SavedConnectionCommand extends DatabaseCommand {
logger.debug("doPost Connection: {}", request.getParameter("connectionName"));
}
DatabaseConfiguration jdbcConfig = getJdbcConfiguration(request);
@ -241,6 +246,21 @@ public class SavedConnectionCommand extends DatabaseCommand {
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) {
response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection with name " + jdbcConfig.getConnectionName() + " already exists!");
@ -267,7 +287,13 @@ 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)

View File

@ -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);
@ -289,4 +294,33 @@ public class SavedConnectionCommandTest extends DBExtensionTests{
}
}
/**
* Added to check XSS invalid tokens
* @throws IOException
* @throws ServletException
*/
@Test
public void testDoPostInvalidConnectionName() throws IOException, ServletException {
when(request.getParameter("connectionName")).thenReturn("<img></img>");
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._-]");
}
}