diff --git a/src/net/sourceforge/plantuml/FileSystem.java b/src/net/sourceforge/plantuml/FileSystem.java index a38339946..97915209c 100644 --- a/src/net/sourceforge/plantuml/FileSystem.java +++ b/src/net/sourceforge/plantuml/FileSystem.java @@ -72,32 +72,29 @@ public class FileSystem { } public SFile getFile(String nameOrPath) throws IOException { - if (isAbsolute(nameOrPath)) { + if (isAbsolute(nameOrPath)) return new SFile(nameOrPath).getCanonicalFile(); - } + final SFile dir = getCurrentDir(); SFile filecurrent = null; if (dir != null) { filecurrent = dir.getAbsoluteFile().file(nameOrPath); - if (filecurrent.exists()) { + if (filecurrent.exists()) return filecurrent.getCanonicalFile(); - } } for (SFile d : SecurityUtils.getPath(SecurityUtils.PATHS_INCLUDES)) { assert d.isDirectory(); final SFile file = d.file(nameOrPath); - if (file.exists()) { + if (file.exists()) return file.getCanonicalFile(); - - } } for (SFile d : SecurityUtils.getPath(SecurityUtils.PATHS_CLASSES)) { assert d.isDirectory(); final SFile file = d.file(nameOrPath); - if (file.exists()) { + if (file.exists()) return file.getCanonicalFile(); - } + } if (dir == null) { assert filecurrent == null; diff --git a/src/net/sourceforge/plantuml/code/URLEncoder.java b/src/net/sourceforge/plantuml/code/URLEncoder.java index 8d6b3171e..e102a54c0 100644 --- a/src/net/sourceforge/plantuml/code/URLEncoder.java +++ b/src/net/sourceforge/plantuml/code/URLEncoder.java @@ -5,12 +5,12 @@ * (C) Copyright 2009-2023, Arnaud Roques * * Project Info: http://plantuml.com - * + * * If you like this project or if you find it useful, you can support us at: - * + * * http://plantuml.com/patreon (only 1$ per month!) * http://plantuml.com/paypal - * + * * This file is part of PlantUML. * * PlantUML is free software; you can redistribute it and/or modify it diff --git a/src/net/sourceforge/plantuml/creole/atom/AtomImg.java b/src/net/sourceforge/plantuml/creole/atom/AtomImg.java index 1b5ea50ef..d3d218428 100644 --- a/src/net/sourceforge/plantuml/creole/atom/AtomImg.java +++ b/src/net/sourceforge/plantuml/creole/atom/AtomImg.java @@ -120,38 +120,38 @@ public class AtomImg extends AbstractAtom implements Atom { try { // Check if valid URL if (src.startsWith("http:") || src.startsWith("https:")) { - if (src.endsWith(".svg")) { + if (src.endsWith(".svg")) return buildSvgFromUrl(src, fc, SURL.create(src), scale, url); - } + return buildRasterFromUrl(src, fc, SURL.create(src), scale, url); } final SFile f = FileSystem.getInstance().getFile(src); if (f.exists() == false) { - if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) { + if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) return AtomTextUtils.createLegacy("(File not found: " + f.getPrintablePath() + ")", fc); - } + return AtomTextUtils.createLegacy("(Cannot decode)", fc); } if (f.getName().endsWith(".svg")) { final String tmp = FileUtils.readSvg(f); - if (tmp == null) { + if (tmp == null) return AtomTextUtils.createLegacy("(Cannot decode)", fc); - } + return new AtomImgSvg(new TileImageSvg(tmp)); } final BufferedImage read = f.readRasterImageFromFile(); if (read == null) { - if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) { + if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) return AtomTextUtils.createLegacy("(Cannot decode: " + f.getPrintablePath() + ")", fc); - } + return AtomTextUtils.createLegacy("(Cannot decode)", fc); } return new AtomImg(f.readRasterImageFromFile(), scale, url, src); } catch (IOException e) { e.printStackTrace(); - if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) { + if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) return AtomTextUtils.createLegacy("ERROR " + e.toString(), fc); - } + return AtomTextUtils.createLegacy("ERROR", fc); } } diff --git a/src/net/sourceforge/plantuml/security/SFile.java b/src/net/sourceforge/plantuml/security/SFile.java index 9b877c850..f573155e3 100644 --- a/src/net/sourceforge/plantuml/security/SFile.java +++ b/src/net/sourceforge/plantuml/security/SFile.java @@ -82,7 +82,13 @@ public class SFile implements Comparable { @Override public String toString() { - return "Image42"; + if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) + try { + return internal.getCanonicalPath(); + } catch (IOException e) { + return internal.getAbsolutePath(); + } + return super.toString(); } public SFile(String nameOrPath) { @@ -174,7 +180,7 @@ public class SFile implements Comparable { final File[] tmp = internal.listFiles(); if (tmp == null) return Collections.emptyList(); - + final List result = new ArrayList<>(tmp.length); for (File f : tmp) { result.add(new SFile(f)); @@ -263,7 +269,7 @@ public class SFile implements Comparable { if (isInAllowList(SecurityUtils.getPath(SecurityUtils.PATHS_INCLUDES))) return true; - if (isInAllowList(SecurityUtils.getPath(SecurityUtils.PATHS_ALLOWED))) + if (isInAllowList(SecurityUtils.getPath(SecurityUtils.ALLOWLIST_LOCAL_PATHS))) return true; if (SecurityUtils.getSecurityProfile() == SecurityProfile.INTERNET) diff --git a/src/net/sourceforge/plantuml/security/SURL.java b/src/net/sourceforge/plantuml/security/SURL.java index 1808b95f7..fb9a48777 100644 --- a/src/net/sourceforge/plantuml/security/SURL.java +++ b/src/net/sourceforge/plantuml/security/SURL.java @@ -110,7 +110,7 @@ public class SURL { /** * Regex to remove the UserInfo part from a URL. */ - private static final Pattern PATTERN_USERINFO = Pattern.compile("(^https?://)(.*@)(.*)"); + private static final Pattern PATTERN_USERINFO = Pattern.compile("(^https?://)([-_0-9a-zA-Z]+@)([^@]*)"); private static final ExecutorService EXE = Executors.newCachedThreadPool(new ThreadFactory() { public Thread newThread(Runnable r) { @@ -229,7 +229,7 @@ public class SURL { // We are UNSECURE anyway return true; - if (isInAllowList()) + if (isInUrlAllowList()) return true; if (SecurityUtils.getSecurityProfile() == SecurityProfile.INTERNET) { @@ -244,16 +244,16 @@ public class SURL { } private boolean forbiddenURL(String full) { - if (full.matches("^https?://\\d+\\.\\d+\\.\\d+\\.\\d+.*")) + if (full.matches("^https?://[.0-9]+/.*")) return true; if (full.matches("^https?://[^.]+/.*")) return true; return false; } - private boolean isInAllowList() { + private boolean isInUrlAllowList() { final String full = cleanPath(internal.toString()); - for (String allow : getAllowList()) + for (String allow : getUrlAllowList()) if (full.startsWith(cleanPath(allow))) return true; @@ -271,8 +271,8 @@ public class SURL { return path; } - private List getAllowList() { - final String env = SecurityUtils.getenv(SecurityUtils.PATHS_ALLOWED); + private List getUrlAllowList() { + final String env = SecurityUtils.getenv(SecurityUtils.ALLOWLIST_URL); if (env == null) return Collections.emptyList(); diff --git a/src/net/sourceforge/plantuml/security/SecurityUtils.java b/src/net/sourceforge/plantuml/security/SecurityUtils.java index 5d3188d2d..3c102b82a 100644 --- a/src/net/sourceforge/plantuml/security/SecurityUtils.java +++ b/src/net/sourceforge/plantuml/security/SecurityUtils.java @@ -95,7 +95,12 @@ public class SecurityUtils { /** * Whitelist of paths from where scripts can load data. */ - public static final String PATHS_ALLOWED = "plantuml.allowlist.path"; + public static final String ALLOWLIST_LOCAL_PATHS = "plantuml.allowlist.path"; + + /** + * Whitelist of urls + */ + public static final String ALLOWLIST_URL = "plantuml.allowlist.url"; /** * Paths to folders with security specific content (not allowed to read via diff --git a/src/net/sourceforge/plantuml/svg/SvgGraphics.java b/src/net/sourceforge/plantuml/svg/SvgGraphics.java index b8b8a06b0..5e887e23f 100644 --- a/src/net/sourceforge/plantuml/svg/SvgGraphics.java +++ b/src/net/sourceforge/plantuml/svg/SvgGraphics.java @@ -42,6 +42,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -642,19 +643,8 @@ public class SvgGraphics { } public void createXml(OutputStream os) throws TransformerException, IOException { - if (images.size() == 0) { - createXmlInternal(os); - return; - } - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - createXmlInternal(baos); - String s = new String(baos.toByteArray()); - for (Map.Entry ent : images.entrySet()) { - final String k = "<" + ent.getKey() + "/>"; - s = s.replace(k, ent.getValue()); - } - s = removeXmlHeader(s); - os.write(s.getBytes()); + createXmlInternal(os); +// s = removeXmlHeader(s); } private String removeXmlHeader(String s) { @@ -673,7 +663,7 @@ public class SvgGraphics { final int maxXscaled = (int) (maxX * scale); final int maxYscaled = (int) (maxY * scale); String style = "width:" + maxXscaled + "px;height:" + maxYscaled + "px;"; - if (/*this.classesForDarkness.size() == 0 &&*/ backcolor != null) + if (/* this.classesForDarkness.size() == 0 && */ backcolor != null) style += "background:" + backcolor + ";"; if (svgDimensionStyle) { @@ -894,17 +884,26 @@ public class SvgGraphics { ensureVisible(x + image.getWidth(), y + image.getHeight()); } - private final Map images = new HashMap(); - public void svgImage(UImageSvg image, double x, double y) { + // https://developer.mozilla.org/fr/docs/Web/SVG/Element/image if (hidden == false) { + final Element elt = (Element) document.createElement("image"); + elt.setAttribute("width", format(image.getWidth())); + elt.setAttribute("height", format(image.getHeight())); + elt.setAttribute("x", format(x)); + elt.setAttribute("y", format(y)); + String svg = manageScale(image); - final String pos = ""; - svg = pos + svg.substring(5); - final String key = "imagesvginlined" + image.getMD5Hex() + images.size(); - final Element elt = (Element) document.createElement(key); + + final String svgHeader = ""; + + svg = svgHeader + svg.substring(5); + + final String s = toBase64(svg); + elt.setAttribute("xlink:href", "data:image/svg+xml;base64," + s); + getG().appendChild(elt); - images.put(key, svg); } ensureVisible(x, y); ensureVisible(x + image.getData("width"), y + image.getData("height")); @@ -935,6 +934,11 @@ public class SvgGraphics { return new String(Base64Coder.encode(data)); } + private String toBase64(String s) { + final byte data[] = s.getBytes(Charset.forName("UTF8")); + return new String(Base64Coder.encode(data)); + } + // Shadow private boolean withShadow = false; diff --git a/src/net/sourceforge/plantuml/ugraphic/UImageSvg.java b/src/net/sourceforge/plantuml/ugraphic/UImageSvg.java index 206724d18..f122518b5 100644 --- a/src/net/sourceforge/plantuml/ugraphic/UImageSvg.java +++ b/src/net/sourceforge/plantuml/ugraphic/UImageSvg.java @@ -43,37 +43,23 @@ import net.sourceforge.plantuml.SignatureUtils; public class UImageSvg implements UShape { - private static final String EMPTY_SVG = ""; private final String svg; private final double scale; public UImageSvg(String svg, double scale) { - this.svg = clean(Objects.requireNonNull(svg)); + this.svg = Objects.requireNonNull(svg); this.scale = scale; } - private String clean(final String svg) { - final String svg2 = svg.toLowerCase().replaceAll("\\s", ""); - if (svg2.contains("")) - return EMPTY_SVG; - if (svg2.contains("")) - return EMPTY_SVG; - return svg; - } - public String getMD5Hex() { return SignatureUtils.getMD5Hex(svg); } public String getSvg(boolean raw) { String result = svg; - if (raw) { + if (raw) return result; - } + if (result.startsWith("", " "); - } + } - if (result.startsWith("") == false) { + if (result.startsWith("") == false) throw new IllegalArgumentException(); - } + return result; } private String extractBackground(String style) { final Pattern p = Pattern.compile("background:([^;]+)"); final Matcher m = p.matcher(style); - if (m.find()) { + if (m.find()) return m.group(1); - } + return null; } private String extractSvgStyle() { final Pattern p = Pattern.compile("(?i)\\]+style=\"([^\">]+)\""); final Matcher m = p.matcher(svg); - if (m.find()) { + if (m.find()) return m.group(1); - } + return null; } diff --git a/src/net/sourceforge/plantuml/version/Version.java b/src/net/sourceforge/plantuml/version/Version.java index 0fd338c21..a587da53c 100644 --- a/src/net/sourceforge/plantuml/version/Version.java +++ b/src/net/sourceforge/plantuml/version/Version.java @@ -80,7 +80,7 @@ public class Version { } public static int beta() { - final int beta = 0; + final int beta = 1; return beta; }