CVE-2023-3432 (Java, published 2023-06-27T15:15Z)
Description: value: Server-Side Request Forgery (SSRF) in GitHub repository plantuml/plantuml prior to 1.2023.9.
Severity: nan (CVSS v3 base score: 10)
Weakness: CWE-918 \u2014 Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
Repository: https://github.com/plantuml/plantuml
Files changed:
gradle.properties
src/net/sourceforge/plantuml/file/AParentFolderRegular.java
src/net/sourceforge/plantuml/filesdiagram/FilesEntry.java
src/net/sourceforge/plantuml/filesdiagram/FilesListing.java
src/net/sourceforge/plantuml/filesdiagram/FilesType.java
src/net/sourceforge/plantuml/klimt/sprite/SpriteColor.java
src/net/sourceforge/plantuml/nwdiag/NwDiagram.java
src/net/sourceforge/plantuml/nwdiag/core/NServer.java
src/net/sourceforge/plantuml/security/SURL.java
src/net/sourceforge/plantuml/tim/TFunctionImpl.java
src/net/sourceforge/plantuml/version/LicenseInfo.java
src/net/sourceforge/plantuml/version/Version.java
Commit message: fix: improve filelist support and nwdiag


Vulnerable code:
version = 1.2023.9beta3
		if (dir == null) {
		} else {
		}
		if (filecurrent.exists()) {
		}
import java.util.ArrayList;
import java.util.List;

import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
	private final List<String> tmp = new ArrayList<>();
		for (String s : tmp) {
			final Display display = Display.getWithNewlines("<:1f4c4:>" + s);
			TextBlock result = display.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam);
			result.drawU(ug);
			ug = ug.apply(UTranslate.dy(result.calculateDimension(ug.getStringBounder()).getHeight()));
		}

			tmp.add(line.substring(1));
		if (x < 0 || x >= width) {
		}
		if (y < 0 || y >= height) {
		}
		if (level < 0 || level >= 16) {
		}
		if (x < 0 || x >= width) {
		}
		if (y < 0 || y >= height) {
		}
		final BufferedImage im = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		if (backcolor == null) {
		}
		if (forecolor == null) {
		}
	private static final Pattern PATTERN_USERINFO = Pattern.compile("(^https?://)([-_0-9a-zA-Z]+@)([^@]*)");
		for (TFunctionArgument tmp : args) {
		}
		for (String n : namedArguments) {
			if (signature.getNamedArguments().contains(n) == false) {
			}
		}
		if (nbArg > args.size()) {
		}
			if (namedArguments.contains(arg.getName())) {
			}
			if (arg.getOptionalDefaultValue() == null) {
			}
		if (nbArg < neededArgument) {
		}
			if (value == null) {
			}
			if (functionType == TFunctionType.PROCEDURE) {
				// this.functionType = TFunctionType.RETURN;
			}
		if (functionType != TFunctionType.PROCEDURE && functionType != TFunctionType.LEGACY_DEFINELONG) {
		}
		if (functionType == TFunctionType.LEGACY_DEFINE) {
		}
		if (functionType != TFunctionType.RETURN_FUNCTION) {
		}
		if (result == null) {
		}
		if (legacyDefinition == null) {
		}
		if (tmp == null) {
		}
		if (functionType != TFunctionType.LEGACY_DEFINELONG) {
		}
		if (OptionFlags.ALLOW_INCLUDE == false) {
		}
			if (cache.isValid()) {
			}
					if (result == null) {
					}
					if (cache.isValid()) {
					}
			if (SignatureUtils.toHexString(s1).equals(SignatureUtils.toHexString(licenseInfo.sha)) == false) {
			}
			if (dis == null) {
			}
		if (dis == null) {
		}
			for (StackTraceElement ste : creationPoint.getStackTrace()) {
				if (ste.toString().contains(result.context)) {
				}
			}
			if (dir.isFile()) {
			}
			if (dir != null && dir.isDirectory()) {
			}
		if (value.isValid() || def.isNone()) {
		}
		if (br == null) {
		}
			if (result != null) {
			}
	private static final String version = "1.2023.9beta3";

Fixed code:
version = 1.2023.9beta4
		if (dir == null)
		else

		if (filecurrent.exists())

/* ========================================================================
 * PlantUML : a free UML diagram generator
 * ========================================================================
 *
 * (C) Copyright 2009-2024, Arnaud Roques
 *
 * Project Info:  https://plantuml.com
 * 
 * If you like this project or if you find it useful, you can support us at:
 * 
 * https://plantuml.com/patreon (only 1$ per month!)
 * https://plantuml.com/paypal
 * 
 * This file is part of PlantUML.
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PlantUML distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 *
 * Original Author:  Arnaud Roques
 *
 */
package net.sourceforge.plantuml.filesdiagram;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.style.ISkinParam;

public class FilesEntry implements Iterable<FilesEntry> {

	private final String name;
	private FilesType type;
	private List<FilesEntry> children = new ArrayList<>();

	public FilesEntry(String name, FilesType type) {
		this.name = name;
		this.type = type;
	}

	public FilesEntry addRawEntry(String raw) {
		final int x = raw.indexOf('/');
		if (x == -1) {
			final FilesEntry result = new FilesEntry(raw, FilesType.DATA);
			children.add(result);
			return result;
		}
		final FilesEntry folder = getOrCreateFolder(raw.substring(0, x));
		final String remain = raw.substring(x + 1);
		if (remain.length() == 0)
			return folder;
		return folder.addRawEntry(remain);
	}

	private FilesEntry getOrCreateFolder(String folderName) {
		for (FilesEntry child : children)
			if (child.type == FilesType.FOLDER && child.getName().equals(folderName))
				return child;

		final FilesEntry result = new FilesEntry(folderName, FilesType.FOLDER);
		children.add(result);
		return result;
	}

	@Override
	public Iterator<FilesEntry> iterator() {
		return Collections.unmodifiableCollection(children).iterator();
	}

	public String getName() {
		return name;
	}

	public String getEmoticon() {
		if (type == FilesType.FOLDER)
			return "<:1f4c2:>";
		// return "<:1f4c1:>";
		return "<:1f4c4:>";
	}

	public UGraphic drawAndMove(UGraphic ug, FontConfiguration fontConfiguration, ISkinParam skinParam, double deltax) {
		final Display display = Display.getWithNewlines(getEmoticon() + getName());
		TextBlock result = display.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam);
		result.drawU(ug.apply(UTranslate.dx(deltax)));
		ug = ug.apply(UTranslate.dy(result.calculateDimension(ug.getStringBounder()).getHeight() + 2));
		for (FilesEntry child : children)
			ug = child.drawAndMove(ug, fontConfiguration, skinParam, deltax + 21);
		return ug;
	}

}
	private final FilesEntry root = new FilesEntry("", FilesType.FOLDER);
		for (FilesEntry ent : root)
			ug = ent.drawAndMove(ug, fontConfiguration, skinParam, 0);
			root.addRawEntry(line.substring(1));
/* ========================================================================
 * PlantUML : a free UML diagram generator
 * ========================================================================
 *
 * (C) Copyright 2009-2024, Arnaud Roques
 *
 * Project Info:  https://plantuml.com
 * 
 * If you like this project or if you find it useful, you can support us at:
 * 
 * https://plantuml.com/patreon (only 1$ per month!)
 * https://plantuml.com/paypal
 * 
 * This file is part of PlantUML.
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PlantUML distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 *
 * Original Author:  Arnaud Roques
 *
 */
package net.sourceforge.plantuml.filesdiagram;

public enum FilesType {
	FOLDER, DATA;

}
		if (x < 0 || x >= width)

		if (y < 0 || y >= height)

		if (level < 0 || level >= 16)

		if (x < 0 || x >= width)

		if (y < 0 || y >= height)

		final BufferedImage im = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		if (backcolor == null)

		if (forecolor == null)


		if (networks.size() == 0 && groups.size() == 0)
			eventuallyConnectAllStandaloneServersToHiddenNetwork();

	private void eventuallyConnectAllStandaloneServersToHiddenNetwork() {
		Network first = null;
		for (NServer server : servers.values())
			if (server.isAlone()) {
				if (first == null) {
					first = createNetwork("");
					first.goInvisible();
				}
				server.connectMeIfAlone(first);
			}
	}

		if (address == null)
			address = "";
	private static final Pattern PATTERN_USERINFO = Pattern.compile("(^https?://)([-_0-9a-zA-Z]+@)([^@]*)$");
		// Thanks to Agasthya Kasturi
		if (full.contains("@"))
			return true;
		// Thanks to Agasthya Kasturi
		if (full.contains("@"))
			return false;
		for (TFunctionArgument tmp : args)

		for (String n : namedArguments)
			if (signature.getNamedArguments().contains(n) == false)

		if (nbArg > args.size())

			if (namedArguments.contains(arg.getName()))

			if (arg.getOptionalDefaultValue() == null)

		if (nbArg < neededArgument)

			if (value == null)

			if (functionType == TFunctionType.PROCEDURE)
		if (functionType != TFunctionType.PROCEDURE && functionType != TFunctionType.LEGACY_DEFINELONG)

		if (functionType == TFunctionType.LEGACY_DEFINE)

		if (functionType != TFunctionType.RETURN_FUNCTION)

		if (result == null)

		if (legacyDefinition == null)

		if (tmp == null)

		if (functionType != TFunctionType.LEGACY_DEFINELONG)

		if (OptionFlags.ALLOW_INCLUDE == false)

			if (cache.isValid())

					if (result == null)

					if (cache.isValid())

			if (SignatureUtils.toHexString(s1).equals(SignatureUtils.toHexString(licenseInfo.sha)) == false)

			if (dis == null)

		if (dis == null)

			for (StackTraceElement ste : creationPoint.getStackTrace())
				if (ste.toString().contains(result.context))

			if (dir.isFile())

			if (dir != null && dir.isDirectory())

		if (value.isValid() || def.isNone())

		if (br == null)

			if (result != null)

	private static final String version = "1.2023.9beta4";