From 213812355860e3732e1b28e620df31db8ff160aa Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Mon, 15 Mar 2021 20:53:01 +0530
Subject: 569855: Fix for Eclipse live help. - Use tokens - Backport to
 R4_15_maintenance branch

Index: eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.base/src/org/eclipse/help/internal/base/BaseHelpSystem.java
===================================================================
--- eclipse-platform-sources-I20180906-0745.orig/eclipse.platform.ua/org.eclipse.help.base/src/org/eclipse/help/internal/base/BaseHelpSystem.java
+++ eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.base/src/org/eclipse/help/internal/base/BaseHelpSystem.java
@@ -59,6 +59,7 @@ public final class BaseHelpSystem {
 	private IBrowser browser;
 	private IBrowser internalBrowser;
 	private HelpDisplay helpDisplay = null;
+	private String liveHelpToken = null;
 
 	private BaseHelpSystem() {
 		super();
@@ -350,4 +351,29 @@ public final class BaseHelpSystem {
 		}
 	}
 
+	/**
+ 	 * Check supplied token against stored token. Clears the stored token if
+ 	 * successful.
+ 	 *
+ 	 * @param helpSessionToken
+ 	 * @return true if match successful
+ 	 */
+ 	public boolean matchOnceLiveHelpToken(String helpSessionToken) {
+ 		/*
+ 		 * @FIXME - should we use a constant time comparison, and store/compare a
+ 		 * cryptographic hash?
+ 		 */
+ 		if (liveHelpToken != null && liveHelpToken.equals(helpSessionToken)) {
+ 			// Enforce one-time use.
+ 			liveHelpToken = null;
+ 			return true;
+ 		} else {
+ 			return false;
+ 		}
+ 	}
+
+ 	public void setLiveHelpToken(String helpSessionToken) {
+ 		liveHelpToken = helpSessionToken;
+ 	}
+
 }
Index: eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.base/src/org/eclipse/help/internal/base/HelpDisplay.java
===================================================================
--- eclipse-platform-sources-I20180906-0745.orig/eclipse.platform.ua/org.eclipse.help.base/src/org/eclipse/help/internal/base/HelpDisplay.java
+++ eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.base/src/org/eclipse/help/internal/base/HelpDisplay.java
@@ -15,6 +15,8 @@ package org.eclipse.help.internal.base;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
 
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
@@ -196,6 +198,12 @@ public class HelpDisplay {
 				String topic = helpURL.substring("topic=".length()); //$NON-NLS-1$
 				helpURL = getHelpDisplay().getHelpForTopic( topic, WebappManager.getHost(),  WebappManager.getPort());
 			}
+			String basehelp = getBaseURL();
+			if (BaseHelpSystem.getMode() != BaseHelpSystem.MODE_INFOCENTER && helpURL.startsWith(basehelp)) {
+				String sessid = UUID.randomUUID().toString();
+				BaseHelpSystem.getInstance().setLiveHelpToken(sessid);
+				helpURL += (helpURL.indexOf('?') < 0 ? '?' : '&') + "token=" + sessid; //$NON-NLS-1$
+			}
 
 			BaseHelpSystem.getHelpBrowser(forceExternal)
 						.displayURL(helpURL);
Index: eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/advanced/livehelp_js.jsp
===================================================================
--- eclipse-platform-sources-I20180906-0745.orig/eclipse.platform.ua/org.eclipse.help.webapp/advanced/livehelp_js.jsp
+++ eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/advanced/livehelp_js.jsp
@@ -47,7 +47,15 @@ function liveActionInternal(topHelpWindo
 	url=url.substring(0, i+1);
 	var encodedArg=encodeURIComponent(argument);
 	url=url+"livehelp/?pluginID="+pluginId+"&class="+className+"&arg="+encodedArg+"&nocaching="+Math.random();
-
+    <%
+    Object token = request.getSession().getAttribute("LSESSION"); //$NON-NLS-1$
+    // Validate token to protect against XSS
+    if (token instanceof String && ((String)token).matches("[a-z0-9-]{36}")) {//$NON-NLS-1$) {
+    %>
+    url=url+"&token=<%=token%>";
+    <%
+    }
+    %>
 	// we need to find the toolbar frame.
 	// to do: cleanup this, including the location of the hidden livehelp frame.	
 	var toolbarFrame = topHelpWindow.HelpFrame.ContentFrame.ContentToolbarFrame;
Index: eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/index.jsp
===================================================================
--- eclipse-platform-sources-I20180906-0745.orig/eclipse.platform.ua/org.eclipse.help.webapp/index.jsp
+++ eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/index.jsp
@@ -12,6 +12,8 @@
      IBM Corporation - initial API and implementation
 --%>
 <%@ page import="org.eclipse.help.internal.webapp.data.*" errorPage="/advanced/err.jsp" contentType="text/html; charset=UTF-8"%>
+<%@ page import="java.util.UUID" %>
+<%@ page import="org.eclipse.help.internal.base.BaseHelpSystem" %>
 <%
 	request.setCharacterEncoding("UTF-8");
 	ServerState.webappStarted(application,request, response);	
@@ -38,9 +40,28 @@
 </body>
 </html>	
 <%
-	}else if(data.isAdvancedUI()){
-		request.getRequestDispatcher("/advanced/index.jsp" + data.getQuery()).forward(request, response);
-	}else{
-		request.getRequestDispatcher("/basic/index.jsp" + data.getQuery()).forward(request, response);
+	} else {
+       // For live help
+        String token = request.getParameter("token"); //$NON-NLS-1$
+        if (token != null && token.matches("[a-z0-9-]{36}")) { //$NON-NLS-1$
+            if (BaseHelpSystem.getInstance().matchOnceLiveHelpToken(token)) {
+                // Only one session can grab this
+                if (request.getSession().getAttribute("XSESSION") == null) { //$NON-NLS-1$
+                    String token2 = UUID.randomUUID().toString();
+                    request.getSession().setAttribute("XSESSION", token2); //$NON-NLS-1$
+                    int port = request.getLocalPort();
+                    response.addHeader("Set-Cookie", "XSESSION-" + port + "=" + token2 + "; HttpOnly; SameSite=Strict"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+                    String token3 = UUID.randomUUID().toString();
+                    request.getSession().setAttribute("LSESSION", token3); //$NON-NLS-1$
+                }
+            }
+        }
+
+       if(data.isAdvancedUI()) {
+           request.getRequestDispatcher("/advanced/index.jsp" + data.getQuery()).forward(request, response);
+       } else { // legacy UI
+           request.getRequestDispatcher("/basic/index.jsp" + data.getQuery()).forward(request, response);
+       }
+       request.getRequestDispatcher("/advanced/index.jsp" + data.getQuery()).forward(request, response);
 	}
 %>
Index: eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/src/org/eclipse/help/internal/webapp/data/LayoutData.java
===================================================================
--- eclipse-platform-sources-I20180906-0745.orig/eclipse.platform.ua/org.eclipse.help.webapp/src/org/eclipse/help/internal/webapp/data/LayoutData.java
+++ eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/src/org/eclipse/help/internal/webapp/data/LayoutData.java
@@ -46,6 +46,11 @@ public class LayoutData extends RequestD
 
 		// initialize the query string
 		String qs = request.getQueryString();
+		// Remove any live help token
+        if (qs != null) {
+            qs = qs.replaceFirst("^token=[a-z0-9-]{36}", ""); //$NON-NLS-1$ //$NON-NLS-2$
+            qs = qs.replaceFirst("&token=[a-z0-9-]{36}", ""); //$NON-NLS-1$ //$NON-NLS-2$
+        }
 		if (qs != null && qs.length() > 0)
 			query = "?" + qs; //$NON-NLS-1$
 	}
Index: eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/src/org/eclipse/help/internal/webapp/servlet/LiveHelpServlet.java
===================================================================
--- eclipse-platform-sources-I20180906-0745.orig/eclipse.platform.ua/org.eclipse.help.webapp/src/org/eclipse/help/internal/webapp/servlet/LiveHelpServlet.java
+++ eclipse-platform-sources-I20180906-0745/eclipse.platform.ua/org.eclipse.help.webapp/src/org/eclipse/help/internal/webapp/servlet/LiveHelpServlet.java
@@ -14,8 +14,8 @@
 package org.eclipse.help.internal.webapp.servlet;
 
 import java.io.IOException;
-
 import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -51,6 +51,45 @@ public class LiveHelpServlet extends Htt
 			return;
 		}
 		req.setCharacterEncoding("UTF-8"); //$NON-NLS-1$
+		String sessionid = req.getSession().getId();
+ 		Cookie cookies[] = req.getCookies();
+ 		boolean jsessOK = false;
+ 		boolean xsessOK = false;
+ 		boolean lsessOK = false;
+ 		// Unique session ID per help server
+ 		int port = req.getLocalPort();
+ 		String xsessname = "XSESSION-" + port; //$NON-NLS-1$
+ 		if (cookies != null) {
+ 			for (Cookie cookie : cookies) {
+ 				if (cookie.getName().equals("JSESSIONID")) {//$NON-NLS-1$
+ 					if (sessionid.length() >= 30 &&
+ 							cookie.getValue().startsWith(sessionid)) {
+ 						jsessOK = true;
+ 					}
+ 				}
+ 				if (cookie.getName().equals(xsessname)) {
+ 					if (cookie.getValue().equals(req.getSession().getAttribute("XSESSION"))) { //$NON-NLS-1$
+ 						xsessOK = true;
+ 					}
+ 				}
+ 			}
+ 		}
+ 		String token = req.getParameter("token"); //$NON-NLS-1$
+ 		if (token != null && token.equals(req.getSession().getAttribute("LSESSION"))) { //$NON-NLS-1$
+ 			lsessOK = true;
+ 		}
+ 		if (!jsessOK) {
+ 			resp.sendError(HttpServletResponse.SC_FORBIDDEN, "JSESSIONID"); //$NON-NLS-1$
+ 			return;
+ 		}
+ 		if (!lsessOK) {
+ 			resp.sendError(HttpServletResponse.SC_FORBIDDEN, "token"); //$NON-NLS-1$
+ 			return;
+ 		}
+ 		if (!xsessOK) {
+ 			resp.sendError(HttpServletResponse.SC_FORBIDDEN, xsessname);
+ 			return;
+ 		}
 		String pluginID = req.getParameter("pluginID"); //$NON-NLS-1$
 		if (pluginID == null)
 			return;
@@ -59,6 +98,11 @@ public class LiveHelpServlet extends Htt
 			return;
 		String arg = req.getParameter("arg"); //$NON-NLS-1$
 		BaseHelpSystem.runLiveHelp(pluginID, className, arg);
+		/*
+ 		 * @FIXME Should runLiveHelp return an error if the plugin/class is wrong
+ 		 * so a SC_BAD_REQUEST can be returned? Or does this reveal too much?
+ 		 */
+ 		resp.setStatus(HttpServletResponse.SC_ACCEPTED);
 	}
 	/**
 	 *
