Skip to content

Commit b8de7a1

Browse files
committed
xml validation exception introduced fixes #31
1 parent ddeb6ad commit b8de7a1

File tree

6 files changed

+62
-8
lines changed

6 files changed

+62
-8
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ repositories {
1414
}
1515

1616
dependencies {
17-
implementation("com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:1.1")
17+
implementation("com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20220608.1")
1818
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
1919
}
2020

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.bgalek.security.svg;
2+
3+
public class InvalidXMLSyntaxException extends RuntimeException {
4+
public InvalidXMLSyntaxException(Throwable cause) {
5+
super(cause);
6+
}
7+
}

src/main/java/com/github/bgalek/security/svg/SvgSecurityValidator.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package com.github.bgalek.security.svg;
22

3-
import com.google.common.collect.ImmutableMap;
4-
import com.google.common.collect.ImmutableSet;
5-
import org.owasp.html.CssSchema;
63
import org.owasp.html.HtmlChangeListener;
74
import org.owasp.html.HtmlPolicyBuilder;
85
import org.owasp.html.PolicyFactory;
96

7+
import javax.xml.parsers.DocumentBuilder;
8+
import java.io.ByteArrayInputStream;
109
import java.nio.charset.StandardCharsets;
1110
import java.util.Arrays;
1211
import java.util.Collections;
@@ -29,6 +28,7 @@ public class SvgSecurityValidator implements XssDetector {
2928

3029
private final String[] svgElements;
3130
private final String[] svgAttributes;
31+
private final DocumentBuilder xmlParser;
3232

3333
/**
3434
* Use builder SvgSecurityValidator.builder()
@@ -37,11 +37,13 @@ public class SvgSecurityValidator implements XssDetector {
3737
public SvgSecurityValidator() {
3838
this.svgElements = SvgElements.DEFAULT_SVG_ELEMENTS;
3939
this.svgAttributes = SvgAttributes.DEFAULT_SVG_ATTRIBUTES;
40+
this.xmlParser = null;
4041
}
4142

42-
SvgSecurityValidator(String[] elements, String[] attributes) {
43+
SvgSecurityValidator(String[] elements, String[] attributes, DocumentBuilder xmlParser) {
4344
this.svgElements = elements;
4445
this.svgAttributes = attributes;
46+
this.xmlParser = xmlParser;
4547
}
4648

4749
public static SvgSecurityValidatorBuilder builder() {
@@ -57,6 +59,7 @@ public static SvgSecurityValidatorBuilder builder() {
5759
*/
5860
@Override
5961
public ValidationResult validate(String input) {
62+
if (xmlParser != null) validateXMLSchema(input);
6063
Set<String> offendingElements = getOffendingElements(input);
6164
if (offendingElements.isEmpty()) return new NegativeValidationResult();
6265
return new PositiveValidationResult(offendingElements);
@@ -67,6 +70,15 @@ public ValidationResult validate(byte[] input) {
6770
return validate(new String(input, StandardCharsets.UTF_8));
6871
}
6972

73+
private void validateXMLSchema(String input) {
74+
try {
75+
assert xmlParser != null;
76+
xmlParser.parse(new ByteArrayInputStream(input.getBytes()));
77+
} catch (Exception e) {
78+
throw new InvalidXMLSyntaxException(e);
79+
}
80+
}
81+
7082
private Set<String> getOffendingElements(String xml) {
7183
if (JAVASCRIPT_PROTOCOL_IN_CSS_URL.matcher(xml).find()) return Collections.singleton("style");
7284
PolicyFactory policy = new HtmlPolicyBuilder()

src/main/java/com/github/bgalek/security/svg/SvgSecurityValidatorBuilder.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.github.bgalek.security.svg;
22

3+
import javax.xml.parsers.DocumentBuilder;
4+
import javax.xml.parsers.DocumentBuilderFactory;
5+
import javax.xml.parsers.ParserConfigurationException;
36
import java.util.Arrays;
47
import java.util.List;
58
import java.util.stream.Stream;
@@ -10,6 +13,7 @@
1013
public class SvgSecurityValidatorBuilder {
1114
private String[] elements = DEFAULT_SVG_ELEMENTS;
1215
private String[] attributes = DEFAULT_SVG_ATTRIBUTES;
16+
private DocumentBuilder xmlParser;
1317

1418
SvgSecurityValidatorBuilder() {
1519
}
@@ -24,7 +28,20 @@ public SvgSecurityValidatorBuilder withAdditionalAttributes(List<String> additio
2428
return this;
2529
}
2630

31+
public SvgSecurityValidatorBuilder withSyntaxValidation() {
32+
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
33+
try {
34+
documentBuilderFactory.setNamespaceAware(true);
35+
documentBuilderFactory.setFeature("http://xml.org/sax/features/namespaces", false);
36+
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
37+
this.xmlParser = documentBuilderFactory.newDocumentBuilder();
38+
} catch (ParserConfigurationException e) {
39+
throw new RuntimeException(e);
40+
}
41+
return this;
42+
}
43+
2744
public SvgSecurityValidator build() {
28-
return new SvgSecurityValidator(elements, attributes);
45+
return new SvgSecurityValidator(elements, attributes, xmlParser);
2946
}
3047
}

src/test/java/com/github/bgalek/security/SvgSecurityValidatorTest.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.github.bgalek.security;
22

3+
import com.github.bgalek.security.svg.InvalidXMLSyntaxException;
34
import com.github.bgalek.security.svg.SvgSecurityValidator;
45
import com.github.bgalek.security.svg.ValidationResult;
6+
import org.junit.jupiter.api.Assertions;
57
import org.junit.jupiter.api.Test;
68
import org.junit.jupiter.params.ParameterizedTest;
79
import org.junit.jupiter.params.provider.Arguments;
@@ -10,7 +12,6 @@
1012
import java.io.File;
1113
import java.io.IOException;
1214
import java.nio.file.Files;
13-
import java.util.ArrayList;
1415
import java.util.Arrays;
1516
import java.util.Collections;
1617
import java.util.List;
@@ -49,12 +50,24 @@ void shouldNotDetectAnythingInValidFilesUsingBytes(String file) {
4950

5051
@MethodSource("brokenUseCases")
5152
@ParameterizedTest(name = "validate {0} svg")
52-
void shouldThrowExceptionWhenInputIsNotValidXml(String file) {
53+
void shouldNotThrowExceptionWhenInputIsNotValidXml(String file) {
5354
ValidationResult detect = SvgSecurityValidator.builder().build().validate(loadFile(file));
5455
assertEquals(Collections.emptySet(), detect.getOffendingElements());
5556
assertFalse(detect.hasViolations());
5657
}
5758

59+
@MethodSource("brokenUseCases")
60+
@ParameterizedTest(name = "validate {0} svg")
61+
void shouldThrowExceptionWhenInputIsNotValidXmlAndSyntaxValidationIsEnabled(String file) {
62+
InvalidXMLSyntaxException exception = Assertions.assertThrows(InvalidXMLSyntaxException.class, () ->
63+
SvgSecurityValidator.builder()
64+
.withSyntaxValidation()
65+
.build()
66+
.validate(loadFile(file)));
67+
assertTrue(exception.getMessage().contains("lineNumber:"));
68+
assertTrue(exception.getMessage().contains("columnNumber:"));
69+
}
70+
5871
@Test
5972
void shouldNotFailWhenUserDefinedAttributesAreUsed() {
6073
String testFile = loadFile("custom/custom1.svg");
@@ -120,6 +133,7 @@ private static Stream<Arguments> evilUseCases() {
120133

121134
private static Stream<Arguments> brokenUseCases() {
122135
return Stream.of(
136+
Arguments.of("broken/broken.syntax.svg"),
123137
Arguments.of("broken/broken.csv.svg"),
124138
Arguments.of("broken/broken.png.svg")
125139
);
Lines changed: 4 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)