Skip to content

Commit

Permalink
Merge pull request #35 from Mogztter/issue-33-include-stdlib
Browse files Browse the repository at this point in the history
resolves #33 preserve stdlib includes
  • Loading branch information
ggrossetie authored Nov 13, 2019
2 parents 2b91058 + 27e5301 commit 2eaff21
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 4 deletions.
34 changes: 30 additions & 4 deletions server/src/main/java/io/kroki/server/service/Plantuml.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import net.sourceforge.plantuml.ErrorUml;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.LineLocation;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.code.Base64Coder;
import net.sourceforge.plantuml.core.Diagram;
Expand All @@ -34,9 +35,23 @@ public class Plantuml implements DiagramService {

private static final List<FileFormat> SUPPORTED_FORMATS = Arrays.asList(FileFormat.PNG, FileFormat.SVG, FileFormat.JPEG, FileFormat.BASE64);

private static final Pattern INCLUDE_RX = Pattern.compile("^\\s*!include(?:url)?\\s+.*");
private static final Pattern INCLUDE_RX = Pattern.compile("^\\s*!include(?:url)?\\s+(.*)");
private static final Pattern STDLIB_PATH_RX = Pattern.compile("<([a-zA-Z0-9]+)/[^>]+>");

private final SourceDecoder sourceDecoder;
private final DiagramResponse diagramResponse;
private static final List<String> STDLIB = Arrays.asList(
"aws",
"awslib",
"azure",
"c4",
"cloudinsight",
"cloudogu",
"kubernetes",
"material",
"office",
"osa",
"tupadr3");

public Plantuml() {
this.sourceDecoder = new SourceDecoder() {
Expand All @@ -46,6 +61,8 @@ public String decode(String encoded) throws DecodeException {
}
};
this.diagramResponse = new DiagramResponse(new Caching(Version.etag()));
// Disable include for security reasons
OptionFlags.ALLOW_INCLUDE = false;
}

@Override
Expand Down Expand Up @@ -131,7 +148,7 @@ static String withDelimiter(String source) {
return result;
}

private String sanitize(String input) throws IOException {
static String sanitize(String input) throws IOException {
try (BufferedReader reader = new BufferedReader(new StringReader(input))) {
StringBuilder sb = new StringBuilder();
String line = reader.readLine();
Expand All @@ -143,9 +160,18 @@ private String sanitize(String input) throws IOException {
}
}

private void ignoreInclude(String line, StringBuilder sb) {
private static void ignoreInclude(String line, StringBuilder sb) {
Matcher matcher = INCLUDE_RX.matcher(line);
if (!matcher.matches()) {
if (matcher.matches()) {
String include = matcher.group(1);
Matcher stdlibPathMatcher = STDLIB_PATH_RX.matcher(include);
if (stdlibPathMatcher.matches()) {
String prefix = stdlibPathMatcher.group(1).toLowerCase();
if (STDLIB.contains(prefix)) {
sb.append(line).append("\n");
}
}
} else {
sb.append(line).append("\n");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import io.kroki.server.error.BadRequestException;
import io.kroki.server.format.FileFormat;
import org.assertj.core.api.ThrowableAssert;
import org.junit.jupiter.api.Test;

import java.io.IOException;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

Expand Down Expand Up @@ -31,4 +34,83 @@ void should_return_a_diagram() {
byte[] convert = Plantuml.convert(diagram, FileFormat.SVG);
assertThat(convert).isNotEmpty();
}

@Test
void should_preserve_stdlib_include() throws IOException {
String diagram = "@startuml\n" +
"!include <azure/AzureRaw>\n" +
"!include <azure/Databases/AzureCosmosDb>\n" +
"!include <azure/Compute/AzureFunction>\n" +
"\n" +
"component \"<color:red><$AzureFunction></color>\" as myFunction\n" +
"database \"<color:#0072C6><$AzureCosmosDb></color>\" as myCosmosDb\n" +
"rectangle \"<color:AZURE_SYMBOL_COLOR><$AzureCosmosDb></color>\" as mySecondCosmosDb\n" +
"AzureFunction(mySecondFunction, \"Stream Processing\", \"Consumption\")\n" +
"\n" +
"myFunction --> myCosmosDb\n" +
"mySecondFunction --> mySecondCosmosDb\n" +
"@enduml";
byte[] convert = Plantuml.convert(Plantuml.sanitize(diagram), FileFormat.SVG);
assertThat(convert).isNotEmpty();
}

@Test
void should_remove_invalid_lib_include() throws IOException {
String diagram = "@startuml\n" +
"!include <foo/AzureRaw>\n" +
"!include <foo/azure>\n" +
"!include <azure>\n" +
"!include <azure\n" +
"!include </azure\n" +
"@enduml";
String result = Plantuml.sanitize(diagram);
assertThat(result).isEqualTo("@startuml\n@enduml\n");
}

@Test
void should_preserve_valid_lib_include() throws IOException {
String diagram = "@startuml\n" +
"!include <aws/common>\n" +
"!include <awslib/Database/Aurora.puml>\n" +
"!include <azure/AzureCommon.puml>\n" +
"!include <c4/C4.puml>\n" +
"!include <cloudinsight/kafka>\n" +
"!include <cloudogu/tools/docker>\n" +
"!include <kubernetes/k8s-sprites-unlabeled-25pct>\n" +
"!include <material/folder_move>\n" +
"!include <office/Servers/database_server>\n" +
"!include <osa/ai.puml>\n" +
"!include <tupadr3/common>\n" +
"@enduml";
String result = Plantuml.sanitize(diagram);
assertThat(result).isEqualTo(diagram + "\n");
}

@Test
void should_sanitize_include_url() throws IOException {
String diagram = "@startuml\n" +
"!include https://foo.bar\n" +
" !includeurl https://foo.bar\n" +
"@enduml";
String result = Plantuml.sanitize(diagram);
assertThat(result).isEqualTo("@startuml\n@enduml\n");
}

@Test
void should_sanitize_include_to_local_file() throws IOException {
String diagram = "@startuml\n" +
"!include /etc/password\n" +
"@enduml";
String result = Plantuml.sanitize(diagram);
assertThat(result).isEqualTo("@startuml\n@enduml\n");
}

@Test
void should_not_include_local_file() {
String diagram = "@startuml\n" +
"!include /etc/password\n" +
"@enduml";
assertThatThrownBy(() -> Plantuml.convert(diagram, FileFormat.SVG))
.hasMessage("cannot include /etc/password (line: 1)");
}
}

0 comments on commit 2eaff21

Please sign in to comment.