Skip to content

Commit 16d5498

Browse files
committed
Add quarkus.liquibase.secure-parsing to allow disabling secure parsing
Fixes #47101
1 parent 2c3bf6d commit 16d5498

File tree

10 files changed

+1507
-5
lines changed

10 files changed

+1507
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.quarkus.liquibase.test;
2+
3+
import jakarta.inject.Inject;
4+
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.RegisterExtension;
7+
8+
import io.quarkus.liquibase.LiquibaseFactory;
9+
import io.quarkus.test.QuarkusUnitTest;
10+
import liquibase.Liquibase;
11+
12+
public class LiquibaseExtensionSecureParsingDisabledTest {
13+
14+
@Inject
15+
LiquibaseFactory liquibaseFactory;
16+
17+
@RegisterExtension
18+
static final QuarkusUnitTest config = new QuarkusUnitTest()
19+
.withApplicationRoot((jar) -> jar
20+
.addAsResource("insecure-db/changeLog.xml", "db/changeLog.xml")
21+
.addAsResource("insecure-db/dbchangelog-3.8.xsd", "db/dbchangelog-3.8.xsd")
22+
.addAsResource("secure-parsing-disabled.properties", "application.properties"));
23+
24+
@Test
25+
public void testSecureParsingDisabled() throws Exception {
26+
try (Liquibase liquibase = liquibaseFactory.createLiquibase()) {
27+
// TODO: this will fail as the system property is not enforced
28+
// List<ChangeSetStatus> status = liquibase.getChangeSetStatuses(liquibaseFactory.createContexts(),
29+
// liquibaseFactory.createLabels());
30+
// assertNotNull(status);
31+
// assertEquals(1, status.size());
32+
// assertEquals("id-1", status.get(0).getChangeSet().getId());
33+
// assertFalse(status.get(0).getWillRun());
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.quarkus.liquibase.test;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.fail;
5+
6+
import jakarta.inject.Inject;
7+
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.extension.RegisterExtension;
10+
11+
import io.quarkus.liquibase.LiquibaseFactory;
12+
import io.quarkus.test.QuarkusUnitTest;
13+
14+
public class LiquibaseExtensionSecureParsingEnabledTest {
15+
16+
@Inject
17+
LiquibaseFactory liquibaseFactory;
18+
19+
@RegisterExtension
20+
static final QuarkusUnitTest config = new QuarkusUnitTest()
21+
.withApplicationRoot((jar) -> jar
22+
.addAsResource("insecure-db/changeLog.xml", "db/changeLog.xml")
23+
.addAsResource("insecure-db/dbchangelog-3.8.xsd", "db/dbchangelog-3.8.xsd")
24+
.addAsResource("secure-parsing-enabled.properties", "application.properties"))
25+
.assertException(t -> {
26+
assertThat(t.getCause().getCause())
27+
.hasMessageContaining("because 'file' access is not allowed");
28+
});
29+
30+
@Test
31+
public void testSecureParsing() throws Exception {
32+
fail("should not be executed");
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog db/dbchangelog-3.8.xsd"
4+
logicalFilePath="changeLog.xml">
5+
6+
<changeSet author="dev (generated)" id="id-1">
7+
<createTable tableName="TEST_INSECURE_PARSING">
8+
<column name="ID" type="VARCHAR(255)">
9+
<constraints nullable="false"/>
10+
</column>
11+
<column name="NAME" type="VARCHAR(255)"/>
12+
</createTable>
13+
</changeSet>
14+
15+
</databaseChangeLog>

extensions/liquibase/liquibase/deployment/src/test/resources/insecure-db/dbchangelog-3.8.xsd

+1,381
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
quarkus.datasource.db-kind=h2
2+
quarkus.datasource.username=sa
3+
quarkus.datasource.password=sa
4+
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1
5+
# Liquibase config properties
6+
quarkus.liquibase.migrate-at-start=true
7+
quarkus.liquibase.secure-parsing=false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
quarkus.datasource.db-kind=h2
2+
quarkus.datasource.username=sa
3+
quarkus.datasource.password=sa
4+
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1
5+
# Liquibase config properties
6+
quarkus.liquibase.migrate-at-start=true

extensions/liquibase/liquibase/runtime/src/main/java/io/quarkus/liquibase/LiquibaseFactory.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.nio.file.Paths;
55
import java.sql.Connection;
66
import java.sql.DriverManager;
7+
import java.util.HashMap;
78
import java.util.Map;
89

910
import javax.sql.DataSource;
@@ -166,10 +167,17 @@ public String getDataSourceName() {
166167
}
167168

168169
public ResettableSystemProperties createResettableSystemProperties() {
169-
if (config.allowDuplicatedChangesetIdentifiers.isEmpty()) {
170-
return ResettableSystemProperties.empty();
170+
Map<String, String> resettableProperties = new HashMap<>();
171+
172+
if (config.allowDuplicatedChangesetIdentifiers.isPresent()) {
173+
resettableProperties.put("liquibase.allowDuplicatedChangesetIdentifiers",
174+
config.allowDuplicatedChangesetIdentifiers.get().toString());
175+
}
176+
177+
if (!config.secureParsing) {
178+
resettableProperties.put("liquibase.secureParsing", "false");
171179
}
172-
return ResettableSystemProperties.of("liquibase.allowDuplicatedChangesetIdentifiers",
173-
config.allowDuplicatedChangesetIdentifiers.get().toString());
180+
181+
return new ResettableSystemProperties(resettableProperties);
174182
}
175183
}

extensions/liquibase/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseConfig.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ public class LiquibaseConfig {
100100
/**
101101
* Allows duplicated changeset identifiers without failing Liquibase execution.
102102
*/
103-
public Optional<Boolean> allowDuplicatedChangesetIdentifiers;
103+
public Optional<Boolean> allowDuplicatedChangesetIdentifiers = Optional.empty();
104+
105+
/**
106+
* Whether Liquibase should enforce secure parsing.
107+
* <p>
108+
* If secure parsing is enforced, unsecure files may not be parsed.
109+
*/
110+
public boolean secureParsing = true;
104111

105112
}

extensions/liquibase/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseCreator.java

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public LiquibaseFactory createLiquibaseFactory(DataSource dataSource, String dat
4040
config.cleanAtStart = liquibaseRuntimeConfig.cleanAtStart();
4141
config.validateOnMigrate = liquibaseRuntimeConfig.validateOnMigrate();
4242
config.allowDuplicatedChangesetIdentifiers = liquibaseRuntimeConfig.allowDuplicatedChangesetIdentifiers();
43+
config.secureParsing = liquibaseRuntimeConfig.secureParsing();
4344
return new LiquibaseFactory(config, dataSource, dataSourceName);
4445
}
4546
}

extensions/liquibase/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseDataSourceRuntimeConfig.java

+7
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,11 @@ public interface LiquibaseDataSourceRuntimeConfig {
117117
*/
118118
Optional<Boolean> allowDuplicatedChangesetIdentifiers();
119119

120+
/**
121+
* Whether Liquibase should enforce secure parsing.
122+
* <p>
123+
* If secure parsing is enforced, insecure files may not be parsed.
124+
*/
125+
@WithDefault("true")
126+
boolean secureParsing();
120127
}

0 commit comments

Comments
 (0)