David Benson 3 лет назад
Родитель
Сommit
9dc0d5d1b4

+ 7 - 0
ChangeLog

@@ -1,3 +1,10 @@
+20-MAY-2022: 18.1.1
+
+- [VSDX import] Fixes case when theme index is incorrect
+- Checks stencil name parameter in embed servlet
+- Adds DOM_PURIFY_CONFIG global variable
+- Check env var is set in ExportProxy
+
 19-MAY-2022: 18.0.8
 
 - Deletes unused ExportProxyServlet

+ 1 - 1
VERSION

@@ -1 +1 @@
-18.0.8
+18.1.1

+ 1 - 1
etc/build/build.xml

@@ -504,7 +504,7 @@
 
 	<target name="javac" description="Java compilation">
 		<mkdir dir="${javac.dir}"/>
-		<javac includeantruntime="false" srcdir="${src.dir}" excludes="**/EmbedServlet2.java" destdir="${javac.dir}">
+		<javac includeantruntime="false" srcdir="${src.dir}" destdir="${javac.dir}">
 			<classpath refid="javac.class.path" />
 		</javac>
 		<copy todir="${javac.dir}" file="${src.dir}/log4j.properties" />

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

@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.io.File;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
@@ -268,7 +269,7 @@ public class EmbedServlet2 extends HttpServlet
 
 			for (int i = 0; i < names.length; i++)
 			{
-				if (names[i].indexOf("..") < 0 && !done.contains(names[i]))
+				if (names[i].indexOf("..") < 0 && !done.contains(names[i]) && names[i].length() > 0)
 				{
 					if (names[i].equals("*"))
 					{
@@ -278,6 +279,9 @@ public class EmbedServlet2 extends HttpServlet
 					}
 					else
 					{
+						// Makes name canonical
+						names[i] = new File("/" + names[i]).getCanonicalPath().substring(1);
+
 						// Checks if any JS files are associated with the library
 						// name and injects the JS into the page
 						String[] libs = libraries.get(names[i]);

+ 168 - 0
src/main/java/com/mxgraph/online/ExportProxyServlet.java

@@ -0,0 +1,168 @@
+package com.mxgraph.online;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Servlet implementation ExportProxyServlet
+ */
+@SuppressWarnings("serial")
+public class ExportProxyServlet extends HttpServlet
+{
+	private final String[] supportedServices = {"EXPORT_URL", "PLANTUML_URL", "VSD_CONVERT_URL", "EMF_CONVERT_URL"};
+	
+	private void doRequest(String method, HttpServletRequest request,
+			HttpServletResponse response) throws ServletException, IOException
+	{
+		try
+		{
+			int serviceId = 0;
+			String proxyPath = "";
+			String queryString = "";
+			
+			try 
+			{
+				if (request.getQueryString() != null)
+				{
+					queryString = "?" + request.getQueryString(); 	
+				}
+				
+				if (request.getPathInfo() != null) // /{serviceId}/*
+				{
+					String[] pathParts = request.getPathInfo().split("/");
+	
+					if (pathParts.length > 1)
+					{
+						serviceId = Integer.parseInt(pathParts[1]);
+					}
+					
+					if (pathParts.length > 2)
+					{
+						proxyPath = String.join("/", Arrays.copyOfRange(pathParts, 2, pathParts.length));
+					}
+					
+					if (serviceId < 0 || serviceId > supportedServices.length)
+					{
+						serviceId = 0;
+					}
+				}
+			}
+			catch (Exception e) 
+			{
+				// Ignore and use 0
+				serviceId = 0;
+			}
+			
+			String exportUrl = System.getenv(supportedServices[serviceId]);
+			
+			if (exportUrl == null || exportUrl.isEmpty())
+			{
+				throw new Exception(supportedServices[serviceId] + " not set");
+			}
+			
+			URL url = new URL(exportUrl + proxyPath + queryString);
+			HttpURLConnection con = (HttpURLConnection) url.openConnection();
+			
+			con.setRequestMethod(method);
+			
+			//Copy request headers to export server
+			Enumeration<String> headerNames = request.getHeaderNames();
+			 
+	        while (headerNames.hasMoreElements()) 
+	        {
+	            String headerName = headerNames.nextElement();
+	            Enumeration<String> headers = request.getHeaders(headerName);
+	            
+	            while (headers.hasMoreElements()) 
+	            {
+	                String headerValue = headers.nextElement();
+	                con.addRequestProperty(headerName, headerValue);
+	            }
+	        }
+	        
+	        if ("POST".equals(method))
+	        {
+				// Send post request
+				con.setDoOutput(true);
+				
+				OutputStream params = con.getOutputStream();
+				Utils.copy(request.getInputStream(), params);
+				params.flush();
+				params.close();
+	        }
+	        
+	        int responseCode = con.getResponseCode();
+			//Copy response code
+			response.setStatus(responseCode);
+			
+			//Copy response headers
+			Map<String, List<String>> map = con.getHeaderFields();
+			
+			for (Map.Entry<String, List<String>> entry : map.entrySet()) 
+			{
+				String key = entry.getKey();
+				
+				if (key != null)
+				{
+					for (String val : entry.getValue())
+					{	
+						
+						response.addHeader(entry.getKey(), val);
+					}
+				}
+			}
+			
+			//Copy response
+			OutputStream out = response.getOutputStream();
+			
+			//Error
+			if (responseCode >= 400)
+			{
+				Utils.copy(con.getErrorStream(), out);
+			}
+			else //Success
+			{
+				Utils.copy(con.getInputStream(), out);
+			}
+			
+			out.flush();
+			out.close();
+		}
+		catch (Exception e)
+		{
+			response.setStatus(
+					HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+			e.printStackTrace();
+		}
+	}
+
+	
+	/**
+	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
+	 */
+	protected void doGet(HttpServletRequest request,
+			HttpServletResponse response) throws ServletException, IOException
+	{
+		doRequest("GET", request, response);
+	}
+	
+	/**
+	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
+	 */
+	protected void doPost(HttpServletRequest request,
+	HttpServletResponse response) throws ServletException, IOException
+	{
+		doRequest("POST", request, response);
+	}
+}

+ 14 - 0
src/main/webapp/WEB-INF/web.xml

@@ -95,6 +95,20 @@
     <servlet-name>ConverterServlet</servlet-name>
     <url-pattern>/convert</url-pattern>
   </servlet-mapping>
+  <servlet>
+    <description/>
+    <display-name>ExportProxyServlet</display-name>
+    <servlet-name>ExportProxyServlet</servlet-name>
+    <servlet-class>com.mxgraph.online.ExportProxyServlet</servlet-class>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>ExportProxyServlet</servlet-name>
+    <url-pattern>/export</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>ExportProxyServlet</servlet-name>
+    <url-pattern>/service/*</url-pattern>
+  </servlet-mapping>
   <servlet>
     <description/>
     <display-name>GitlabAuthServlet</display-name>

+ 273 - 47
src/main/webapp/electron.js

@@ -1530,7 +1530,7 @@ ipcMain.on('export', exportDiagram);
 // Renderer Helper functions
 //================================================================
 
-const { O_SYNC, O_CREAT, O_WRONLY, O_TRUNC } = fs.constants;
+const { O_SYNC, O_CREAT, O_WRONLY, O_TRUNC, O_RDONLY } = fs.constants;
 const DRAFT_PREFEX = '.$';
 const OLD_DRAFT_PREFEX = '~$';
 const DRAFT_EXT = '.dtmp';
@@ -1538,6 +1538,211 @@ const BKP_PREFEX = '.$';
 const OLD_BKP_PREFEX = '~$';
 const BKP_EXT = '.bkp';
 
+/**
+ * Checks the file content type
+ * Confirm content is xml, pdf, png, jpg, svg, vsdx ...
+ */
+function checkFileContent(body, enc)
+{
+	if (body != null)
+	{
+		let head, headBinay;
+		
+		if (typeof body === 'string')
+		{
+			if (enc == 'base64')
+			{
+				headBinay = Buffer.from(body.substring(0, 22), 'base64');
+				head = headBinay.toString();
+			}
+			else
+			{
+				head = body.substring(0, 16);
+				headBinay = Buffer.from(head);
+			}
+		}
+		else
+		{
+			head = new TextDecoder("utf-8").decode(body.subarray(0, 16));
+			headBinay = body;
+		}
+		
+		let c1 = head[0],
+		c2 = head[1],
+		c3 = head[2],
+		c4 = head[3],
+		c5 = head[4],
+		c6 = head[5],
+		c7 = head[6],
+		c8 = head[7],
+		c9 = head[8],
+		c10 = head[9],
+		c11 = head[10],
+		c12 = head[11],
+		c13 = head[12],
+		c14 = head[13],
+		c15 = head[14],
+		c16 = head[15];
+
+		let cc1 = headBinay[0],
+		cc2 = headBinay[1],
+		cc3 = headBinay[2],
+		cc4 = headBinay[3],
+		cc5 = headBinay[4],
+		cc6 = headBinay[5],
+		cc7 = headBinay[6],
+		cc8 = headBinay[7],
+		cc9 = headBinay[8],
+		cc10 = headBinay[9],
+		cc11 = headBinay[10],
+		cc12 = headBinay[11],
+		cc13 = headBinay[12],
+		cc14 = headBinay[13],
+		cc15 = headBinay[14],
+		cc16 = headBinay[15];
+
+		if (c1 == '<')
+		{
+			// text/html
+			if (c2 == '!'
+					|| ((c2 == 'h'
+							&& (c3 == 't' && c4 == 'm' && c5 == 'l'
+									|| c3 == 'e' && c4 == 'a' && c5 == 'd')
+							|| (c2 == 'b' && c3 == 'o' && c4 == 'd'
+									&& c5 == 'y')))
+					|| ((c2 == 'H'
+							&& (c3 == 'T' && c4 == 'M' && c5 == 'L'
+									|| c3 == 'E' && c4 == 'A' && c5 == 'D')
+							|| (c2 == 'B' && c3 == 'O' && c4 == 'D'
+									&& c5 == 'Y'))))
+			{
+				return true;
+			}
+
+			// application/xml
+			if (c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l'
+					&& c6 == ' ')
+			{
+				return true;
+			}
+			
+			// application/svg+xml
+			if (c2 == 's' && c3 == 'v' && c4 == 'g' && c5 == ' ')
+			{
+				return true;
+			}
+		}
+
+		// big and little (identical) endian UTF-8 encodings, with BOM
+		// application/xml
+		if (cc1 == 0xef && cc2 == 0xbb && cc3 == 0xbf)
+		{
+			if (c4 == '<' && c5 == '?' && c6 == 'x')
+			{
+				return true;
+			}
+		}
+
+		// big and little endian UTF-16 encodings, with byte order mark
+		// application/xml
+		if (cc1 == 0xfe && cc2 == 0xff)
+		{
+			if (cc3 == 0 && c4 == '<' && cc5 == 0 && c6 == '?' && cc7 == 0
+					&& c8 == 'x')
+			{
+				return true;
+			}
+		}
+
+		// application/xml
+		if (cc1 == 0xff && cc2 == 0xfe)
+		{
+			if (c3 == '<' && cc4 == 0 && c5 == '?' && cc6 == 0 && c7 == 'x'
+					&& cc8 == 0)
+			{
+				return true;
+			}
+		}
+
+		// big and little endian UTF-32 encodings, with BOM
+		// application/xml
+		if (cc1 == 0x00 && cc2 == 0x00 && cc3 == 0xfe && cc4 == 0xff)
+		{
+			if (cc5 == 0 && cc6 == 0 && cc7 == 0 && c8 == '<' && cc9 == 0
+					&& cc10 == 0 && cc11 == 0 && c12 == '?' && cc13 == 0
+					&& cc14 == 0 && cc15 == 0 && c16 == 'x')
+			{
+				return true;
+			}
+		}
+
+		// application/xml
+		if (cc1 == 0xff && cc2 == 0xfe && cc3 == 0x00 && cc4 == 0x00)
+		{
+			if (c5 == '<' && cc6 == 0 && cc7 == 0 && cc8 == 0 && c9 == '?'
+					&& cc10 == 0 && cc11 == 0 && cc12 == 0 && c13 == 'x'
+					&& cc14 == 0 && cc15 == 0 && cc16 == 0)
+			{
+				return true;
+			}
+		}
+
+		// application/pdf (%PDF-)
+		if (cc1 == 37 && cc2 == 80 && cc3 == 68 && cc4 == 70 && cc5 == 45)
+		{
+			return true;
+		}
+
+		// image/png
+		if ((cc1 == 137 && cc2 == 80 && cc3 == 78 && cc4 == 71 && cc5 == 13
+				&& cc6 == 10 && cc7 == 26 && cc8 == 10) ||
+			(cc1 == 194 && cc2 == 137 && cc3 == 80 && cc4 == 78 && cc5 == 71 && cc6 == 13 //Our embedded PNG+XML
+				&& cc7 == 10 && cc8 == 26 && cc9 == 10))
+		{
+			return true;
+		}
+
+		// image/jpeg
+		if (cc1 == 0xFF && cc2 == 0xD8 && cc3 == 0xFF)
+		{
+			if (cc4 == 0xE0 || cc4 == 0xEE)
+			{
+				return true;
+			}
+
+			/**
+			 * File format used by digital cameras to store images.
+			 * Exif Format can be read by any application supporting
+			 * JPEG. Exif Spec can be found at:
+			 * http://www.pima.net/standards/it10/PIMA15740/Exif_2-1.PDF
+			 */
+			if ((cc4 == 0xE1) && (c7 == 'E' && c8 == 'x' && c9 == 'i'
+					&& c10 == 'f' && cc11 == 0))
+			{
+				return true;
+			}
+		}
+
+		// vsdx, vssx (also zip, jar, odt, ods, odp, docx, xlsx, pptx, apk, aar)
+		if (cc1 == 0x50 && cc2 == 0x4B && cc3 == 0x03 && cc4 == 0x04)
+		{
+			return true;
+		}
+		else if (cc1 == 0x50 && cc2 == 0x4B && cc3 == 0x03 && cc4 == 0x06)
+		{
+			return true;
+		}
+
+		// mxfile, mxlibrary, mxGraphModel
+		if (c1 == '<' && c2 == 'm' && c3 == 'x')
+		{
+			return true;
+		}
+	}
+
+	return false;
+};
+
 function isConflict(origStat, stat)
 {
 	return stat != null && origStat != null && stat.mtimeMs != origStat.mtimeMs;
@@ -1608,9 +1813,9 @@ async function getFileDrafts(fileObject)
 
 async function saveDraft(fileObject, data)
 {
-	if (data == null || data.length == 0)
+	if (!checkFileContent(data))
 	{
-		throw new Error('empty data'); 
+		throw new Error('Invalid file data');
 	}
 	else
 	{
@@ -1632,6 +1837,11 @@ async function saveDraft(fileObject, data)
 
 async function saveFile(fileObject, data, origStat, overwrite, defEnc)
 {
+	if (!checkFileContent(data))
+	{
+		throw new Error('Invalid file data');
+	}
+
 	var retryCount = 0;
 	var backupCreated = false;
 	var bkpPath = path.join(path.dirname(fileObject.path), BKP_PREFEX + path.basename(fileObject.path) + BKP_EXT);
@@ -1640,59 +1850,52 @@ async function saveFile(fileObject, data, origStat, overwrite, defEnc)
 
 	var writeFile = async function()
 	{
-		if (data == null || data.length == 0)
+		let fh;
+
+		try
 		{
-			throw new Error('empty data');
+			// O_SYNC is for sync I/O and reduce risk of file corruption
+			fh = await fsProm.open(fileObject.path, O_SYNC | O_CREAT | O_WRONLY | O_TRUNC);
+			await fsProm.writeFile(fh, data, writeEnc);
+		}
+		finally
+		{
+			await fh?.close();
 		}
-		else
-		{			
-			let fh;
 
-			try
+		let stat2 = await fsProm.stat(fileObject.path);
+		// Workaround for possible writing errors is to check the written
+		// contents of the file and retry 3 times before showing an error
+		let writtenData = await fsProm.readFile(fileObject.path, writeEnc);
+		
+		if (data != writtenData)
+		{
+			retryCount++;
+			
+			if (retryCount < 3)
 			{
-				// O_SYNC is for sync I/O and reduce risk of file corruption
-				fh = await fsProm.open(fileObject.path, O_SYNC | O_CREAT | O_WRONLY | O_TRUNC);
-				await fsProm.writeFile(fh, data, writeEnc);
+				return await writeFile();
 			}
-			finally
+			else
 			{
-				await fh?.close();
+				throw new Error('all saving trials failed');
 			}
-
-			let stat2 = await fsProm.stat(fileObject.path);
-			// Workaround for possible writing errors is to check the written
-			// contents of the file and retry 3 times before showing an error
-			let writtenData = await fsProm.readFile(fileObject.path, writeEnc);
-			
-			if (data != writtenData)
+		}
+		else
+		{
+			//We'll keep the backup file in case the original file is corrupted. TODO When should we delete the backup file?
+			if (backupCreated)
 			{
-				retryCount++;
-				
-				if (retryCount < 3)
-				{
-					return await writeFile();
-				}
-				else
+				//fs.unlink(bkpPath, (err) => {}); //Ignore errors!
+
+				//Delete old backup file with old prefix
+				if (fs.existsSync(oldBkpPath))
 				{
-					throw new Error('all saving trials failed');
+					fs.unlink(oldBkpPath, (err) => {}); //Ignore errors
 				}
 			}
-			else
-			{
-				//We'll keep the backup file in case the original file is corrupted. TODO When should we delete the backup file?
-				if (backupCreated)
-				{
-					//fs.unlink(bkpPath, (err) => {}); //Ignore errors!
 
-					//Delete old backup file with old prefix
-					if (fs.existsSync(oldBkpPath))
-					{
-						fs.unlink(oldBkpPath, (err) => {}); //Ignore errors
-					}
-				}
-
-				return stat2;
-			}
+			return stat2;
 		}
 	};
 	
@@ -1758,7 +1961,14 @@ async function saveFile(fileObject, data, origStat, overwrite, defEnc)
 
 async function writeFile(path, data, enc)
 {
-	return await fsProm.writeFile(path, data, enc);
+	if (!checkFileContent(data, enc))
+	{
+		throw new Error('Invalid file data');
+	}
+	else
+	{
+		return await fsProm.writeFile(path, data, enc);
+	}
 };
 
 function getAppDataFolder()
@@ -1856,7 +2066,14 @@ function dirname(path_p)
 
 async function readFile(filename, encoding)
 {
-	return await fsProm.readFile(filename, encoding);
+	let data = await fsProm.readFile(filename, encoding);
+
+	if (checkFileContent(data, encoding))
+	{
+		return data;
+	}
+
+	throw new Error('Invalid file data');
 }
 
 async function fileStat(file)
@@ -1897,7 +2114,16 @@ function clipboardAction(method, data)
 
 async function deleteFile(file) 
 {
-	await fsProm.unlink(file);
+	// Reading the header of the file to confirm it is a file we can delete
+	let fh = await fsProm.open(file, O_RDONLY);
+	let buffer = Buffer.allocUnsafe(16);
+	await fh.read(buffer, 0, 16);
+	await fh.close();
+
+	if (checkFileContent(buffer))
+	{
+		await fsProm.unlink(file);
+	}
 }
 
 function windowAction(method)

Разница между файлами не показана из-за своего большого размера
+ 15 - 12
src/main/webapp/js/app.min.js


+ 20 - 8
src/main/webapp/js/diagramly/DrawioFileSync.js

@@ -452,12 +452,22 @@ DrawioFileSync.prototype.updateOnlineState = function()
 			if (this.file.isRealtimeEnabled() && this.file.isRealtimeSupported())
 			{
 				var state = this.file.getRealtimeState();
-				var err = this.file.getRealtimeError();
+				var status = mxResources.get('disconnected');
+
+				if (this.file.invalidChecksum)
+				{
+					status = mxResources.get('error') + ': ' + mxResources.get('checksum');
+				}
+				else if (this.ui.isOffline(true) || !this.isConnected())
+				{
+					status = mxResources.get('offline');
+				}
+				else if (state == 1)
+				{
+					status = mxResources.get('online');
+				}
 
-				this.ui.showError(mxResources.get('realtimeCollaboration'),
-				mxUtils.htmlEntities(state == 1 ? mxResources.get('online') :
-					((err != null && err.message != null) ?
-					err.message : mxResources.get('disconnected'))));
+				this.ui.showError(mxResources.get('realtimeCollaboration'), mxUtils.htmlEntities(status));
 			}
 			else
 			{
@@ -500,7 +510,7 @@ DrawioFileSync.prototype.updateOnlineState = function()
 		{
 			elt.style.filter = 'invert(100%)';
 		}
-		
+
 		// Prevents focus
 		mxEvent.addListener(elt, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
 			mxUtils.bind(this, function(evt)
@@ -515,9 +525,10 @@ DrawioFileSync.prototype.updateOnlineState = function()
 	
 	if (this.collaboratorsElement != null)
 	{
-		var status = '';
+		this.collaboratorsElement.style.display = 'inline-block';
 		var src = Editor.cloudImage;
-		
+		var status = '';
+
 		if (!this.enabled)
 		{
 			status = mxResources.get('disconnected');
@@ -545,6 +556,7 @@ DrawioFileSync.prototype.updateOnlineState = function()
 		
 				if (state == 1)
 				{
+					this.collaboratorsElement.style.display = 'none';
 					src = Editor.syncImage;
 				}
 				else

+ 14 - 0
src/main/webapp/js/diagramly/vsdx/importer.js

@@ -3472,6 +3472,17 @@ var com;
                     mxVsdxModel.prototype.getThemes = function () {
                         return this.themes;
                     };
+                    
+                    mxVsdxModel.prototype.getDefaultTheme = function () 
+                    {
+                        if (this.defaultTheme == null && this.themes.entries != null && this.themes.entries.length > 0)
+                        {
+                            this.defaultTheme = this.themes.entries[0].getValue();
+                        }
+
+                        return this.defaultTheme;
+                    };
+                    
                     mxVsdxModel.prototype.getRelationship = function (rid, path) {
                         var relsDoc = (function (m, k) { return m[k] ? m[k] : null; })(this.xmlDocs, path);
                         if (relsDoc == null || rid == null || (rid.length === 0)) {
@@ -9941,6 +9952,9 @@ var com;
                             if (m.entries[i].key.equals != null && m.entries[i].key.equals(k) || m.entries[i].key === k) {
                                 return m.entries[i].value;
                             } return null; })(model.getThemes(), themeIndex);
+                        if (theme == null) {
+                            theme = model.getDefaultTheme();
+                        }
                         var variant = page.getCellIntValue("VariationColorIndex", 0);
                         _this.setThemeAndVariant(theme, variant);
                         {

Разница между файлами не показана из-за своего большого размера
+ 5 - 5
src/main/webapp/js/extensions.min.js


+ 13 - 3
src/main/webapp/js/grapheditor/Graph.js

@@ -1750,13 +1750,23 @@ Graph.sanitizeNode = function(value)
 	return Graph.domPurify(value, true);
 };
 
+// Allows use tag in SVG with local references only
+DOMPurify.addHook('afterSanitizeAttributes', function(node)
+{
+	if (node.hasAttribute('xlink:href') && !node.getAttribute('xlink:href').match(/^#/))
+	{
+		node.remove();
+	} 
+});
+
 /**
  * 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});
+	window.DOM_PURIFY_CONFIG.IN_PLACE = inPlace;
+	
+	return DOMPurify.sanitize(value, window.DOM_PURIFY_CONFIG);
 };
 
 /**
@@ -1784,7 +1794,7 @@ Graph.clipSvgDataUri = function(dataUri, ignorePreserveAspect)
 			{
 				// Strips leading XML declaration and doctypes
 				div.innerHTML = data.substring(idx);
-				
+
 				// Removes all attributes starting with on
 				Graph.sanitizeNode(div);
 				

+ 3 - 0
src/main/webapp/js/grapheditor/Init.js

@@ -7,6 +7,9 @@
 window.urlParams = window.urlParams || {};
 
 // Public global variables
+window.DOM_PURIFY_CONFIG = window.DOM_PURIFY_CONFIG ||
+    {ADD_TAGS: ['use'], ADD_ATTR: ['target'], FORBID_TAGS: ['form'],
+    ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel|callto|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i};
 window.MAX_REQUEST_SIZE = window.MAX_REQUEST_SIZE  || 10485760;
 window.MAX_AREA = window.MAX_AREA || 15000 * 15000;
 

Разница между файлами не показана из-за своего большого размера
+ 20 - 17
src/main/webapp/js/integrate.min.js


Разница между файлами не показана из-за своего большого размера
+ 7 - 5
src/main/webapp/js/viewer-static.min.js


Разница между файлами не показана из-за своего большого размера
+ 7 - 5
src/main/webapp/js/viewer.min.js


Разница между файлами не показана из-за своего большого размера
+ 2 - 2
src/main/webapp/mxgraph/mxClient.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
src/main/webapp/service-worker.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
src/main/webapp/service-worker.js.map