You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by jm...@apache.org on 2022/08/29 17:32:06 UTC
[geode] branch support/1.12 updated: GEODE-10411: fix XSS vulnerability in pulse (#7836)
This is an automated email from the ASF dual-hosted git repository.
jmelchior pushed a commit to branch support/1.12
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/support/1.12 by this push:
new f9e65fd726 GEODE-10411: fix XSS vulnerability in pulse (#7836)
f9e65fd726 is described below
commit f9e65fd726682fbabd7360371cd55aa6bd215c6b
Author: Joris Melchior <jo...@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 8aa78d70f4..66250eabed 100644
--- a/geode-pulse/src/main/webapp/WEB-INF/web.xml
+++ b/geode-pulse/src/main/webapp/WEB-INF/web.xml
@@ -51,6 +51,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>");
+ });
+ }
}