Skip to content

Commit 595076c

Browse files
SnakeYaml SafeConstructor restricting deserialization (#6319)
ParsedDockerComposeFile is vulnerable to deserialization gadget chain attacks that can lead to remote code execution when the file has untrusted content: https://nvd.nist.gov/vuln/detail/CVE-2022-1471 This should be fixed by using SafeConstructor as suggested by the SnakeYaml developers. Deserialization of arbitrary Java types is not used by the Compose file spec and therefore can be disabled without any loss of functionality: https://docs.docker.com/compose/compose-file/ --------- Co-authored-by: Eddú Meléndez Gonzales <[email protected]>
1 parent a00d048 commit 595076c

File tree

4 files changed

+33
-1
lines changed

4 files changed

+33
-1
lines changed

core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import lombok.extern.slf4j.Slf4j;
88
import org.apache.commons.io.FileUtils;
99
import org.testcontainers.images.ParsedDockerfile;
10+
import org.yaml.snakeyaml.LoaderOptions;
1011
import org.yaml.snakeyaml.Yaml;
12+
import org.yaml.snakeyaml.constructor.SafeConstructor;
1113

1214
import java.io.File;
1315
import java.io.FileInputStream;
@@ -35,7 +37,7 @@ class ParsedDockerComposeFile {
3537
private Map<String, Set<String>> serviceNameToImageNames = new HashMap<>();
3638

3739
ParsedDockerComposeFile(File composeFile) {
38-
Yaml yaml = new Yaml();
40+
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
3941
try (FileInputStream fileInputStream = FileUtils.openInputStream(composeFile)) {
4042
composeFileContent = yaml.load(fileInputStream);
4143
} catch (Exception e) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.testcontainers.containers;
2+
3+
public class ParsedDockerComposeFileBean {
4+
5+
public String foo;
6+
7+
public ParsedDockerComposeFileBean(String foo) {
8+
this.foo = foo;
9+
}
10+
}

core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.common.collect.ImmutableMap;
44
import com.google.common.collect.Sets;
5+
import lombok.SneakyThrows;
56
import org.junit.Test;
67

78
import java.io.File;
@@ -61,6 +62,22 @@ public void shouldIgnoreUnknownStructure() {
6162
new ParsedDockerComposeFile(ImmutableMap.of("version", "9000"));
6263
}
6364

65+
@Test
66+
@SneakyThrows
67+
public void shouldRejectDeserializationOfArbitraryClasses() {
68+
// Reject deserialization gadget chain attacks: https://nvd.nist.gov/vuln/detail/CVE-2022-1471
69+
// https://raw.githubusercontent.com/mbechler/marshalsec/master/marshalsec.pdf
70+
71+
File file = new File("src/test/resources/docker-compose-deserialization.yml");
72+
73+
// ParsedDockerComposeFile should reject deserialization of ParsedDockerComposeFileBean
74+
assertThatThrownBy(() -> {
75+
new ParsedDockerComposeFile(file);
76+
})
77+
.hasMessageContaining(file.getAbsolutePath())
78+
.hasMessageContaining("Unable to parse YAML file");
79+
}
80+
6481
@Test
6582
public void shouldObtainImageNamesV1() {
6683
File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
redis:
2+
image: redis
3+
key: !!org.testcontainers.containers.ParsedDockerComposeFileBean '{foo: bar}'

0 commit comments

Comments
 (0)