Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
gradle.properties
### 代码内容：
# Warning, "version" should be the same in gradle.properties and Version.java
# Any idea anyone how to magically synchronize those :-) ?
version = 1.2023.9beta3
org.gradle.workers.max = 3
## 内容2
### 文件路径：
src/net/sourceforge/plantuml/file/AParentFolderRegular.java
### 代码内容：
package net.sourceforge.plantuml.file;

import java.io.IOException;

import net.sourceforge.plantuml.security.SFile;

public class AParentFolderRegular implements AParentFolder {

	private final SFile dir;

	public AParentFolderRegular(SFile dir) {
		this.dir = dir;
		// Log.info("Creating AParentFolderRegular " + dir);
	}

	@Override
	public String toString() {
		return "AParentFolderRegular::" + (dir == null ? "NULL" : dir.getPrintablePath());
	}

	public AFile getAFile(String nameOrPath) throws IOException {
		final SFile filecurrent;
		// Log.info("AParentFolderRegular::looking for " + nameOrPath);
		// Log.info("AParentFolderRegular::dir = " + dir);
		if (dir == null) {
			filecurrent = new SFile(nameOrPath);
		} else {
			filecurrent = dir.getAbsoluteFile().file(nameOrPath);
		}
		// Log.info("AParentFolderRegular::Filecurrent " + filecurrent);
		if (filecurrent.exists()) {
			return new AFileRegular(filecurrent.getCanonicalFile());
		}
		return null;
	}

}

## 内容3
### 文件路径：
src/net/sourceforge/plantuml/filesdiagram/FilesListing.java
### 代码内容：
package net.sourceforge.plantuml.filesdiagram;

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.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.font.UFont;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.AbstractTextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.style.ISkinParam;

public class FilesListing extends AbstractTextBlock {

	private final ISkinParam skinParam;
	private final FontConfiguration fontConfiguration = FontConfiguration.blackBlueTrue(UFont.courier(14));
	private final List<String> tmp = new ArrayList<>();

	public FilesListing(ISkinParam skinParam) {
		this.skinParam = skinParam;
	}

	@Override
	public XDimension2D calculateDimension(StringBounder stringBounder) {
		return new XDimension2D(200, 200);
	}

	@Override
	public void drawU(UGraphic ug) {
		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()));
		}

	}

	public void add(String line) {
		if (line.startsWith("/"))
			tmp.add(line.substring(1));
	}

}

## 内容4
### 文件路径：
src/net/sourceforge/plantuml/klimt/sprite/SpriteColor.java
### 代码内容：
package net.sourceforge.plantuml.klimt.sprite;

import java.awt.Color;
import java.awt.image.BufferedImage;

import net.atmp.PixelImage;
import net.sourceforge.plantuml.klimt.AffineTransformType;
import net.sourceforge.plantuml.klimt.color.ColorMapper;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.color.HColorGradient;
import net.sourceforge.plantuml.klimt.color.HColors;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.AbstractTextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.UImage;

public class SpriteColor implements Sprite {

	private final int width;
	private final int height;
	private final int gray[][];
	private final int color[][];

	public SpriteColor(int width, int height) {
		this.width = width;
		this.height = height;
		this.gray = new int[height][width];
		this.color = new int[height][width];
	}

	public void setGray(int x, int y, int level) {
		if (x < 0 || x >= width) {
			return;
		}
		if (y < 0 || y >= height) {
			return;
		}
		if (level < 0 || level >= 16) {
			throw new IllegalArgumentException();
		}
		gray[y][x] = level;
		color[y][x] = -1;
	}

	public void setColor(int x, int y, int col) {
		if (x < 0 || x >= width) {
			return;
		}
		if (y < 0 || y >= height) {
			return;
		}
		gray[y][x] = -1;
		color[y][x] = col;
	}

	public int getHeight() {
		return height;
	}

	public int getWidth() {
		return width;
	}

	public UImage toUImage(ColorMapper colorMapper, HColor backcolor, HColor forecolor) {
		final BufferedImage im = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

		if (backcolor == null) {
			backcolor = HColors.WHITE;
		}
		if (forecolor == null) {
			forecolor = HColors.BLACK;
		}
		final HColorGradient gradient = HColors.gradient(backcolor, forecolor, '\0');
		for (int col = 0; col < width; col++) {
			for (int line = 0; line < height; line++) {
				final int localColor = color[line][col];
				if (localColor == -1) {
					final double coef = 1.0 * gray[line][col] / (16 - 1);
					final Color c = gradient.getColor(colorMapper, coef, 255);
					im.setRGB(col, line, c.getRGB());
				} else {
					im.setRGB(col, line, localColor);
				}
			}
		}
		return new UImage(new PixelImage(im, AffineTransformType.TYPE_BILINEAR));
	}

	public TextBlock asTextBlock(final HColor color, final double scale) {
		return new AbstractTextBlock() {

			public void drawU(UGraphic ug) {
				final UImage image = toUImage(ug.getColorMapper(), ug.getParam().getBackcolor(), color);
				ug.draw(image.scale(scale));
			}

			public XDimension2D calculateDimension(StringBounder stringBounder) {
				return new XDimension2D(getWidth() * scale, getHeight() * scale);
			}
		};
	}

}

## 内容5
### 文件路径：
src/net/sourceforge/plantuml/nwdiag/NwDiagram.java
### 代码内容：
package net.sourceforge.plantuml.nwdiag;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.UmlDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.DiagramDescription;
import net.sourceforge.plantuml.core.ImageData;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.HColor;
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.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.AbstractTextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlockUtils;
import net.sourceforge.plantuml.klimt.shape.UDrawable;
import net.sourceforge.plantuml.klimt.shape.UEmpty;
import net.sourceforge.plantuml.klimt.sprite.SpriteContainerEmpty;
import net.sourceforge.plantuml.nwdiag.core.NServer;
import net.sourceforge.plantuml.nwdiag.core.NStackable;
import net.sourceforge.plantuml.nwdiag.core.Network;
import net.sourceforge.plantuml.nwdiag.core.NwGroup;
import net.sourceforge.plantuml.nwdiag.next.GridTextBlockDecorated;
import net.sourceforge.plantuml.nwdiag.next.NBar;
import net.sourceforge.plantuml.nwdiag.next.NPlayField;
import net.sourceforge.plantuml.nwdiag.next.NServerDraw;
import net.sourceforge.plantuml.skin.UmlDiagramType;
import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleBuilder;
import net.sourceforge.plantuml.style.StyleSignatureBasic;

public class NwDiagram extends UmlDiagram {

	private boolean initDone;
	private final Map<String, NServer> servers = new LinkedHashMap<>();
	private final List<Network> networks = new ArrayList<>();
	private final List<NwGroup> groups = new ArrayList<>();
	private final List<NStackable> stack = new ArrayList<NStackable>();

	private final NPlayField playField = new NPlayField();

	public DiagramDescription getDescription() {
		return new DiagramDescription("(Nwdiag)");
	}

	public NwDiagram(UmlSource source) {
		super(source, UmlDiagramType.NWDIAG, null);
	}

	public void init() {
		initDone = true;
	}

	private Network currentNetwork() {
		for (int i = 0; i < stack.size(); i++)
			if (stack.get(i) instanceof Network)
				return (Network) stack.get(i);

		return null;
	}

	private NwGroup currentGroup() {
		if (stack.size() > 0 && stack.get(0) instanceof NwGroup)
			return (NwGroup) stack.get(0);
		return null;
	}

	@Override
	public void makeDiagramReady() {
		super.makeDiagramReady();
		if (networks.size() == 0)
			createNetwork("").goInvisible();
		for (NServer server : servers.values()) {
			server.connectMeIfAlone(networks.get(0));
			playField.addInPlayfield(server.getBar());
		}
	}

	public CommandExecutionResult openGroup(String name) {
		if (initDone == false)
			return errorNoInit();

		for (NStackable element : stack)
			if (element instanceof NwGroup)
				return CommandExecutionResult.error("Cannot nest group");

		final NwGroup newGroup = new NwGroup(name);
		stack.add(0, newGroup);
		groups.add(newGroup);
		return CommandExecutionResult.ok();
	}

	public CommandExecutionResult openNetwork(String name) {
		if (initDone == false)
			return errorNoInit();

		if (currentGroup() != null)
			return CommandExecutionResult.error("Cannot open network in a group");

		for (NStackable element : stack)
			if (element instanceof Network)
				return CommandExecutionResult.error("Cannot nest network");
		final Network network = createNetwork(name);
		stack.add(0, network);
		return CommandExecutionResult.ok();
	}

	public CommandExecutionResult closeSomething() {
		if (initDone == false)
			return errorNoInit();

		if (stack.size() > 0)
			stack.remove(0);

		return CommandExecutionResult.ok();
	}

	private Network createNetwork(String name) {
		final Network network = new Network(playField.getLast(), playField.createNewStage(), name);
		networks.add(network);
		return network;
	}

	public CommandExecutionResult link(String name1, String name2) {
		if (initDone == false)
			return errorNoInit();

		NServer server1 = servers.get(name1);
		if (server1 == null) {
			if (networks.size() == 0)
				return veryFirstLink(name1, name2);
			return CommandExecutionResult.error("what about " + name1);
		}

		if (server1.isAlone()) {
			if (networks.size() == 0)
				createNetwork("").goInvisible();
			server1.connectMeIfAlone(networks.get(0));
		}

		final Network tmp1 = server1.getMainNetworkNext();
		final Network justAfter = justAfter(tmp1);

		final Network network;
		if (justAfter != null && justAfter.isVisible() == false)
			network = justAfter;
		else
			network = createNetwork("");

		NServer server2 = servers.get(name2);

		network.goInvisible();
		if (server2 == null) {
			server2 = new NServer(name2, server1.getBar(), getSkinParam());
			servers.put(name2, server2);
			server1.connectTo(network, "");
			server2.connectTo(network, "");
		} else {
			server1.blankSomeAddress();
			server1.connectTo(network, server1.someAddress());
			server2.connectTo(network, server2.someAddress());

		}
		playField.addInPlayfield(server2.getBar());

		return CommandExecutionResult.ok();
	}

	private Network justAfter(Network n) {
		final int x = networks.indexOf(n);
		if (x != -1 && x < networks.size() - 1)
			return networks.get(x + 1);
		return null;
	}

	private CommandExecutionResult veryFirstLink(String name1, String name2) {
		Network network = createNetwork(name1);
		NServer server2 = NServer.create(name2, getSkinParam());
		servers.put(name2, server2);
		server2.connectTo(network, "");
		playField.addInPlayfield(server2.getBar());
		return CommandExecutionResult.ok();
	}

	public CommandExecutionResult addElement(String name, String definition) {
		if (initDone == false)
			return errorNoInit();

		final Map<String, String> props = toSet(definition);
		NServer server = servers.get(name);
		if (server == null) {
			server = NServer.create(name, getSkinParam());
			servers.put(name, server);
		}

		if (currentGroup() != null) {
			if (alreadyInSomeGroup(name))
				return CommandExecutionResult.error("Element already in another group.");

			currentGroup().addName(name);
			if (currentNetwork() == null) {
				server.updateProperties(props);
				return CommandExecutionResult.ok();
			}
		}

		if (networks.size() == 0 || currentNetwork() == null) {
			server.updateProperties(props);
			server.learnThisAddress(props.get("address"));
			return CommandExecutionResult.ok();

		}

		if (networks.size() == 0) {
			final Network network = createNetwork("");
			network.goInvisible();
			server.connectTo(network, props.get("address"));
			playField.addInPlayfield(server.getBar());
			server.doNotPrintFirstLink();
		} else {
			/*
			 * if (networks.size() == 1) server.connectTo(networks.get(0),
			 * props.get("address")); else
			 */
			if (currentNetwork() != null) {
				server.connectTo(currentNetwork(), props.get("address"));
				playField.addInPlayfield(server.getBar());
			} else {
				server.learnThisAddress(props.get("address"));
			}
		}
		server.updateProperties(props);
		return CommandExecutionResult.ok();
	}

	private boolean alreadyInSomeGroup(String name) {
		for (NwGroup g : groups)
			if (g.names().contains(name))
				return true;

		return false;
	}

	private CommandExecutionResult errorNoInit() {
		return CommandExecutionResult.error("Maybe you forget 'nwdiag {' in your diagram ?");
	}

	private Map<String, String> toSet(String definition) {
		final Map<String, String> result = new HashMap<String, String>();
		if (definition == null)
			return result;

		final Pattern p = Pattern.compile("\\s*(\\w+)\\s*=\\s*(\"([^\"]*)\"|[^\\s,]+)");
		final Matcher m = p.matcher(definition);
		while (m.find()) {
			final String name = m.group(1);
			final String value = m.group(3) == null ? m.group(2) : m.group(3);
			result.put(name, value);
		}
		return result;

	}

	@Override
	protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption)
			throws IOException {

		return createImageBuilder(fileFormatOption).drawable(getTextBlock()).write(os);
	}

	@Override
	protected TextBlock getTextBlock() {
		return new AbstractTextBlock() {
			public void drawU(UGraphic ug) {
				drawMe(ug);
			}

			public XDimension2D calculateDimension(StringBounder stringBounder) {
				return getTotalDimension(stringBounder);
			}

		};
	}

	private StyleSignatureBasic getStyleDefinitionNetwork(SName sname) {
		return StyleSignatureBasic.of(SName.root, SName.element, SName.nwdiagDiagram, sname);
	}

	private TextBlock toTextBlockForNetworkName(String name, String s) {
		if (s != null)
			name += "\\n" + s;

		final StyleBuilder styleBuilder = getSkinParam().getCurrentStyleBuilder();
		final Style style = getStyleDefinitionNetwork(SName.network).getMergedStyle(styleBuilder);
		final FontConfiguration fontConfiguration = style.getFontConfiguration(getSkinParam().getIHtmlColorSet());
		return Display.getWithNewlines(name).create(fontConfiguration, HorizontalAlignment.RIGHT,
				new SpriteContainerEmpty());
	}

	private XDimension2D getTotalDimension(StringBounder stringBounder) {
		return TextBlockUtils.getMinMax(new UDrawable() {
			public void drawU(UGraphic ug) {
				drawMe(ug);
			}
		}, stringBounder, true).getDimension();
	}

	private final double margin = 5;

	private void drawMe(UGraphic ug) {
		ug = ug.apply(new UTranslate(margin, margin));

		final StringBounder stringBounder = ug.getStringBounder();

		double deltaX = 0;
		double deltaY = 0;

		final GridTextBlockDecorated grid = buildGrid(stringBounder);

		for (int i = 0; i < networks.size(); i++) {
			final Network current = networks.get(i);
			final String address = current.getOwnAdress();
			final TextBlock desc = toTextBlockForNetworkName(current.getDisplayName(), address);
			final XDimension2D dim = desc.calculateDimension(stringBounder);
			if (i == 0)
				deltaY = (dim.getHeight() - GridTextBlockDecorated.NETWORK_THIN) / 2;

			deltaX = Math.max(deltaX, dim.getWidth());
		}
		double y = 0;
		for (int i = 0; i < networks.size(); i++) {
			final Network current = networks.get(i);
			final String address = current.getOwnAdress();
			final TextBlock desc = toTextBlockForNetworkName(current.getDisplayName(), address);
			final XDimension2D dim = desc.calculateDimension(stringBounder);
			desc.drawU(ug.apply(new UTranslate(deltaX - dim.getWidth(), y)));

			y += grid.lineHeight(stringBounder, i);
		}
		deltaX += 5;

		grid.drawU(ug.apply(new UTranslate(deltaX, deltaY)));
		final XDimension2D dimGrid = grid.calculateDimension(stringBounder);

		ug.apply(new UTranslate(dimGrid.getWidth() + deltaX + margin, dimGrid.getHeight() + deltaY + margin))
				.draw(new UEmpty(1, 1));

	}

	private Map<Network, String> getLinks(NServer element) {
		final Map<Network, String> result = new LinkedHashMap<>();
		for (Network network : networks) {
			final String s = element.getAdress(network);
			if (s != null)
				result.put(network, s);

		}
		return result;
	}

	private GridTextBlockDecorated buildGrid(StringBounder stringBounder) {

		playField.fixGroups(groups, servers.values());

		final GridTextBlockDecorated grid = new GridTextBlockDecorated(networks.size(), servers.size(), groups,
				networks, getSkinParam());

		final Map<NBar, Integer> layout = playField.doLayout();
		for (int i = 0; i < networks.size(); i++) {
			final Network current = networks.get(i);
			for (Map.Entry<String, NServer> ent : servers.entrySet()) {
				final NServer server = ent.getValue();
				if (server.getMainNetworkNext() == current) {
					final Map<Network, String> conns = getLinks(server);
					final Integer col = layout.get(server.getBar());
					if (col == null)
						continue;
					double topMargin = NServerDraw.MAGIC;
					NwGroup group = getGroupOf(server);
					if (group != null)
						topMargin += group.getTopHeaderHeight(stringBounder, getSkinParam());
					grid.add(i, col, server.getDraw(topMargin, conns, networks, getSkinParam()));
				}
			}
		}

		return grid;
	}

	private NwGroup getGroupOf(NServer server) {
		for (NwGroup group : groups)
			if (group.contains(server))
				return group;

		return null;
	}

	public CommandExecutionResult setProperty(String property, String value) {
		if (initDone == false)
			return errorNoInit();

		if ("address".equalsIgnoreCase(property) && currentNetwork() != null)
			currentNetwork().setOwnAdress(value);

		if ("width".equalsIgnoreCase(property) && currentNetwork() != null)
			currentNetwork().setFullWidth("full".equalsIgnoreCase(value));

		if ("color".equalsIgnoreCase(property)) {
			final HColor color = value == null ? null : getSkinParam().getIHtmlColorSet().getColorOrWhite(value);
			if (currentGroup() != null)
				currentGroup().setColor(color);
			else if (currentNetwork() != null)
				currentNetwork().setColor(color);

		}
		if ("description".equalsIgnoreCase(property))
			if (currentGroup() == null)
				currentNetwork().setDescription(value);
			else
				currentGroup().setDescription(value);

		return CommandExecutionResult.ok();
	}

	@Override
	public ClockwiseTopRightBottomLeft getDefaultMargins() {
		return ClockwiseTopRightBottomLeft.none();
	}
}

## 内容6
### 文件路径：
src/net/sourceforge/plantuml/nwdiag/core/NServer.java
### 代码内容：
package net.sourceforge.plantuml.nwdiag.core;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.sourceforge.plantuml.decoration.symbol.USymbol;
import net.sourceforge.plantuml.decoration.symbol.USymbols;
import net.sourceforge.plantuml.klimt.Fashion;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.klimt.creole.Display;
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.klimt.shape.TextBlockUtils;
import net.sourceforge.plantuml.nwdiag.next.NBar;
import net.sourceforge.plantuml.nwdiag.next.NServerDraw;
import net.sourceforge.plantuml.skin.ActorStyle;
import net.sourceforge.plantuml.skin.ComponentStyle;
import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleBuilder;
import net.sourceforge.plantuml.style.StyleSignatureBasic;
import net.sourceforge.plantuml.svek.PackageStyle;

public class NServer {

	private final Map<Network, String> connections = new LinkedHashMap<Network, String>();

	private USymbol shape = USymbols.RECTANGLE;
	private final String name;
	private String description;
	private String backcolor;
	private final NBar bar;
	private final ISkinParam skinParam;
	private String declaredAddress;

	private boolean printFirstLink = true;

	public void doNotPrintFirstLink() {
		this.printFirstLink = false;
	}

	public void connectMeIfAlone(Network network) {
		if (isAlone()) {
			connectTo(network, "");
			if (network.isVisible() == false)
				this.doNotPrintFirstLink();
		}
	}

	public boolean isAlone() {
		return connections.size() == 0;
	}

	public String someAddress() {
		if (connections.size() > 0 && connections.values().iterator().next().length() > 0)
			return connections.values().iterator().next();
		if (declaredAddress != null)
			return declaredAddress;
		return "";
	}

	public Network someNetwork() {
		if (connections.size() > 0)
			return connections.keySet().iterator().next();
		return null;
	}

	public void blankSomeAddress() {
		if (connections.size() > 0) {
			final Network it = connections.keySet().iterator().next();
			connections.put(it, "");
		}
	}

	public void learnThisAddress(String address) {
		for (Entry<Network, String> ent : connections.entrySet()) {
			if (ent.getValue().length() == 0) {
				connections.put(ent.getKey(), address);
				return;
			}
		}

	}

	public final boolean printFirstLink() {
		return printFirstLink;
	}

	public Network getMainNetworkNext() {
		return connections.keySet().iterator().next();
	}

	public String getAdress(Network network) {
		return connections.get(network);
	}

	public TextBlock toTextBlock(SName sname, String s) {
		if (s == null)
			return null;

		if (s.length() == 0)
			return TextBlockUtils.empty(0, 0);

		s = s.replace(", ", "\\n");
		return Display.getWithNewlines(s).create(getFontConfiguration(sname), HorizontalAlignment.LEFT, skinParam);
	}

	private StyleSignatureBasic getStyleDefinition(SName sname) {
		return StyleSignatureBasic.of(SName.root, SName.element, SName.nwdiagDiagram, sname);
	}

	private FontConfiguration getFontConfiguration(SName sname) {
		final StyleBuilder styleBuilder = skinParam.getCurrentStyleBuilder();
		final Style style = getStyleDefinition(sname).getMergedStyle(styleBuilder);
		return style.getFontConfiguration(skinParam.getIHtmlColorSet());
	}

	public NServerDraw getDraw(double topMargin, Map<Network, String> conns, List<Network> networks,
			ISkinParam skinParam) {
		final StyleBuilder styleBuilder = skinParam.getCurrentStyleBuilder();
		Fashion symbolContext = getStyleDefinition(SName.server).getMergedStyle(styleBuilder)
				.getSymbolContext(skinParam.getIHtmlColorSet());
		if (backcolor != null)
			try {
				final HColor back = skinParam.getIHtmlColorSet().getColor(backcolor);
				symbolContext = symbolContext.withBackColor(back);
			} catch (NoSuchColorException e) {
			}

		final TextBlock desc = toTextBlock(SName.server, getDescription());
		final TextBlock box = getShape().asSmall(TextBlockUtils.empty(0, 0), desc, TextBlockUtils.empty(0, 0),
				symbolContext, HorizontalAlignment.CENTER);
		return new NServerDraw(this, box, conns, networks, topMargin);
	}

	public void connectTo(Network network, String address) {
		if (network == null)
			throw new IllegalArgumentException();
		if (address == null)
			address = "";
		if (address.length() == 0 && connections.containsKey(network))
			return;

		connections.put(network, address);
		if (bar.getStart() == null)
			bar.addStage(network.getNstage());
		else if (this.getMainNetworkNext() != network)
			bar.addStage(network.getUp());
	}

	public void updateProperties(Map<String, String> props) {
		if (props.get("description") != null)
			this.description = props.get("description");

		if (props.get("color") != null)
			this.backcolor = props.get("color");

		if (props.get("address") != null)
			this.declaredAddress = props.get("address");

		final String shape = props.get("shape");
		if (shape != null) {
			final USymbol shapeFromString = USymbols.fromString(shape, ActorStyle.STICKMAN, ComponentStyle.RECTANGLE,
					PackageStyle.RECTANGLE);
			if (shapeFromString != null)
				this.shape = shapeFromString;
		}

	}

	@Override
	public final String toString() {
		return name;
	}

	public static NServer create(String name, ISkinParam skinParam) {
		return new NServer(name, new NBar(), skinParam);
	}

	public NServer(String name, NBar bar, ISkinParam skinParam) {
		this.description = name;
		this.name = name;
		this.bar = bar;
		this.skinParam = skinParam;
	}

	public final String getDescription() {
		return description;
	}

	public final String getName() {
		return name;
	}

	public final USymbol getShape() {
		return shape;
	}

	public final NBar getBar() {
		return bar;
	}

	public final ISkinParam getSkinParam() {
		return skinParam;
	}

}

## 内容7
### 文件路径：
src/net/sourceforge/plantuml/security/SURL.java
### 代码内容：
package net.sourceforge.plantuml.security;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HttpsURLConnection;
import javax.swing.ImageIcon;

import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.log.Logme;
import net.sourceforge.plantuml.security.authentication.SecurityAccessInterceptor;
import net.sourceforge.plantuml.security.authentication.SecurityAuthentication;
import net.sourceforge.plantuml.security.authentication.SecurityCredentials;
//::uncomment when __CORE__
//import net.sourceforge.plantuml.FileUtils;
//::done

/**
 * Secure replacement for java.net.URL.
 * <p>
 * This class should be used instead of java.net.URL.
 * <p>
 * This class does some control access and manages access-tokens via URL. If a
 * URL contains a access-token, similar to a user prefix, SURL loads the
 * authorization config for this user-token and passes the credentials to the
 * host.
 * <p>
 * Example:<br/>
 *
 * <pre>
 *     SURL url = SURL.create ("https://jenkins-access@jenkins.mycompany.com/api/json")
 * </pre>
 *
 * The {@code jenkins-access} will checked against the Security context access
 * token configuration. If a configuration exists for this token name, the token
 * will be removed from the URL and the credentials will be added to the
 * headers. If the token is not found, the URL remains as it is and no separate
 * authentication will be performed.
 * <p>
 * TODO: Some methods should be moved to a HttpClient implementation, because
 * SURL is not the valid class to manage it. <br/>
 * TODO: BAD_HOSTS implementation should be reviewed and moved to HttpClient
 * implementation with a circuit-breaker. <br/>
 * TODO: Token expiration with refresh should be implemented in future. <br/>
 */
public class SURL {

	/**
	 * Indicates, that we have no authentication to access the URL.
	 */
	public static final String WITHOUT_AUTHENTICATION = SecurityUtils.NO_CREDENTIALS;

	/**
	 * Internal URL, maybe cleaned from user-token.
	 */
	private final URL internal;

	/**
	 * Assigned credentials to this URL.
	 */
	private final String securityIdentifier;

	private SURL(URL url, String securityIdentifier) {
		this.internal = Objects.requireNonNull(url);
		this.securityIdentifier = Objects.requireNonNull(securityIdentifier);
	}

	/**
	 * Create a secure URL from a String.
	 * <p>
	 * The url must be http or https. Return null in case of error or if
	 * <code>url</code> is null
	 *
	 * @param url plain url starting by http:// or https//
	 * @return the secure URL or null
	 */
	public static SURL create(String url) {
		if (url == null)
			return null;

		if (url.startsWith("http://") || url.startsWith("https://"))
			try {
				return create(new URL(url));
			} catch (MalformedURLException e) {
				Logme.error(e);
			}
		return null;
	}

	/**
	 * Create a secure URL from a <code>java.net.URL</code> object.
	 * <p>
	 * It takes into account credentials.
	 *
	 * @param url
	 * @return the secure URL
	 * @throws MalformedURLException if <code>url</code> is null
	 */
	public static SURL create(URL url) throws MalformedURLException {
		if (url == null)
			throw new MalformedURLException("URL cannot be null");

		// ::comment when __CORE__
		final String credentialId = url.getUserInfo();

		if (credentialId == null || credentialId.indexOf(':') > 0)
			// No user info at all, or a user with password (This is a legacy BasicAuth
			// access, and we bypass it):
			return new SURL(url, WITHOUT_AUTHENTICATION);
		else if (SecurityUtils.existsSecurityCredentials(credentialId))
			// Given userInfo, but without a password. We try to find SecurityCredentials
			return new SURL(removeUserInfo(url), credentialId);
		else
			// ::done
			return new SURL(url, WITHOUT_AUTHENTICATION);
	}

	// ::uncomment when __CORE__
//	public InputStream openStream() {
//	try {
//		return internal.openStream();
//	} catch (IOException e) {
//		System.err.println("SURL::openStream " + e);
//		return null;
//	}
//}
//public byte[] getBytes() {
//	final InputStream is = openStream();
//	if (is != null)
//		try {
//			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
//			FileUtils.copyInternal(is, baos, true);
//			return baos.toByteArray();
//		} catch (IOException e) {
//			System.err.println("SURL::getBytes " + e);
//		}
//	return null;
//}
	// ::done

	public BufferedImage readRasterImageFromURL() {
		if (isUrlOk())
			try {
				final byte[] bytes = getBytes();
				if (bytes == null || bytes.length == 0)
					return null;
				final ImageIcon tmp = new ImageIcon(bytes);
				return SecurityUtils.readRasterImage(tmp);
			} catch (Exception e) {
				Logme.error(e);
			}
		return null;
	}

	/**
	 * Check SecurityProfile to see if this URL can be opened.
	 */
	private boolean isUrlOk() {
		// ::comment when __CORE__
		if (SecurityUtils.getSecurityProfile() == SecurityProfile.SANDBOX)
			// In SANDBOX, we cannot read any URL
			return false;

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.LEGACY)
			return true;

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE)
			// We are UNSECURE anyway
			return true;

		if (isInUrlAllowList())
			// ::done
			return true;
		// ::comment when __CORE__

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.INTERNET) {
			if (forbiddenURL(cleanPath(internal.toString())))
				return false;

			final int port = internal.getPort();
			// Using INTERNET profile, port 80 and 443 are ok
			return port == 80 || port == 443 || port == -1;
		}
		return false;
		// ::done
	}

	// ::comment when __CORE__
	/**
	 * Regex to remove the UserInfo part from a URL.
	 */
	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) {
			final Thread t = Executors.defaultThreadFactory().newThread(r);
			t.setDaemon(true);
			return t;
		}
	});

	private static final Map<String, Long> BAD_HOSTS = new ConcurrentHashMap<String, Long>();

	/**
	 * Creates a URL without UserInfo part and without SecurityCredentials.
	 *
	 * @param url plain URL
	 * @return SURL without any user credential information.
	 * @throws MalformedURLException
	 */
	static SURL createWithoutUser(URL url) throws MalformedURLException {
		return new SURL(removeUserInfo(url), WITHOUT_AUTHENTICATION);
	}

	/**
	 * Clears the bad hosts cache.
	 * <p>
	 * In some test cases (and maybe also needed for other functionality) the bad
	 * hosts cache must be cleared.<br/>
	 * E.g., in a test we check the failure on missing credentials and then a test
	 * with existing credentials. With a bad host cache the second test will fail,
	 * or we have unpredicted results.
	 */
	static void resetBadHosts() {
		BAD_HOSTS.clear();
	}

	@Override
	public String toString() {
		return internal.toString();
	}

	private boolean forbiddenURL(String full) {
		if (full.startsWith("https://") == false && full.startsWith("http://") == false)
			return true;
		if (full.matches("^https?://[-#.0-9:\\[\\]+]+/.*"))
			return true;
		if (full.matches("^https?://[^.]+/.*"))
			return true;
		if (full.matches("^https?://[^.]+$"))
			return true;
		return false;
	}

	private boolean isInUrlAllowList() {
		final String full = cleanPath(internal.toString());
		for (String allow : getUrlAllowList())
			if (full.startsWith(cleanPath(allow)))
				return true;

		return false;
	}

	private String cleanPath(String path) {
		// Remove user information, because we don't like to store user/password or
		// userTokens in allow-list
		path = removeUserInfoFromUrlPath(path);
		path = path.trim().toLowerCase(Locale.US);
		// We simplify/normalize the url, removing default ports
		path = path.replace(":80/", "");
		path = path.replace(":443/", "");
		return path;
	}

	private List<String> getUrlAllowList() {
		final String env = SecurityUtils.getenv(SecurityUtils.ALLOWLIST_URL);
		if (env == null)
			return Collections.emptyList();

		return Arrays.asList(StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(env).split(";"));
	}

	/**
	 * Reads from an endpoint (with configured credentials and proxy) the response
	 * as blob.
	 * <p>
	 * This method allows access to an endpoint, with a configured
	 * SecurityCredentials object. The credentials will load on the fly and
	 * authentication fetched from an authentication-manager. Caching of tokens is
	 * not supported.
	 * <p>
	 * authors: Alain Corbiere, Aljoscha Rittner
	 *
	 * @return data loaded data from endpoint
	 */
	public byte[] getBytes() {
		if (isUrlOk() == false)
			return null;

		final SecurityCredentials credentials = SecurityUtils.loadSecurityCredentials(securityIdentifier);
		final SecurityAuthentication authentication = SecurityUtils.getAuthenticationManager(credentials)
				.create(credentials);
		try {
			final String host = internal.getHost();
			final Long bad = BAD_HOSTS.get(host);
			if (bad != null) {
				if ((System.currentTimeMillis() - bad) < 1000L * 60)
					return null;
				BAD_HOSTS.remove(host);
			}

			try {
				final Future<byte[]> result = EXE
						.submit(requestWithGetAndResponse(internal, credentials.getProxy(), authentication, null));
				final byte[] data = result.get(SecurityUtils.getSecurityProfile().getTimeout(), TimeUnit.MILLISECONDS);
				if (data != null)
					return data;

			} catch (Exception e) {
				System.err.println("issue " + host + " " + e);
			}

			BAD_HOSTS.put(host, System.currentTimeMillis());
			return null;
		} finally {
			// clean up. We don't cache tokens, no expire handling. All time a re-request.
			credentials.eraseCredentials();
			authentication.eraseCredentials();
		}
	}

	/**
	 * Reads from an endpoint with a given authentication and proxy the response as
	 * blob.
	 * <p>
	 * This method allows a parametrized access to an endpoint, without a configured
	 * SecurityCredentials object. This is useful to access internally identity
	 * providers (IDP), or authorization servers (to request access tokens).
	 * <p>
	 * This method don't use the "bad-host" functionality, because the access to
	 * infrastructure services should not be obfuscated by some internal management.
	 * <p>
	 * <strong>Please don't use this method directly from DSL scripts.</strong>
	 *
	 * @param authentication authentication object data. Caller is responsible to
	 *                       erase credentials
	 * @param proxy          proxy configuration
	 * @param headers        additional headers, if needed
	 * @return loaded data from endpoint
	 */
	private byte[] getBytes(Proxy proxy, SecurityAuthentication authentication, Map<String, Object> headers) {
		if (isUrlOk() == false)
			return null;

		final Future<byte[]> result = EXE.submit(requestWithGetAndResponse(internal, proxy, authentication, headers));

		try {
			return result.get(SecurityUtils.getSecurityProfile().getTimeout(), TimeUnit.MILLISECONDS);
		} catch (Exception e) {
			System.err.println("SURL response issue to " + internal.getHost() + " " + e);
			return null;
		}
	}

	/**
	 * Post to an endpoint with a given authentication and proxy the response as
	 * blob.
	 * <p>
	 * This method allows a parametrized access to an endpoint, without a configured
	 * SecurityCredentials object. This is useful to access internally identity
	 * providers (IDP), or authorization servers (to request access tokens).
	 * <p>
	 * This method don't use the "bad-host" functionality, because the access to
	 * infrastructure services should not be obfuscated by some internal management.
	 * <p>
	 * <strong>Please don't use this method directly from DSL scripts.</strong>
	 *
	 * @param authentication authentication object data. Caller is responsible to
	 *                       erase credentials
	 * @param proxy          proxy configuration
	 * @param data           content to post
	 * @param headers        headers, if needed
	 * @return loaded data from endpoint
	 */
	public byte[] getBytesOnPost(Proxy proxy, SecurityAuthentication authentication, String data,
			Map<String, Object> headers) {
		if (isUrlOk() == false)
			return null;

		final Future<byte[]> result = EXE
				.submit(requestWithPostAndResponse(internal, proxy, authentication, data, headers));

		try {
			return result.get(SecurityUtils.getSecurityProfile().getTimeout(), TimeUnit.MILLISECONDS);
		} catch (Exception e) {
			System.err.println("SURL response issue to " + internal.getHost() + " " + e);
			return null;
		}
	}

	/**
	 * Creates a GET request and response handler
	 *
	 * @param url            URL to request
	 * @param proxy          proxy to apply
	 * @param authentication the authentication to use
	 * @param headers        additional headers, if needed
	 * @return the callable handler.
	 */
	private static Callable<byte[]> requestWithGetAndResponse(final URL url, final Proxy proxy,
			final SecurityAuthentication authentication, final Map<String, Object> headers) {
		return new Callable<byte[]>() {

			private HttpURLConnection openConnection(final URL url) throws IOException {
				// Add proxy, if passed throw parameters
				final URLConnection connection = proxy == null ? url.openConnection() : url.openConnection(proxy);
				if (connection == null)
					return null;

				final HttpURLConnection http = (HttpURLConnection) connection;

				applyEndpointAccessAuthentication(http, authentication);
				applyAdditionalHeaders(http, headers);
				return http;
			}

			public byte[] call() throws IOException {
				HttpURLConnection http = openConnection(url);
				final int responseCode = http.getResponseCode();

				if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP
						|| responseCode == HttpURLConnection.HTTP_MOVED_PERM) {
					final String newUrl = http.getHeaderField("Location");
					http = openConnection(new URL(newUrl));
				}

				return retrieveResponseAsBytes(http);
			}
		};
	}

	/**
	 * Creates a POST request and response handler with a simple String content. The
	 * content will be identified as form or JSON data. The charset encoding can be
	 * set by header parameters or will be set to UTF-8. The method to some fancy
	 * logic to simplify it for the user.
	 *
	 * @param url            URL to request via POST method
	 * @param proxy          proxy to apply
	 * @param authentication the authentication to use
	 * @param headers        additional headers, if needed
	 * @return the callable handler.
	 */
	private static Callable<byte[]> requestWithPostAndResponse(final URL url, final Proxy proxy,
			final SecurityAuthentication authentication, final String data, final Map<String, Object> headers) {
		return new Callable<byte[]>() {
			public byte[] call() throws IOException {
				// Add proxy, if passed throw parameters
				final URLConnection connection = proxy == null ? url.openConnection() : url.openConnection(proxy);
				if (connection == null)
					return null;

				final boolean withContent = StringUtils.isNotEmpty(data);

				final HttpURLConnection http = (HttpURLConnection) connection;
				http.setRequestMethod("POST");
				if (withContent)
					http.setDoOutput(true);

				applyEndpointAccessAuthentication(http, authentication);
				applyAdditionalHeaders(http, headers);

				final Charset charSet = extractCharset(http.getRequestProperty("Content-Type"));

				if (withContent)
					sendRequestAsBytes(http, data.getBytes(charSet != null ? charSet : StandardCharsets.UTF_8));

				return retrieveResponseAsBytes(http);
			}
		};
	}

	private static Charset extractCharset(String contentType) {
		if (StringUtils.isEmpty(contentType))
			return null;

		final Matcher matcher = Pattern.compile("(?i)\\bcharset=\\s*\"?([^\\s;\"]*)").matcher(contentType);
		if (matcher.find())
			try {
				return Charset.forName(matcher.group(1));
			} catch (Exception e) {
				Logme.error(e);
			}

		return null;
	}

	/**
	 * Loads a response from an endpoint as a byte[] array.
	 *
	 * @param connection the URL connection
	 * @return the loaded byte arrays
	 * @throws IOException an exception, if the connection cannot establish or the
	 *                     download was broken
	 */
	private static byte[] retrieveResponseAsBytes(HttpURLConnection connection) throws IOException {
		final int responseCode = connection.getResponseCode();
		if (responseCode < HttpURLConnection.HTTP_BAD_REQUEST) {
			try (InputStream input = connection.getInputStream()) {
				return retrieveData(input);
			}
		} else {
			try (InputStream error = connection.getErrorStream()) {
				final byte[] bytes = retrieveData(error);
				throw new IOException(
						"HTTP error " + responseCode + " with " + new String(bytes, StandardCharsets.UTF_8));
			}
		}
	}

	/**
	 * Reads data in a byte[] array.
	 *
	 * @param input input stream
	 * @return byte data
	 * @throws IOException if something went wrong
	 */
	private static byte[] retrieveData(InputStream input) throws IOException {
		final ByteArrayOutputStream out = new ByteArrayOutputStream();
		final byte[] buffer = new byte[1024];
		int read;
		while ((read = input.read(buffer)) > 0) {
			out.write(buffer, 0, read);
		}
		out.close();
		return out.toByteArray();
	}

	/**
	 * Sends a request content payload to an endpoint.
	 *
	 * @param connection HTTP connection
	 * @param data       data as byte array
	 * @throws IOException if something went wrong
	 */
	private static void sendRequestAsBytes(HttpURLConnection connection, byte[] data) throws IOException {
		connection.setFixedLengthStreamingMode(data.length);
		try (OutputStream os = connection.getOutputStream()) {
			os.write(data);
		}
	}

	public InputStream openStream() {
		if (isUrlOk()) {
			final byte[] data = getBytes();
			if (data != null)
				return new ByteArrayInputStream(data);

		}
		return null;
	}

	/**
	 * Informs, if SecurityCredentials are configured for this connection.
	 *
	 * @return true, if credentials will be used for a connection
	 */
	public boolean isAuthorizationConfigured() {
		return WITHOUT_AUTHENTICATION.equals(securityIdentifier) == false;
	}

	/**
	 * Applies the given authentication data to the http connection.
	 *
	 * @param http           HTTP URL connection (must be an encrypted https-TLS/SSL
	 *                       connection, or http must be activated with a property)
	 * @param authentication the data to request the access
	 * @see SecurityUtils#getAccessInterceptor(SecurityAuthentication)
	 * @see SecurityUtils#isNonSSLAuthenticationAllowed()
	 */
	private static void applyEndpointAccessAuthentication(URLConnection http, SecurityAuthentication authentication) {
		if (authentication.isPublic())
			// Shortcut: No need to apply authentication.
			return;

		if (http instanceof HttpsURLConnection || SecurityUtils.isNonSSLAuthenticationAllowed()) {
			SecurityAccessInterceptor accessInterceptor = SecurityUtils.getAccessInterceptor(authentication);
			accessInterceptor.apply(authentication, http);
		} else {
			// We cannot allow applying secret tokens on plain connections. Everyone can
			// read the data.
			throw new IllegalStateException(
					"The transport of authentication data over an unencrypted http connection is not allowed");
		}
	}

	/**
	 * Set the headers for a URL connection
	 *
	 * @param headers map Keys with values (can be String or list of String)
	 */
	private static void applyAdditionalHeaders(URLConnection http, Map<String, Object> headers) {
		if (headers == null || headers.isEmpty())
			return;

		for (Map.Entry<String, Object> header : headers.entrySet()) {
			final Object value = header.getValue();
			if (value instanceof String)
				http.setRequestProperty(header.getKey(), (String) value);
			else if (value instanceof List)
				for (Object item : (List<?>) value)
					if (item != null)
						http.addRequestProperty(header.getKey(), item.toString());

		}
	}

	/**
	 * Removes the userInfo part from the URL, because we want to use the
	 * SecurityCredentials instead.
	 *
	 * @param url URL with UserInfo part
	 * @return url without UserInfo part
	 * @throws MalformedURLException
	 */
	private static URL removeUserInfo(URL url) throws MalformedURLException {
		return new URL(removeUserInfoFromUrlPath(url.toExternalForm()));
	}

	/**
	 * Removes the userInfo part from the URL, because we want to use the
	 * SecurityCredentials instead.
	 *
	 * @param url URL with UserInfo part
	 * @return url without UserInfo part
	 */
	private static String removeUserInfoFromUrlPath(String url) {
		// Simple solution:
		final Matcher matcher = PATTERN_USERINFO.matcher(url);
		if (matcher.find())
			return matcher.replaceFirst("$1$3");

		return url;
	}
	// ::done
}

## 内容8
### 文件路径：
src/net/sourceforge/plantuml/tim/TFunctionImpl.java
### 代码内容：
package net.sourceforge.plantuml.tim;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sourceforge.plantuml.text.StringLocated;
import net.sourceforge.plantuml.text.TLineType;
import net.sourceforge.plantuml.tim.expression.TValue;
import net.sourceforge.plantuml.utils.LineLocation;

public class TFunctionImpl implements TFunction {

	private final TFunctionSignature signature;
	private final List<TFunctionArgument> args;
	private final List<StringLocated> body = new ArrayList<>();
	private final boolean unquoted;
	private /* final */ TFunctionType functionType;// = TFunctionType.VOID;
	private String legacyDefinition;
	private boolean containsReturn;

	public TFunctionImpl(String functionName, List<TFunctionArgument> args, boolean unquoted,
			TFunctionType functionType) {
		final Set<String> names = new HashSet<>();
		for (TFunctionArgument tmp : args) {
			names.add(tmp.getName());
		}
		this.signature = new TFunctionSignature(functionName, args.size(), names);
		this.args = args;
		this.unquoted = unquoted;
		this.functionType = functionType;
	}

	public boolean canCover(int nbArg, Set<String> namedArguments) {
		for (String n : namedArguments) {
			if (signature.getNamedArguments().contains(n) == false) {
				return false;
			}
		}
		if (nbArg > args.size()) {
			return false;
		}
		assert nbArg <= args.size();
		int neededArgument = 0;
		for (TFunctionArgument arg : args) {
			if (namedArguments.contains(arg.getName())) {
				continue;
			}
			if (arg.getOptionalDefaultValue() == null) {
				neededArgument++;
			}
		}
		if (nbArg < neededArgument) {
			return false;
		}
		assert nbArg >= neededArgument;
		return true;
	}

	private TMemory getNewMemory(TMemory memory, List<TValue> values, Map<String, TValue> namedArguments) {
		final Map<String, TValue> result = new HashMap<String, TValue>();
		int ivalue = 0;
		for (TFunctionArgument arg : args) {
			final TValue value;
			if (namedArguments.containsKey(arg.getName())) {
				value = namedArguments.get(arg.getName());
			} else if (ivalue < values.size()) {
				value = values.get(ivalue);
				ivalue++;
			} else {
				value = arg.getOptionalDefaultValue();
			}
			if (value == null) {
				throw new IllegalStateException();
			}
			result.put(arg.getName(), value);
		}
		return memory.forkFromGlobal(result);
	}

	@Override
	public String toString() {
		return "FUNCTION " + signature + " " + args;
	}

	public void addBody(StringLocated s) throws EaterExceptionLocated {
		body.add(s);
		if (s.getType() == TLineType.RETURN) {
			this.containsReturn = true;
			if (functionType == TFunctionType.PROCEDURE) {
				throw EaterExceptionLocated
						.located("A procedure cannot have !return directive. Declare it as a function instead ?", s);
				// this.functionType = TFunctionType.RETURN;
			}
		}
	}

	public void executeProcedure(TContext context, TMemory memory, LineLocation location, String s)
			throws EaterException, EaterExceptionLocated {
		final EaterFunctionCall call = new EaterFunctionCall(new StringLocated(s, location),
				context.isLegacyDefine(signature.getFunctionName()), unquoted);
		call.analyze(context, memory);
		final String endOfLine = call.getEndOfLine();
		final List<TValue> args = call.getValues();
		final Map<String, TValue> named = call.getNamedArguments();
		executeProcedureInternal(context, memory, args, named);
		context.appendEndOfLine(endOfLine);
	}

	public void executeProcedureInternal(TContext context, TMemory memory, List<TValue> args, Map<String, TValue> named)
			throws EaterException, EaterExceptionLocated {
		if (functionType != TFunctionType.PROCEDURE && functionType != TFunctionType.LEGACY_DEFINELONG) {
			throw new IllegalStateException();
		}
		final TMemory copy = getNewMemory(memory, args, named);
		context.executeLines(copy, body, TFunctionType.PROCEDURE, false);
	}

	public TValue executeReturnFunction(TContext context, TMemory memory, LineLocation location, List<TValue> args,
			Map<String, TValue> named) throws EaterException, EaterExceptionLocated {
		if (functionType == TFunctionType.LEGACY_DEFINE) {
			return executeReturnLegacyDefine(location, context, memory, args);
		}
		if (functionType != TFunctionType.RETURN_FUNCTION) {
			throw EaterException.unlocated("Illegal call here. Is there a return directive in your function?");
		}
		final TMemory copy = getNewMemory(memory, args, named);
		final TValue result = context.executeLines(copy, body, TFunctionType.RETURN_FUNCTION, true);
		if (result == null) {
			throw EaterException.unlocated("No return directive found in your function");
		}
		return result;
	}

	private TValue executeReturnLegacyDefine(LineLocation location, TContext context, TMemory memory, List<TValue> args)
			throws EaterException, EaterExceptionLocated {
		if (legacyDefinition == null) {
			throw new IllegalStateException();
		}
		final TMemory copy = getNewMemory(memory, args, Collections.<String, TValue>emptyMap());
		final String tmp = context.applyFunctionsAndVariables(copy, location, legacyDefinition);
		if (tmp == null) {
			return TValue.fromString("");
		}
		return TValue.fromString(tmp);
		// eaterReturn.execute(context, copy);
		// // System.err.println("s3=" + eaterReturn.getValue2());
		// return eaterReturn.getValue2();
	}

	public final TFunctionType getFunctionType() {
		return functionType;
	}

	public final TFunctionSignature getSignature() {
		return signature;
	}

//	public void setFunctionType(TFunctionType type) {
//		this.functionType = type;
//	}

	public void setLegacyDefinition(String legacyDefinition) {
		this.legacyDefinition = legacyDefinition;
	}

	public boolean isUnquoted() {
		return unquoted;
	}

	public boolean hasBody() {
		return body.size() > 0;
	}

	public void finalizeEnddefinelong() {
		if (functionType != TFunctionType.LEGACY_DEFINELONG) {
			throw new UnsupportedOperationException();
		}
		if (body.size() == 1) {
			this.functionType = TFunctionType.LEGACY_DEFINE;
			this.legacyDefinition = body.get(0).getString();
		}
	}

	public final boolean doesContainReturn() {
		return containsReturn;
	}

}

## 内容9
### 文件路径：
src/net/sourceforge/plantuml/version/LicenseInfo.java
### 代码内容：
package net.sourceforge.plantuml.version;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.TreeSet;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.log.Logme;
import net.sourceforge.plantuml.security.SFile;
import net.sourceforge.plantuml.security.SImageIO;
import net.sourceforge.plantuml.utils.Log;
import net.sourceforge.plantuml.utils.SignatureUtils;

public class LicenseInfo {

	public static synchronized LicenseInfo retrieveQuick() {
		// ::revert when __CORE__
		if (cache == null)
			cache = retrieveDistributor();

		if (cache == null)
			cache = retrieveNamedSlow();
		return cache;
		// return new LicenseInfo();
		// ::done
	}

	public boolean isValid() {
		// ::revert when __CORE__
		return owner != null && System.currentTimeMillis() <= this.expirationDate;
		// return false;
		// ::done
	}

	// ::comment when __CORE__
	private static LicenseInfo cache;

	private final static Preferences prefs = Preferences.userNodeForPackage(LicenseInfo.class);
	public final static LicenseInfo NONE = new LicenseInfo(LicenseType.NONE, 0, 0, null, null, null);

	private final LicenseType type;
	private final long generationDate;
	private final long expirationDate;
	private final String owner;
	private final String context;
	private final byte[] sha;

	public LicenseInfo(LicenseType type, long generationDate, long expirationDate, String owner, String context,
			byte[] sha) {
		this.type = type;
		this.generationDate = generationDate;
		this.expirationDate = expirationDate;
		this.owner = owner;
		this.context = context;
		this.sha = sha;
	}

	public static void persistMe(String key) throws BackingStoreException {
		prefs.sync();
		prefs.put("license", key);
	}

	public static boolean retrieveNamedOrDistributorQuickIsValid() {
		return retrieveQuick().isValid();
	}

	public static synchronized LicenseInfo retrieveNamedSlow() {
		cache = LicenseInfo.NONE;
		if (OptionFlags.ALLOW_INCLUDE == false) {
			return cache;
		}
		final String key = prefs.get("license", "");
		if (key.length() > 0) {
			cache = setIfValid(retrieveNamed(key), cache);
			if (cache.isValid()) {
				return cache;
			}
		}
		for (SFile f : fileCandidates()) {
			try {
				if (f.exists() && f.canRead()) {
					final LicenseInfo result = retrieve(f);
					if (result == null) {
						return null;
					}
					cache = setIfValid(result, cache);
					if (cache.isValid()) {
						return cache;
					}
				}
			} catch (IOException e) {
				Log.info("Error " + e);
				// Logme.error(e);
			}
		}
		return cache;
	}

	public static LicenseInfo retrieveNamed(final String key) {
		if (key.length() > 99 && key.matches("^[0-9a-z]+$")) {
			try {
				final String sig = SignatureUtils.toHexString(PLSSignature.signature());
				return PLSSignature.retrieveNamed(sig, key, true);
			} catch (Exception e) {
				// Logme.error(e);
				Log.info("Error retrieving license info" + e);
			}
		}
		return LicenseInfo.NONE;
	}

	public static BufferedImage retrieveDistributorImage(LicenseInfo licenseInfo) {
		if (licenseInfo.getLicenseType() != LicenseType.DISTRIBUTOR) {
			return null;
		}
		try {
			final byte[] s1 = PLSSignature.retrieveDistributorImageSignature();
			if (SignatureUtils.toHexString(s1).equals(SignatureUtils.toHexString(licenseInfo.sha)) == false) {
				return null;
			}
			final InputStream dis = PSystemVersion.class.getResourceAsStream("/distributor.png");
			if (dis == null) {
				return null;
			}
			try {
				final BufferedImage result = SImageIO.read(dis);
				return result;
			} finally {
				dis.close();
			}
		} catch (Exception e) {
			Logme.error(e);
		}
		return null;
	}

	public static LicenseInfo retrieveDistributor() {
		final InputStream dis = PSystemVersion.class.getResourceAsStream("/distributor.txt");
		if (dis == null) {
			return null;
		}
		try {
			final BufferedReader br = new BufferedReader(new InputStreamReader(dis));
			final String licenseString = br.readLine();
			br.close();
			final LicenseInfo result = PLSSignature.retrieveDistributor(licenseString);
			final Throwable creationPoint = new Throwable();
			creationPoint.fillInStackTrace();
			for (StackTraceElement ste : creationPoint.getStackTrace()) {
				if (ste.toString().contains(result.context)) {
					return result;
				}
			}
			return null;
		} catch (Exception e) {
			Logme.error(e);
			return null;
		}
	}

	public static Collection<SFile> fileCandidates() {
		final Set<SFile> result = new TreeSet<>();
		final String classpath = System.getProperty("java.class.path");
		String[] classpathEntries = classpath.split(SFile.pathSeparator);
		for (String s : classpathEntries) {
			if (s == null)
				continue;
			SFile dir = new SFile(s);
			if (dir.isFile()) {
				dir = dir.getParentFile();
			}
			if (dir != null && dir.isDirectory()) {
				result.add(dir.file("license.txt"));
			}
		}
		return result;
	}

	private static LicenseInfo setIfValid(LicenseInfo value, LicenseInfo def) {
		if (value.isValid() || def.isNone()) {
			return value;
		}
		return def;
	}

	private static LicenseInfo retrieve(SFile f) throws IOException {
		final BufferedReader br = f.openBufferedReader();
		if (br == null) {
			return null;
		}
		try {
			final String s = br.readLine();
			final LicenseInfo result = retrieveNamed(s);
			if (result != null) {
				Log.info("Reading license from " + f.getAbsolutePath());
			}
			return result;
		} finally {
			br.close();
		}
	}

	public static void main(String[] args) {
		LicenseInfo info = retrieveNamedSlow();
		System.err.println("valid=" + info.isValid());
		System.err.println("info=" + info.owner);

	}

	public final Date getGenerationDate() {
		return new Date(generationDate);
	}

	public final Date getExpirationDate() {
		return new Date(expirationDate);
	}

	public final String getOwner() {
		return owner;
	}

	public boolean isNone() {
		return owner == null;
	}

	public boolean hasExpired() {
		return owner != null && System.currentTimeMillis() > this.expirationDate;
	}

	public final LicenseType getLicenseType() {
		return type;
	}

	public final String getContext() {
		return context;
	}
	// ::done

}

## 内容10
### 文件路径：
src/net/sourceforge/plantuml/version/Version.java
### 代码内容：
package net.sourceforge.plantuml.version;

import java.util.Date;

import net.sourceforge.plantuml.log.Logme;
import net.sourceforge.plantuml.security.SURL;
import net.sourceforge.plantuml.utils.SignatureUtils;

public class Version {
	// ::remove folder when __HAXE__

	// Warning, "version" should be the same in gradle.properties and Version.java
	// Any idea anyone how to magically synchronize those :-) ?
	private static final String version = "1.2023.9beta3";

	public static String versionString() {
		return version;
	}

	public static String fullDescription() {
		return "PlantUML version " + Version.versionString() + " (" + Version.compileTimeString() + ")";
	}

	public static String versionString(int size) {
		final StringBuilder sb = new StringBuilder(versionString());
		while (sb.length() < size)
			sb.append(' ');

		return sb.toString();
	}

	public static int beta() {
		final int x = version.indexOf("beta");
		if (x == -1)
			return 0;
		return Integer.parseInt(version.substring(x + "beta".length()));
	}

	public static String etag() {
		return SignatureUtils.getMD5Hex(version);
	}

	public static String turningId() {
		return etag();
	}

	public static long compileTime() {
		return 1684658280620L;
	}

	public static String compileTimeString() {
		if (version.contains("beta"))
			return "Unknown compile time";

		return new Date(Version.compileTime()).toString();
	}

	// ::comment when __CORE__
	public static String getJarPath() {
		try {
			final ClassLoader loader = Version.class.getClassLoader();
			if (loader == null) {
				return "No ClassLoader?";
			}
			final SURL url = SURL.create(loader.getResource("net/sourceforge/plantuml/version/Version.class"));
			if (url == null) {
				return "No URL?";
			}
			String fullpath = url.toString();
			fullpath = fullpath.replaceAll("net/sourceforge/plantuml/version/Version\\.class", "");
			return fullpath;
		} catch (Throwable t) {
			Logme.error(t);
			return t.toString();
		}
	}
	// ::done

}

Output:
【漏洞类型】路径遍历漏洞
【严重性】Medium
【漏洞摘要】路径遍历漏洞允许攻击者通过构造特定的输入路径来访问或操作服务器上的任意文件。在`FilesListing`类的`add`方法中，未对用户输入进行验证，直接将用户输入拼接到文件路径中，导致潜在的路径遍历攻击。
【关键代码模式】未验证的用户输入拼接文件路径，涉及的函数或关键词包括：`add`方法，`String`拼接，`SFile`。
【修复方法说明】通过创建`FilesEntry`类，将用户输入存储在内存中，并使用`FilesEntry`类来构建和绘制文件列表，避免直接拼接文件路径，从而防止路径遍历攻击。