mirror of
https://github.com/octoleo/plantuml-server.git
synced 2024-12-22 00:38:54 +00:00
add security features + java property support
- set `ALLOW_PLANTUML_INCLUDE` only once and decentralized inside the `DiagramResponse` class and call this init method after initializing the server - set `PLANTUML_SECURITY_PROFILE` to `INTERNET` by default (BREAKING CHANGES) - add possibility to set PlantUML system properties over a file with `PLANTUML_PROPERTY_FILE` - adjust documentation - add "Breaking changes" hint to README
This commit is contained in:
parent
5fa6cbc82f
commit
09a7ce4973
15
README.md
15
README.md
@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
PlantUML Server is a web application to generate UML diagrams on-the-fly.
|
PlantUML Server is a web application to generate UML diagrams on-the-fly.
|
||||||
|
|
||||||
[PlantUML is **not** affected by the log4j vulnerability.](https://github.com/plantuml/plantuml/issues/826)
|
> [PlantUML is **not** affected by the log4j vulnerability.](https://github.com/plantuml/plantuml/issues/826)
|
||||||
|
|
||||||
|
> **Breaking changes**:
|
||||||
|
> PlantUML Server sets `PLANTUML_SECURITY_PROFILE` to `INTERNET` by default starting with version `v1.2023.9`.
|
||||||
|
> You can change its behavior back to work like before if you set the environment variable `PLANTUML_SECURITY_PROFILE` to `LEGACY`.
|
||||||
|
> But before you do that, please take a look to [PlantUMLs Security](https://plantuml.com/security) page.
|
||||||
|
|
||||||
|
|
||||||
![PlantUML Server](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/screenshot.png)
|
![PlantUML Server](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/screenshot.png)
|
||||||
@ -119,6 +124,7 @@ You can set all the following variables:
|
|||||||
* Default value: `ROOT`
|
* Default value: `ROOT`
|
||||||
* `PLANTUML_CONFIG_FILE`
|
* `PLANTUML_CONFIG_FILE`
|
||||||
* Local path to a PlantUML configuration file (identical to the `-config` flag on the CLI)
|
* Local path to a PlantUML configuration file (identical to the `-config` flag on the CLI)
|
||||||
|
* File content will be added before each PlantUML diagram code.
|
||||||
* Default value: `null`
|
* Default value: `null`
|
||||||
* `PLANTUML_LIMIT_SIZE`
|
* `PLANTUML_LIMIT_SIZE`
|
||||||
* Limits image width and height
|
* Limits image width and height
|
||||||
@ -135,6 +141,13 @@ You can set all the following variables:
|
|||||||
* `ALLOW_PLANTUML_INCLUDE`
|
* `ALLOW_PLANTUML_INCLUDE`
|
||||||
* Enables `!include` processing which can read files from the server into diagrams. Files are read relative to the current working directory.
|
* Enables `!include` processing which can read files from the server into diagrams. Files are read relative to the current working directory.
|
||||||
* Default value: `false`
|
* Default value: `false`
|
||||||
|
* `PLANTUML_SECURITY_PROFILE`
|
||||||
|
* Set PlantUML security profile. See [PlantUML security](https://plantuml.com/security).
|
||||||
|
* Default value: `INTERNET`
|
||||||
|
* `PLANTUML_PROPERTY_FILE`
|
||||||
|
* Set PlantUML system properties (like over the Java command line using the `-Dpropertyname=value` syntax).
|
||||||
|
* To see what kind of file content is supported, see the documentation of [`java.util.Properties.load`](https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#load-java.io.Reader-).
|
||||||
|
* Default value: `null`
|
||||||
|
|
||||||
|
|
||||||
## Alternate: How to build your docker image
|
## Alternate: How to build your docker image
|
||||||
|
@ -48,6 +48,8 @@ import net.sourceforge.plantuml.core.DiagramDescription;
|
|||||||
import net.sourceforge.plantuml.core.ImageData;
|
import net.sourceforge.plantuml.core.ImageData;
|
||||||
import net.sourceforge.plantuml.error.PSystemError;
|
import net.sourceforge.plantuml.error.PSystemError;
|
||||||
import net.sourceforge.plantuml.preproc.Defines;
|
import net.sourceforge.plantuml.preproc.Defines;
|
||||||
|
import net.sourceforge.plantuml.security.SecurityProfile;
|
||||||
|
import net.sourceforge.plantuml.security.SecurityUtils;
|
||||||
import net.sourceforge.plantuml.version.Version;
|
import net.sourceforge.plantuml.version.Version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,13 +63,18 @@ public class DiagramResponse {
|
|||||||
*/
|
*/
|
||||||
private static final String POWERED_BY = "PlantUML Version " + Version.versionString();
|
private static final String POWERED_BY = "PlantUML Version " + Version.versionString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PLANTUML_CONFIG_FILE content.
|
||||||
|
*/
|
||||||
private static final List<String> CONFIG = new ArrayList<>();
|
private static final List<String> CONFIG = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache/flag to ensure that the `init()` method is called only once.
|
||||||
|
*/
|
||||||
|
private static boolean initialized = false;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
OptionFlags.ALLOW_INCLUDE = false;
|
init();
|
||||||
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,6 +103,43 @@ public class DiagramResponse {
|
|||||||
request = req;
|
request = req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize PlantUML configurations and properties as well as loading the PlantUML config file.
|
||||||
|
*/
|
||||||
|
public static void init() {
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
// set allow include to false by default
|
||||||
|
OptionFlags.ALLOW_INCLUDE = false;
|
||||||
|
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
|
||||||
|
OptionFlags.ALLOW_INCLUDE = true;
|
||||||
|
}
|
||||||
|
// set security profile to INTERNET by default
|
||||||
|
// NOTE: this property is cached inside PlantUML and cannot be changed after the first call of PlantUML
|
||||||
|
System.setProperty("PLANTUML_SECURITY_PROFILE", SecurityProfile.INTERNET.toString());
|
||||||
|
if (System.getenv("PLANTUML_SECURITY_PROFILE") != null) {
|
||||||
|
System.setProperty("PLANTUML_SECURITY_PROFILE", System.getenv("PLANTUML_SECURITY_PROFILE"));
|
||||||
|
}
|
||||||
|
// load properties from file
|
||||||
|
if (System.getenv("PLANTUML_PROPERTY_FILE") != null) {
|
||||||
|
try (FileReader propertyFileReader = new FileReader(System.getenv("PLANTUML_PROPERTY_FILE"))) {
|
||||||
|
System.getProperties().load(propertyFileReader);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// load PlantUML config file
|
||||||
|
if (System.getenv("PLANTUML_CONFIG_FILE") != null) {
|
||||||
|
try (BufferedReader br = new BufferedReader(new FileReader(System.getenv("PLANTUML_CONFIG_FILE")))) {
|
||||||
|
br.lines().forEach(CONFIG::add);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render and send a specific uml diagram.
|
* Render and send a specific uml diagram.
|
||||||
*
|
*
|
||||||
@ -108,23 +152,8 @@ public class DiagramResponse {
|
|||||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||||
response.setContentType(getContentType());
|
response.setContentType(getContentType());
|
||||||
|
|
||||||
if (CONFIG.size() == 0 && System.getenv("PLANTUML_CONFIG_FILE") != null) {
|
final Defines defines = getPreProcDefines();
|
||||||
// Read config
|
SourceStringReader reader = new SourceStringReader(defines, uml, CONFIG);
|
||||||
final BufferedReader br = new BufferedReader(new FileReader(System.getenv("PLANTUML_CONFIG_FILE")));
|
|
||||||
if (br == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String s = null;
|
|
||||||
while ((s = br.readLine()) != null) {
|
|
||||||
CONFIG.add(s);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
br.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceStringReader reader = new SourceStringReader(Defines.createEmpty(), uml, CONFIG);
|
|
||||||
if (CONFIG.size() > 0 && reader.getBlocks().get(0).getDiagram().getWarningOrError() != null) {
|
if (CONFIG.size() > 0 && reader.getBlocks().get(0).getDiagram().getWarningOrError() != null) {
|
||||||
reader = new SourceStringReader(uml);
|
reader = new SourceStringReader(uml);
|
||||||
}
|
}
|
||||||
@ -156,6 +185,23 @@ public class DiagramResponse {
|
|||||||
diagram.exportDiagram(response.getOutputStream(), idx, new FileFormatOption(format));
|
diagram.exportDiagram(response.getOutputStream(), idx, new FileFormatOption(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get PlantUML preprocessor defines.
|
||||||
|
*
|
||||||
|
* @return preprocessor defines
|
||||||
|
*/
|
||||||
|
private Defines getPreProcDefines() {
|
||||||
|
final Defines defines;
|
||||||
|
if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) {
|
||||||
|
// set dirpath to current dir but keep filename and filenameNoExtension undefined
|
||||||
|
defines = Defines.createWithFileName(new java.io.File("dummy.puml"));
|
||||||
|
defines.overrideFilename("");
|
||||||
|
} else {
|
||||||
|
defines = Defines.createEmpty();
|
||||||
|
}
|
||||||
|
return defines;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is block uml unmodified?
|
* Is block uml unmodified?
|
||||||
*
|
*
|
||||||
|
@ -34,7 +34,6 @@ import jakarta.servlet.ServletException;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import net.sourceforge.plantuml.OptionFlags;
|
|
||||||
import net.sourceforge.plantuml.api.PlantumlUtils;
|
import net.sourceforge.plantuml.api.PlantumlUtils;
|
||||||
import net.sourceforge.plantuml.code.NoPlantumlCompressionException;
|
import net.sourceforge.plantuml.code.NoPlantumlCompressionException;
|
||||||
import net.sourceforge.plantuml.png.MetadataTag;
|
import net.sourceforge.plantuml.png.MetadataTag;
|
||||||
@ -55,6 +54,12 @@ import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
|
|||||||
@SuppressWarnings("SERIAL")
|
@SuppressWarnings("SERIAL")
|
||||||
public class PlantUmlServlet extends AsciiCoderServlet {
|
public class PlantUmlServlet extends AsciiCoderServlet {
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Initialize the PlantUML server.
|
||||||
|
// You could say that this is like the `static void main(String[] args)` of the PlantUML server.
|
||||||
|
DiagramResponse.init();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default encoded uml text.
|
* Default encoded uml text.
|
||||||
* Bob -> Alice : hello
|
* Bob -> Alice : hello
|
||||||
@ -66,13 +71,6 @@ public class PlantUmlServlet extends AsciiCoderServlet {
|
|||||||
return "uml";
|
return "uml";
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = false;
|
|
||||||
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode arbitrary string to HTML string.
|
* Encode arbitrary string to HTML string.
|
||||||
*
|
*
|
||||||
|
@ -41,7 +41,6 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import net.sourceforge.plantuml.BlockUml;
|
import net.sourceforge.plantuml.BlockUml;
|
||||||
import net.sourceforge.plantuml.FileFormat;
|
import net.sourceforge.plantuml.FileFormat;
|
||||||
import net.sourceforge.plantuml.OptionFlags;
|
|
||||||
import net.sourceforge.plantuml.SourceStringReader;
|
import net.sourceforge.plantuml.SourceStringReader;
|
||||||
import net.sourceforge.plantuml.core.Diagram;
|
import net.sourceforge.plantuml.core.Diagram;
|
||||||
import net.sourceforge.plantuml.core.UmlSource;
|
import net.sourceforge.plantuml.core.UmlSource;
|
||||||
@ -54,13 +53,6 @@ import net.sourceforge.plantuml.core.UmlSource;
|
|||||||
@SuppressWarnings("SERIAL")
|
@SuppressWarnings("SERIAL")
|
||||||
public class ProxyServlet extends HttpServlet {
|
public class ProxyServlet extends HttpServlet {
|
||||||
|
|
||||||
static {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = false;
|
|
||||||
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean forbiddenURL(String full) {
|
public static boolean forbiddenURL(String full) {
|
||||||
if (full == null) {
|
if (full == null) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -34,7 +34,6 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import net.sourceforge.plantuml.FileFormat;
|
import net.sourceforge.plantuml.FileFormat;
|
||||||
import net.sourceforge.plantuml.OptionFlags;
|
|
||||||
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
|
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
|
||||||
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
|
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
|
||||||
|
|
||||||
@ -44,13 +43,6 @@ import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
|
|||||||
@SuppressWarnings("SERIAL")
|
@SuppressWarnings("SERIAL")
|
||||||
public abstract class UmlDiagramService extends HttpServlet {
|
public abstract class UmlDiagramService extends HttpServlet {
|
||||||
|
|
||||||
static {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = false;
|
|
||||||
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||||
final String url = request.getRequestURI();
|
final String url = request.getRequestURI();
|
||||||
|
@ -29,7 +29,6 @@ import java.net.URLDecoder;
|
|||||||
|
|
||||||
import net.sourceforge.plantuml.FileFormat;
|
import net.sourceforge.plantuml.FileFormat;
|
||||||
import net.sourceforge.plantuml.FileFormatOption;
|
import net.sourceforge.plantuml.FileFormatOption;
|
||||||
import net.sourceforge.plantuml.OptionFlags;
|
|
||||||
import net.sourceforge.plantuml.SourceStringReader;
|
import net.sourceforge.plantuml.SourceStringReader;
|
||||||
import net.sourceforge.plantuml.code.Transcoder;
|
import net.sourceforge.plantuml.code.Transcoder;
|
||||||
import net.sourceforge.plantuml.code.TranscoderUtil;
|
import net.sourceforge.plantuml.code.TranscoderUtil;
|
||||||
@ -42,13 +41,6 @@ import net.sourceforge.plantuml.core.ImageData;
|
|||||||
*/
|
*/
|
||||||
public abstract class UmlExtractor {
|
public abstract class UmlExtractor {
|
||||||
|
|
||||||
static {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = false;
|
|
||||||
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
|
|
||||||
OptionFlags.ALLOW_INCLUDE = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the complete UML source from the compressed source extracted from the
|
* Build the complete UML source from the compressed source extracted from the
|
||||||
* HTTP URI.
|
* HTTP URI.
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>plantumlservlet</servlet-name>
|
<servlet-name>plantumlservlet</servlet-name>
|
||||||
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlServlet</servlet-class>
|
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlServlet</servlet-class>
|
||||||
|
<load-on-startup>0</load-on-startup>
|
||||||
</servlet>
|
</servlet>
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>plantumlservlet</servlet-name>
|
<servlet-name>plantumlservlet</servlet-name>
|
||||||
|
Loading…
Reference in New Issue
Block a user