This is an automated email from the ASF dual-hosted git repository. jmelchior pushed a commit to branch support/1.13 in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/support/1.13 by this push: new b1acc746ff GEODE-10411: fix XSS vulnerability in pulse (#7836) b1acc746ff is described below commit b1acc746fffd7ae1cb6c575e4ad1c06d64350396 Author: Joris Melchior <joris.melch...@gmail.com> AuthorDate: Fri Aug 26 13:26:22 2022 -0400 GEODE-10411: fix XSS vulnerability in pulse (#7836) * GEODE-10411: fix XSS vulnerability in pulse - html encode data coming from Geode queries - add cookie parameters to increase browsing security * Fix spotless check errors --- .../tools/pulse/tests/DataBrowserResultLoader.java | 77 ++++++----- .../geode/tools/pulse/tests/PulseTestData.java | 5 +- .../testQueryResultClusterSmallJSInject.txt | 23 ++++ geode-pulse/src/main/webapp/META-INF/context.xml | 26 ++++ geode-pulse/src/main/webapp/WEB-INF/web.xml | 8 ++ .../scripts/pulsescript/pages/DataBrowserQuery.js | 149 ++++++++++----------- .../pulsescript/pages/DataBrowserQueryHistory.js | 28 ++-- .../tools/pulse/tests/ui/PulseAutomatedTest.java | 65 +++++++-- 8 files changed, 242 insertions(+), 139 deletions(-) diff --git a/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/DataBrowserResultLoader.java b/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/DataBrowserResultLoader.java index 6c50fbc996..392f0c9c48 100644 --- a/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/DataBrowserResultLoader.java +++ b/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/DataBrowserResultLoader.java @@ -17,12 +17,11 @@ package org.apache.geode.tools.pulse.tests; import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.stream.Collectors; public class DataBrowserResultLoader { /* Constants for executing Data Browser queries */ @@ -32,9 +31,10 @@ public class DataBrowserResultLoader { public static final String QUERY_TYPE_FOUR = "query4"; public static final String QUERY_TYPE_FIVE = "query5"; public static final String QUERY_TYPE_SIX = "query6"; - public static final String QUERY_TYPE_SEVENE = "query7"; + public static final String QUERY_TYPE_SEVEN = "query7"; + public static final String QUERY_TYPE_EIGHT = "query8"; - private static DataBrowserResultLoader dbResultLoader = new DataBrowserResultLoader(); + private static final DataBrowserResultLoader dbResultLoader = new DataBrowserResultLoader(); public static DataBrowserResultLoader getInstance() { return dbResultLoader; @@ -42,41 +42,46 @@ public class DataBrowserResultLoader { public String load(String queryString) throws IOException { - URL url = null; - InputStream inputStream = null; - BufferedReader streamReader = null; - String inputStr = null; - StringBuilder sampleQueryResultResponseStrBuilder = null; + String fileName; + String fileContent = ""; try { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if (queryString.equals(QUERY_TYPE_ONE)) { - url = classLoader.getResource("testQueryResultClusterSmall.txt"); - } else if (queryString.equals(QUERY_TYPE_TWO)) { - url = classLoader.getResource("testQueryResultSmall.txt"); - } else if (queryString.equals(QUERY_TYPE_THREE)) { - url = classLoader.getResource("testQueryResult.txt"); - } else if (queryString.equals(QUERY_TYPE_FOUR)) { - url = classLoader.getResource("testQueryResultWithStructSmall.txt"); - } else if (queryString.equals(QUERY_TYPE_FIVE)) { - url = classLoader.getResource("testQueryResultClusterWithStruct.txt"); - } else if (queryString.equals(QUERY_TYPE_SIX)) { - url = classLoader.getResource("testQueryResultHashMapSmall.txt"); - } else if (queryString.equals(QUERY_TYPE_SEVENE)) { - url = classLoader.getResource("testQueryResult1000.txt"); - } else { - url = classLoader.getResource("testQueryResult.txt"); + switch (queryString) { + case QUERY_TYPE_ONE: + fileName = "testQueryResultClusterSmall.txt"; + break; + case QUERY_TYPE_TWO: + fileName = "testQueryResultSmall.txt"; + break; + case QUERY_TYPE_THREE: + fileName = "testQueryResult.txt"; + break; + case QUERY_TYPE_FOUR: + fileName = "testQueryResultWithStructSmall.txt"; + break; + case QUERY_TYPE_FIVE: + fileName = "testQueryResultClusterWithStruct.txt"; + break; + case QUERY_TYPE_SIX: + fileName = "testQueryResultHashMapSmall.txt"; + break; + case QUERY_TYPE_SEVEN: + fileName = "testQueryResult1000.txt"; + break; + case QUERY_TYPE_EIGHT: + fileName = "testQueryResultClusterSmallJSInject.txt"; + break; + default: + fileName = "testQueryResult.txt"; + break; } - File sampleQueryResultFile = new File(url.getPath()); - inputStream = new FileInputStream(sampleQueryResultFile); - streamReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); - sampleQueryResultResponseStrBuilder = new StringBuilder(); - - while ((inputStr = streamReader.readLine()) != null) { - sampleQueryResultResponseStrBuilder.append(inputStr); - } + InputStream inputStream = getClass().getResourceAsStream("/" + fileName); + assert inputStream != null; + BufferedReader streamReader = + new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + fileContent = streamReader.lines().collect(Collectors.joining(System.lineSeparator())); // close stream reader streamReader.close(); @@ -85,6 +90,6 @@ public class DataBrowserResultLoader { ex.printStackTrace(); } - return sampleQueryResultResponseStrBuilder.toString(); + return fileContent; } } diff --git a/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/PulseTestData.java b/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/PulseTestData.java index 338f4dedad..bc03417f4b 100644 --- a/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/PulseTestData.java +++ b/geode-pulse/geode-pulse-test/src/main/java/org/apache/geode/tools/pulse/tests/PulseTestData.java @@ -92,6 +92,9 @@ public class PulseTestData { public static final String partialRgnName = "R"; public static final String chkRgnClassName = "bttn chk checkbox_true_full"; public static final String notChkRgnClassName = "bttn chk checkbox_false_full"; + public static final String resultClusterHeadingsXPath = "//div[@id='clusterDetails']/div/div"; + public static final String resultClusterCellXPath = + "//tr/td[contains(@title, '<script>alert')]"; public static final String regName = "R1"; public static final String query1Text = "select * from /R1"; @@ -99,6 +102,4 @@ public class PulseTestData { public static final String datePattern = "EEE, MMM dd yyyy, HH:mm:ss z"; } - - } diff --git a/geode-pulse/geode-pulse-test/src/main/resources/testQueryResultClusterSmallJSInject.txt b/geode-pulse/geode-pulse-test/src/main/resources/testQueryResultClusterSmallJSInject.txt new file mode 100644 index 0000000000..25a0a2cdeb --- /dev/null +++ b/geode-pulse/geode-pulse-test/src/main/resources/testQueryResultClusterSmallJSInject.txt @@ -0,0 +1,23 @@ +{"result":[ + ["org.apache.geode.cache.query.data.PortfolioDummy", + {"type":["java.lang.String","type0"],"ID":["int",0],"active":["boolean",true],"pk":["java.lang.String","0"],"collectionHolderMapDummy":["java.util.HashMap",{"3":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"2":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"1":["org.apache.geode.cache.query.data. [...] + + ["org.apache.geode.cache.query.data.Portfolio", + {"type":["java.lang.String","type0"],"ID":["int",0],"active":["boolean",true],"pk":["java.lang.String","0"],"collectionHolderMap":["java.util.HashMap",{"3":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"2":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"1":["org.apache.geode.cache.query.data.Colle [...] + + ["org.apache.geode.cache.query.data.Portfolio", + {"type":["java.lang.String","type1"],"ID":["int",1],"active":["boolean",false],"pk":["java.lang.String","1"],"collectionHolderMap":["java.util.HashMap",{"3":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"2":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"1":["org.apache.geode.cache.query.data.Coll [...] + + ["org.apache.geode.cache.query.data.Portfolio", + {"type":["java.lang.String","type2"],"ID":["int",2],"active":["boolean",true],"pk":["java.lang.String","2"],"collectionHolderMap":["java.util.HashMap",{"3":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"2":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"1":["org.apache.geode.cache.query.data.Colle [...] + + ["org.apache.geode.cache.query.data.Portfolio", + {"type":["java.lang.String","type0"],"ID":["int",3],"active":["boolean",false],"pk":["java.lang.String","3"],"collectionHolderMap":["java.util.HashMap",{"3":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"2":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"1":["org.apache.geode.cache.query.data.Coll [...] + + ["org.apache.geode.cache.query.data.PortfolioDummy", + {"type":["java.lang.String","type1"],"ID":["int",4],"active":["boolean",true],"pk":["java.lang.String","4"],"collectionHolderMap":["java.util.HashMap",{"3":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"2":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"1":["org.apache.geode.cache.query.data.Colle [...] + + ["org.apache.geode.cache.query.data.Portfolio", + {"type":["java.lang.String","<script>alert('xss')</script>"],"ID":["int",5],"active":["boolean",false],"pk":["java.lang.String","5"],"collectionHolderMap":["java.util.HashMap",{"3":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"2":["org.apache.geode.cache.query.data.CollectionHolder",{"arr":["java.lang.String[]",["0","1","2","3","4","SUN","IBM","YHOO","GOOG","MSFT"]]}],"1":["org.apache.geo [...] + ] +} \ No newline at end of file diff --git a/geode-pulse/src/main/webapp/META-INF/context.xml b/geode-pulse/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000000..e70eb6ceca --- /dev/null +++ b/geode-pulse/src/main/webapp/META-INF/context.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<Context> + + <!-- Add SameSite to the cookies for Tomcat --> + <CookieProcessor + sameSiteCookies="Strict" /> + +</Context> \ No newline at end of file diff --git a/geode-pulse/src/main/webapp/WEB-INF/web.xml b/geode-pulse/src/main/webapp/WEB-INF/web.xml index 5da192888e..e16a8dd389 100644 --- a/geode-pulse/src/main/webapp/WEB-INF/web.xml +++ b/geode-pulse/src/main/webapp/WEB-INF/web.xml @@ -43,6 +43,14 @@ <param-name>spring.profiles.default</param-name> <param-value>pulse.authentication.default</param-value> </context-param> + + <session-config> + <cookie-config> + <http-only>true</http-only> + <comment>__SAME_SITE_STRICT__</comment> + </cookie-config> + </session-config> + <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> diff --git a/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQuery.js b/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQuery.js index 2a59382892..e5056bacb8 100644 --- a/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQuery.js +++ b/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQuery.js @@ -68,10 +68,10 @@ function executeDBQuery(){ } // Determine selected members query to be execute on - if($("#membersList").html() != ""){ + if($("#membersList").html() !== ""){ var selectedMembers = $( "input[type=checkbox][name=Member]:checked" ); for(var i=0; i< selectedMembers.length; i++){ - if(selectedMemberNames == ""){ + if(selectedMemberNames === ""){ selectedMemberNames = selectedMembers[i].value; }else{ selectedMemberNames += ","+selectedMembers[i].value; @@ -169,7 +169,7 @@ function executeDBQuery(){ }).error(resErrHandler); - return; + } // This function displays error if occurred @@ -181,7 +181,7 @@ function resErrHandler(data){ }else{ console.log(data); } -}; +} // This function creates complete result panel html function createHtmlForQueryResults(){ @@ -458,8 +458,8 @@ function createResultGrid(member, memberResultObject){ }*/ // Determine table columns - var columnName = new Array(); - var columnModel = new Array(); + var columnName = []; + var columnModel = []; for(var cnt=0; cnt<objectResults.length; cnt++){ for(key in objectResults[cnt]){ if(-1 == columnName.indexOf(key)){ @@ -803,14 +803,14 @@ function formDataForPopUpGrid(data){ //Function for converting raw response into expected format function convertRawResponseToExpectedFormat(rawResponeData){ - if(rawResponeData == null || rawResponeData == undefined){ + if(rawResponeData === null || rawResponeData === undefined){ return; } var finalResponseData = {}; - var finalResponseResults = new Array(); + var finalResponseResults = []; - if(rawResponeData.result != null || rawResponeData.result != undefined){ + if(rawResponeData.result != null || rawResponeData.result !== undefined){ var rawResponeDataResult = rawResponeData.result; for(var i=0; i<rawResponeDataResult.length; i++){ @@ -821,7 +821,7 @@ function convertRawResponseToExpectedFormat(rawResponeData){ finalResponseResults = convertToExpectedObjectsFormat(rawResponeDataResult, ""); break; - }else if(rawResponeDataResult[i].member != null && rawResponeDataResult[i].member != undefined){ + }else if(rawResponeDataResult[i].member != null && rawResponeDataResult[i].member !== undefined){ var responseForMember = {}; responseForMember.member = rawResponeDataResult[i].member[0]; @@ -842,31 +842,25 @@ function convertRawResponseToExpectedFormat(rawResponeData){ // Function for converting raw response into expected object wise results format function convertToExpectedObjectsFormat(rawResponseResult, prefixForId){ - - var expResponseResult = new Array(); - - if(rawResponseResult != null && rawResponseResult != undefined ){ + + let entry; + let objectResults; + const expResponseResult = []; + + if(rawResponseResult != null ){ - for(var i=0; i< rawResponseResult.length; i++){ + for(let i=0; i < rawResponseResult.length; i++){ if(rawResponseResult[i] != null){ if(expResponseResult.length > 0){ // search expected object type in expResponseResult - var flagObjectFound = false; - for(var j=0 ; j < expResponseResult.length ; j++){ - if(expResponseResult[j].objectType == rawResponseResult[i][0]){ + let flagObjectFound = false; + for(let j=0 ; j < expResponseResult.length ; j++){ + if(expResponseResult[j].objectType === rawResponseResult[i][0]){ // required object found flagObjectFound = true; - var objectResults = expResponseResult[j].objectResults; - var type = rawResponseResult[i][0]; - var entry = rawResponseResult[i][1]; - - // if entry is not object then convert it into object - if(typeof(entry) != "object" ){ - var entryObj = {}; - entryObj[type] = rawResponseResult[i][1]; - entry = entryObj; - } + objectResults = expResponseResult[j].objectResults; + entry = htmlEncodeEntry(rawResponseResult[i]); // add unique id for new entry entry.uid = generateEntryUID(prefixForId, expResponseResult[j].objectType, objectResults.length); @@ -875,57 +869,12 @@ function convertToExpectedObjectsFormat(rawResponseResult, prefixForId){ break; } } - - if(!flagObjectFound){ // required object not found in expResponseResult - - var objectResults = new Array(); - var type = rawResponseResult[i][0]; - var entry = rawResponseResult[i][1]; - - // if entry is not object then convert it into object - if(typeof(entry) != "object" ){ - var entryObj = {}; - entryObj[type] = rawResponseResult[i][1]; - entry = entryObj; - } - - // add unique id for new entry - entry.uid = generateEntryUID(prefixForId, type, objectResults.length); - - objectResults.push(entry); - - var newResultObject = {}; - newResultObject.objectType = type; - newResultObject.objectResults = objectResults; - - expResponseResult.push(newResultObject); + if(!flagObjectFound){ // required object not found in expResponseResult + expResponseResult.push(addToExpResponseResult(rawResponseResult[i], prefixForId)); } - }else{ // expResponseResult is empty - - var objectResults = new Array(); - var type = rawResponseResult[i][0]; - var entry = rawResponseResult[i][1]; - - // if entry is not object then convert it into object - if(typeof(entry) != "object" ){ - var entryObj = {}; - entryObj[type] = rawResponseResult[i][1]; - entry = entryObj; - } - - // add unique id for new entry - entry.uid = generateEntryUID(prefixForId, type, objectResults.length); - - objectResults.push(entry); - - var newResultObject = {}; - newResultObject.objectType = type; - newResultObject.objectResults = objectResults; - - expResponseResult.push(newResultObject); + expResponseResult.push(addToExpResponseResult(rawResponseResult[i], prefixForId)); } - } } } @@ -933,6 +882,54 @@ function convertToExpectedObjectsFormat(rawResponseResult, prefixForId){ return expResponseResult; } +// Add results to the expected responseResults +function addToExpResponseResult(rawResponseResultEntry, prefixForId) { + let objectResults = []; + let type = rawResponseResultEntry[0]; + let entry = htmlEncodeEntry(rawResponseResultEntry, prefixForId); + + // add unique id for new entry + entry.uid = generateEntryUID(prefixForId, type, objectResults.length); + + objectResults.push(entry); + + let newResultObject = {}; + newResultObject.objectType = type; + newResultObject.objectResults = objectResults; + + return newResultObject; +} + +// Ensure that strings are HTML encoded to reduce likelihood of XSS attacks +function htmlEncodeEntry(rawResponseResultEntry, prefixForId) { + let type = htmlEncodeStringsAndObjects(rawResponseResultEntry[0]); + let entry = rawResponseResultEntry[1]; + + let entryObj = {}; + + // if entry is not object then convert it into object + if(typeof(entry) == "object" ){ + entryObj = htmlEncodeStringsAndObjects(entry); + } else { + entryObj[type] = htmlEncodeStringsAndObjects(entry) + } + + return entryObj; +} + +function htmlEncodeStringsAndObjects(raw) { + switch(typeof(raw)) { + case "string": + return $('<pre/>').text(raw).html(); + case "object": + let objectAsString = JSON.stringify(raw); + objectAsString = $('<pre/>').text(objectAsString).html(); + return JSON.parse(objectAsString); + default: + return raw + } +} + // Function to generate unique idetifier for entry function generateEntryUID(prefixForId, type, len) { diff --git a/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQueryHistory.js b/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQueryHistory.js index 1c6000ba8c..c41381644e 100644 --- a/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQueryHistory.js +++ b/geode-pulse/src/main/webapp/scripts/pulsescript/pages/DataBrowserQueryHistory.js @@ -21,25 +21,25 @@ // updateQueryHistory() function updateQueryHistory(action,queryId) { - requestData = { - action:action, - queryId:queryId + let requestData = { + action: action, + queryId: queryId }; $.getJSON("dataBrowserQueryHistory", requestData, function(data) { - - var queries = new Array(); - if(data.queryHistory != undefined && data.queryHistory != null){ + + let queries = []; + if(data.queryHistory !== undefined && data.queryHistory != null){ queries = data.queryHistory; } - var refHistoryConatiner = $("#detailsHistoryList"); - var queryListHTML = ""; - if(queries.length == 0){ + const refHistoryContainer = $("#detailsHistoryList"); + let queryListHTML = ""; + if(queries.length === 0){ // no queries found queryListHTML = "No Query Found"; }else{ queries.sort(dynamicSort("queryId", "desc")); - for(var i=0; i<queries.length && i<20; i++){ + for(let i=0; i < queries.length && i < 20; i++){ // add query item queryListHTML += "" + "<div class=\"container\">" + @@ -50,7 +50,7 @@ function updateQueryHistory(action,queryId) { "<div class=\"remove\">" + "<a href=\"#\" onclick=\"updateQueryHistory('delete','"+ queries[i].queryId +"');\"> </a>" + "</div>" + - "<div class=\"wrapHistoryContent\" ondblclick=\"queryHistoryItemClicked(this);\">" + queries[i].queryText + + "<div class=\"wrapHistoryContent\" ondblclick=\"queryHistoryItemClicked(this);\">" + queries[i].queryText.replaceAll("\"", "") + "</div>" + "<div class=\"dateTimeHistory\">" + queries[i].queryDateTime + "</div>" + @@ -59,7 +59,7 @@ function updateQueryHistory(action,queryId) { } } - refHistoryConatiner.html(queryListHTML); + refHistoryContainer.html(queryListHTML); //$('.queryHistoryScroll-pane').jScrollPane();/*Custome scroll*/ // Set eventsAdded = false as list is refreshed and slide events @@ -73,13 +73,13 @@ function updateQueryHistory(action,queryId) { // This function displays error if occurred function resErrHandler(data){ // Check for unauthorized access - if (data.status == 401) { + if (data.status === 401) { // redirect user on Login Page window.location.href = "login.html?error=UNAUTH_ACCESS"; }else{ console.log(data); } -}; +} // This function is called when any query from history list is double clicked function queryHistoryItemClicked(divElement){ diff --git a/geode-pulse/src/uiTest/java/org/apache/geode/tools/pulse/tests/ui/PulseAutomatedTest.java b/geode-pulse/src/uiTest/java/org/apache/geode/tools/pulse/tests/ui/PulseAutomatedTest.java index 7732e6d0fa..fdf0dac0de 100644 --- a/geode-pulse/src/uiTest/java/org/apache/geode/tools/pulse/tests/ui/PulseAutomatedTest.java +++ b/geode-pulse/src/uiTest/java/org/apache/geode/tools/pulse/tests/ui/PulseAutomatedTest.java @@ -13,12 +13,6 @@ * the License. * */ -/** - * This test class contains automated tests for Pulse application related to 1. Different grid data - * validations for example - Topology, Server Group, Redundancy Zone 2. Data Browser 3. - * - * @since GemFire 2014-04-02 - */ package org.apache.geode.tools.pulse.tests.ui; import static org.apache.geode.tools.pulse.tests.ui.PulseTestUtils.assertMemberSortingByCpuUsage; @@ -59,13 +53,16 @@ import static org.apache.geode.tools.pulse.tests.ui.PulseTestUtils.verifyElement import static org.apache.geode.tools.pulse.tests.ui.PulseTestUtils.verifyTextPresrntById; import static org.apache.geode.tools.pulse.tests.ui.PulseTestUtils.verifyTextPresrntByXpath; import static org.apache.geode.tools.pulse.tests.ui.PulseTestUtils.waitForElementWithId; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import org.junit.Assert; import org.junit.Before; @@ -84,6 +81,12 @@ import org.apache.geode.tools.pulse.tests.rules.ScreenshotOnFailureRule; import org.apache.geode.tools.pulse.tests.rules.ServerRule; import org.apache.geode.tools.pulse.tests.rules.WebDriverRule; +/** + * This test class contains automated tests for Pulse application related to 1. Different grid data + * validations for example - Topology, Server Group, Redundancy Zone 2. Data Browser 3. + * + * @since GemFire 2014-04-02 + */ public class PulseAutomatedTest extends PulseBase { @ClassRule @@ -851,7 +854,7 @@ public class PulseAutomatedTest extends PulseBase { clickElementUsingXpath(PulseTestLocators.DataBrowser.btnClearXpath); String editorTextAfterClear = getTextUsingId(PulseTestLocators.DataBrowser.queryEditorTxtBoxId); - assertFalse(PulseTestData.DataBrowser.query1Text.equals(editorTextAfterClear)); + assertNotEquals(PulseTestData.DataBrowser.query1Text, editorTextAfterClear); } @Ignore("WIP") // Data Browser's Query History not showing any data on button click, therefore @@ -892,10 +895,50 @@ public class PulseAutomatedTest extends PulseBase { System.out.println("Query Text from History Table: " + queryText); System.out.println("Query Time from History Table: " + historyDateTime); // verify the query text, query datetime in history panel - assertTrue(DataBrowserResultLoader.QUERY_TYPE_ONE.equals(queryText)); - assertTrue(historyDateTime.contains(queryTime[0])); - + assertThat(queryText).isEqualTo(DataBrowserResultLoader.QUERY_TYPE_ONE); + assertThat(historyDateTime).contains(queryTime[0]); } + @Test + public void testDataBrowserHTMLEncode() { + // navigate to Data browser page + loadDataBrowserpage(); + + WebDriver driver = webDriverRule.getDriver(); + List<WebElement> numOfReg = driver + .findElements(By.xpath(PulseTestLocators.DataBrowser.divDataRegions)); + for (int i = 1; i <= numOfReg.size(); i++) { + if (getTextUsingId("treeDemo_" + i + "_span").equals(PulseTestData.DataBrowser.regName)) { + searchByIdAndClick("treeDemo_" + i + "_check"); // driver.findElement(By.id("treeDemo_" + i + // + "_check")).click(); + } + } + + sendKeysUsingId(PulseTestLocators.DataBrowser.queryEditorTxtBoxId, + DataBrowserResultLoader.QUERY_TYPE_EIGHT); + clickElementUsingId(PulseTestLocators.DataBrowser.btnExecuteQueryId); + + clickElementUsingId(PulseTestLocators.DataBrowser.historyIcon); + String queryText = findElementByXpath(PulseTestLocators.DataBrowser.historyLst) + .findElement(By.cssSelector(PulseTestLocators.DataBrowser.queryText)).getText(); + + assertThat(queryText).isEqualTo(DataBrowserResultLoader.QUERY_TYPE_EIGHT); + + List<WebElement> elements = + driver.findElements(By.xpath(PulseTestData.DataBrowser.resultClusterHeadingsXPath)); + List<WebElement> filteredElements = elements.stream().filter(webElement -> webElement.getText() + .equals("org.apache.geode.cache.query.data.Portfolio")).collect( + Collectors.toList()); + List<WebElement> finalElements = filteredElements.stream().map(webElement -> { + webElement.click(); + return webElement.findElements(By.xpath(PulseTestData.DataBrowser.resultClusterCellXPath)); + }).flatMap(Collection::stream).collect(Collectors.toList()); + + // confirm script text is displayed + assertThat(finalElements).hasSize(2); + finalElements.forEach(webElement -> { + assertThat(webElement.getAttribute("title")).isEqualTo("<script>alert('xss')</script>"); + }); + } }