瀏覽代碼

18.0.6 release

David Benson 3 年之前
父節點
當前提交
7a68ebe22a

+ 0 - 2
.github/workflows/build-release.yml

@@ -2,8 +2,6 @@ name: Build & Release
 
 on:
   push:
-    paths:
-      - VERSION
       
 jobs:
   build:

+ 7 - 1
ChangeLog

@@ -1,6 +1,12 @@
+16-MAY-2022: 18.0.6
+
+- Moves sanitize URL to Utils, adds extra IPv6 check
+- Adds additional checks for hyperlinks
+
 15-MAY-2022: 18.0.5
 
-- Security improvements in Java code
+- Adds isLinkLocalAddress() to address checks
+- Limits well known servlet to serving single file
 
 14-MAY-2022: 18.0.4
 

+ 1 - 1
VERSION

@@ -1 +1 @@
-18.0.5
+18.0.6

+ 1 - 1
src/main/java/com/mxgraph/online/EmbedServlet2.java

@@ -393,7 +393,7 @@ public class EmbedServlet2 extends HttpServlet
 				try
 				{
 					// Checks if URL already fetched to avoid duplicates
-					if (!completed.contains(urls[i]))
+					if (!completed.contains(urls[i]) && Utils.sanitizeUrl(urls[i]))
 					{
 						completed.add(urls[i]);
 						URL url = new URL(urls[i]);

+ 2 - 70
src/main/java/com/mxgraph/online/ProxyServlet.java

@@ -11,11 +11,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.UnknownHostException;
-import java.net.InetAddress;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -68,7 +66,7 @@ public class ProxyServlet extends HttpServlet
 	{
 		String urlParam = request.getParameter("url");
 
-		if (checkUrlParameter(urlParam))
+		if (Utils.sanitizeUrl(urlParam))
 		{
 			// build the UML source from the compressed request parameter
 			String ref = request.getHeader("referer");
@@ -118,7 +116,7 @@ public class ProxyServlet extends HttpServlet
 					{
 						String redirectUrl = connection.getHeaderField("Location");
 
-						if (!checkUrlParameter(redirectUrl))
+						if (!Utils.sanitizeUrl(redirectUrl))
 						{
 							break;
 						}
@@ -235,72 +233,6 @@ public class ProxyServlet extends HttpServlet
 		}
 	}
 
-	/**
-	 * Checks if the URL parameter is legal.
-	 */
-	public boolean checkUrlParameter(String url)
-	{
-		if (url != null)
-		{
-			try
-			{
-				URL parsedUrl = new URL(url);
-				String protocol = parsedUrl.getProtocol();
-				String host = parsedUrl.getHost();
-				InetAddress address = InetAddress.getByName(host);
-				String hostAddress = address.getHostAddress();
-				host = host.toLowerCase();
-
-				return (protocol.equals("http") || protocol.equals("https"))
-						&& !address.isAnyLocalAddress()
-						&& !address.isLoopbackAddress()
-						&& !address.isLinkLocalAddress()
-						&& !host.endsWith(".internal") // Redundant
-						&& !host.endsWith(".local") // Redundant
-						&& !host.contains("localhost") // Redundant
-						&& !hostAddress.startsWith("0.") // 0.0.0.0/8 
-						&& !hostAddress.startsWith("10.") // 10.0.0.0/8
-						&& !hostAddress.startsWith("127.") // 127.0.0.0/8
-						&& !hostAddress.startsWith("169.254.") // 169.254.0.0/16
-						&& !hostAddress.startsWith("172.16.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.17.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.18.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.19.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.20.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.21.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.22.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.23.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.24.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.25.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.26.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.27.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.28.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.29.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.30.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("172.31.") // 172.16.0.0/12
-						&& !hostAddress.startsWith("192.0.0.") // 192.0.0.0/24
-						&& !hostAddress.startsWith("192.168.") // 192.168.0.0/16
-						&& !hostAddress.startsWith("198.18.") // 198.18.0.0/15
-						&& !hostAddress.startsWith("198.19.") // 198.18.0.0/15
-						&& !hostAddress.startsWith("fc00::") // fc00::/7 https://stackoverflow.com/questions/53764109/is-there-a-java-api-that-will-identify-the-ipv6-address-fd00-as-local-private
-						&& !hostAddress.startsWith("fd00::") // fd00::/8
-						&& !host.endsWith(".arpa"); // reverse domain (needed?)
-			}
-			catch (MalformedURLException e)
-			{
-				return false;
-			}
-			catch (UnknownHostException e)
-			{
-				return false;
-			}
-		}
-		else
-		{
-			return false;
-		}
-	}
-
 	/**
 	 * Returns true if the content check should be omitted.
 	 */

+ 73 - 0
src/main/java/com/mxgraph/online/Utils.java

@@ -12,7 +12,11 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.net.URLEncoder;
+import java.net.UnknownHostException;
 import java.security.SecureRandom;
 import java.util.zip.Deflater;
 import java.util.zip.Inflater;
@@ -481,4 +485,73 @@ public class Utils
 			return false;  
 		}  
 	}
+
+	/**
+	 * Checks if the URL parameter is legal, i.e. isn't attempting an SSRF
+	 * 
+	 * @param url the URL to check
+	 * @return true if the URL is permitted
+	 */
+	public static boolean sanitizeUrl(String url)
+	{
+		if (url != null)
+		{
+			try
+			{
+				URL parsedUrl = new URL(url);
+				String protocol = parsedUrl.getProtocol();
+				String host = parsedUrl.getHost();
+				InetAddress address = InetAddress.getByName(host);
+				String hostAddress = address.getHostAddress();
+				host = host.toLowerCase();
+
+				return (protocol.equals("http") || protocol.equals("https"))
+						&& !address.isAnyLocalAddress()
+						&& !address.isLoopbackAddress()
+						&& !address.isLinkLocalAddress()
+						&& !host.endsWith(".internal") // Redundant
+						&& !host.endsWith(".local") // Redundant
+						&& !host.contains("localhost") // Redundant
+						&& !hostAddress.startsWith("0.") // 0.0.0.0/8 
+						&& !hostAddress.startsWith("10.") // 10.0.0.0/8
+						&& !hostAddress.startsWith("127.") // 127.0.0.0/8
+						&& !hostAddress.startsWith("169.254.") // 169.254.0.0/16
+						&& !hostAddress.startsWith("172.16.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.17.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.18.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.19.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.20.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.21.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.22.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.23.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.24.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.25.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.26.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.27.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.28.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.29.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.30.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("172.31.") // 172.16.0.0/12
+						&& !hostAddress.startsWith("192.0.0.") // 192.0.0.0/24
+						&& !hostAddress.startsWith("192.168.") // 192.168.0.0/16
+						&& !hostAddress.startsWith("198.18.") // 198.18.0.0/15
+						&& !hostAddress.startsWith("198.19.") // 198.18.0.0/15
+						&& !hostAddress.startsWith("fc00::") // fc00::/7 https://stackoverflow.com/questions/53764109/is-there-a-java-api-that-will-identify-the-ipv6-address-fd00-as-local-private
+						&& !hostAddress.startsWith("fd00::") // fd00::/8
+						&& !host.endsWith(".arpa"); // reverse domain (needed?)
+			}
+			catch (MalformedURLException e)
+			{
+				return false;
+			}
+			catch (UnknownHostException e)
+			{
+				return false;
+			}
+		}
+		else
+		{
+			return false;
+		}
+	}
 }

文件差異過大導致無法顯示
+ 11 - 11
src/main/webapp/js/app.min.js


+ 59 - 29
src/main/webapp/js/grapheditor/Graph.js

@@ -1663,16 +1663,36 @@ Graph.removePasteFormatting = function(elt)
  */
 Graph.sanitizeHtml = function(value, editing)
 {
-	return DOMPurify.sanitize(value, {ADD_ATTR: ['target'], FORBID_TAGS: ['form'],
-		ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel|callto|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i});
+	return Graph.domPurify(value, false);
+};
+
+/**
+ * Returns the size of the page format scaled with the page size.
+ */
+Graph.sanitizeLink = function(href)
+{
+	var a = document.createElement('a');
+	a.setAttribute('href', href);
+	Graph.sanitizeNode(a);
+	
+	return a.getAttribute('href');
 };
 
 /**
- * Sanitizes the SVG in the given DOM node in-place.
+ * Sanitizes the given DOM node in-place.
  */
-Graph.sanitizeSvg = function(div)
+Graph.sanitizeNode = function(value)
 {
-	return DOMPurify.sanitize(div, {IN_PLACE: true});
+	return Graph.domPurify(value, true);
+};
+
+/**
+ * Sanitizes the given value.
+ */
+Graph.domPurify = function(value, inPlace)
+{
+	return DOMPurify.sanitize(value, {IN_PLACE: inPlace, ADD_ATTR: ['target'], FORBID_TAGS: ['form'],
+		ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel|callto|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i});
 };
 
 /**
@@ -1702,7 +1722,7 @@ Graph.clipSvgDataUri = function(dataUri, ignorePreserveAspect)
 				div.innerHTML = data.substring(idx);
 				
 				// Removes all attributes starting with on
-				Graph.sanitizeSvg(div);
+				Graph.sanitizeNode(div);
 				
 				// Gets the size and removes from DOM
 				var svgs = div.getElementsByTagName('svg');
@@ -2889,35 +2909,40 @@ Graph.prototype.openLink = function(href, target, allowOpener)
 	
 	try
 	{
-		// Workaround for blocking in same iframe
-		if (target == '_self' && window != window.top)
-		{
-			window.location.href = href;
-		}
-		else
+		href = Graph.sanitizeLink(href);
+
+		if (href != null)
 		{
-			// Avoids page reload for anchors (workaround for IE but used everywhere)
-			if (href.substring(0, this.baseUrl.length) == this.baseUrl &&
-				href.charAt(this.baseUrl.length) == '#' &&
-				target == '_top' && window == window.top)
+			// Workaround for blocking in same iframe
+			if (target == '_self' && window != window.top)
 			{
-				var hash = href.split('#')[1];
-	
-				// Forces navigation if on same hash
-				if (window.location.hash == '#' + hash)
-				{
-					window.location.hash = '';
-				}
-				
-				window.location.hash = hash;
+				window.location.href = href;
 			}
 			else
 			{
-				result = window.open(href, (target != null) ? target : '_blank');
-	
-				if (result != null && !allowOpener)
+				// Avoids page reload for anchors (workaround for IE but used everywhere)
+				if (href.substring(0, this.baseUrl.length) == this.baseUrl &&
+					href.charAt(this.baseUrl.length) == '#' &&
+					target == '_top' && window == window.top)
+				{
+					var hash = href.split('#')[1];
+		
+					// Forces navigation if on same hash
+					if (window.location.hash == '#' + hash)
+					{
+						window.location.hash = '';
+					}
+					
+					window.location.hash = hash;
+				}
+				else
 				{
-					result.opener = null;
+					result = window.open(href, (target != null) ? target : '_blank');
+		
+					if (result != null && !allowOpener)
+					{
+						result.opener = null;
+					}
 				}
 			}
 		}
@@ -13736,6 +13761,11 @@ if (typeof mxVertexHandler !== 'undefined')
 						}
 					}
 				}
+
+				if (this.linkHint != null)
+				{
+					Graph.sanitizeNode(this.linkHint);
+				}
 			}
 			catch (e)
 			{

文件差異過大導致無法顯示
+ 11 - 11
src/main/webapp/js/integrate.min.js


文件差異過大導致無法顯示
+ 11 - 11
src/main/webapp/js/viewer-static.min.js


文件差異過大導致無法顯示
+ 11 - 11
src/main/webapp/js/viewer.min.js


文件差異過大導致無法顯示
+ 1 - 1
src/main/webapp/mxgraph/mxClient.js


文件差異過大導致無法顯示
+ 1 - 1
src/main/webapp/service-worker.js


文件差異過大導致無法顯示
+ 1 - 1
src/main/webapp/service-worker.js.map