Skip to content

Commit 32192db

Browse files
authored
Fixex #4256: Obfuscate JDBC Password in query.log (#4261)
1 parent b7697f4 commit 32192db

File tree

5 files changed

+86
-34
lines changed

5 files changed

+86
-34
lines changed

extended/src/main/java/apoc/load/Jdbc.java

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,31 @@
1414

1515
import java.math.BigDecimal;
1616
import java.math.BigInteger;
17-
import java.sql.*;
17+
import java.sql.Connection;
18+
import java.sql.PreparedStatement;
19+
import java.sql.ResultSet;
20+
import java.sql.ResultSetMetaData;
21+
import java.sql.SQLException;
22+
import java.sql.Types;
1823
import java.time.LocalDateTime;
1924
import java.time.OffsetDateTime;
2025
import java.time.OffsetTime;
2126
import java.time.ZoneId;
22-
import java.util.*;
27+
import java.util.Collections;
28+
import java.util.Iterator;
29+
import java.util.LinkedHashMap;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.Spliterator;
33+
import java.util.Spliterators;
34+
import java.util.UUID;
2335
import java.util.stream.Stream;
2436
import java.util.stream.StreamSupport;
2537

26-
import static apoc.load.util.JdbcUtil.*;
38+
import static apoc.load.util.JdbcUtil.getConnection;
39+
import static apoc.load.util.JdbcUtil.getSqlOrKey;
40+
import static apoc.load.util.JdbcUtil.getUrlOrKey;
41+
import static apoc.load.util.JdbcUtil.obfuscateJdbcUrl;
2742

2843
/**
2944
* @author mh
@@ -96,10 +111,7 @@ private Stream<RowResult> executeQuery(String urlOrKey, String tableOrSelect, Ma
96111
throw sqle;
97112
}
98113
} catch (Exception e) {
99-
log.error(String.format("Cannot execute SQL statement `%s`.%nError:%n%s", query, e.getMessage()),e);
100-
String errorMessage = "Cannot execute SQL statement `%s`.%nError:%n%s";
101-
if(e.getMessage().contains("No suitable driver")) errorMessage="Cannot execute SQL statement `%s`.%nError:%n%s%n%s";
102-
throw new RuntimeException(String.format(errorMessage, query, e.getMessage(), "Please download and copy the JDBC driver into $NEO4J_HOME/plugins,more details at https://neo4j-contrib.github.io/neo4j-apoc-procedures/#_load_jdbc_resources"), e);
114+
throw logsErrorAndThrowsException(e, query, log);
103115
}
104116
}
105117

@@ -134,13 +146,24 @@ private Stream<RowResult> executeUpdate(String urlOrKey, String query, Map<Strin
134146
throw sqle;
135147
}
136148
} catch (Exception e) {
137-
log.error(String.format("Cannot execute SQL statement `%s`.%nError:%n%s", query, e.getMessage()),e);
138-
String errorMessage = "Cannot execute SQL statement `%s`.%nError:%n%s";
139-
if(e.getMessage().contains("No suitable driver")) errorMessage="Cannot execute SQL statement `%s`.%nError:%n%s%n%s";
140-
throw new RuntimeException(String.format(errorMessage, query, e.getMessage(), "Please download and copy the JDBC driver into $NEO4J_HOME/plugins,more details at https://neo4j-contrib.github.io/neo4j-apoc-procedures/#_load_jdbc_resources"), e);
149+
throw logsErrorAndThrowsException(e, query, log);
141150
}
142151
}
143152

153+
private static RuntimeException logsErrorAndThrowsException(Exception e, String query, Log log) {
154+
String errorMessage = "Cannot execute SQL statement `%s`.%nError:%n%s";
155+
String exceptionMsg = e.getMessage();
156+
157+
if(e.getMessage().contains("No suitable driver")) {
158+
errorMessage = "Cannot execute SQL statement `%s`.%nError:%n%s%n%s";
159+
exceptionMsg = obfuscateJdbcUrl(e.getMessage());
160+
}
161+
162+
Exception ex = new Exception(exceptionMsg);
163+
log.error(String.format("Cannot execute SQL statement `%s`.%nError:%n%s", query, exceptionMsg), ex);
164+
return new RuntimeException(String.format(errorMessage, query, exceptionMsg, "Please download and copy the JDBC driver into $NEO4J_HOME/plugins, more details at https://neo4j-contrib.github.io/neo4j-apoc-procedures/#_load_jdbc_resources"), ex);
165+
}
166+
144167
static void closeIt(Log log, AutoCloseable...closeables) {
145168
for (AutoCloseable c : closeables) {
146169
try {

extended/src/main/java/apoc/load/util/JdbcUtil.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,8 @@ public static String getUrlOrKey(String urlOrKey) {
8181
public static String getSqlOrKey(String sqlOrKey) {
8282
return sqlOrKey.contains(" ") ? sqlOrKey : Util.getLoadUrlByConfigFile(LOAD_TYPE, sqlOrKey, "sql").orElse("SELECT * FROM " + sqlOrKey);
8383
}
84+
85+
public static String obfuscateJdbcUrl(String query) {
86+
return query.replaceAll("(jdbc:[^:]+://)([^\\s\\\"']+)", "$1*******");
87+
}
8488
}

extended/src/test/java/apoc/load/JdbcTest.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@
55
import apoc.util.TestUtil;
66
import apoc.util.Util;
77
import apoc.util.collection.Iterators;
8-
import org.junit.*;
9-
import org.junit.jupiter.api.AfterAll;
8+
import org.junit.After;
9+
import org.junit.Assert;
10+
import org.junit.Before;
11+
import org.junit.Rule;
12+
import org.junit.Test;
1013
import org.junit.rules.ExpectedException;
14+
import org.junit.rules.TemporaryFolder;
1115
import org.junit.rules.TestName;
16+
import org.neo4j.configuration.GraphDatabaseSettings;
17+
import org.neo4j.dbms.api.DatabaseManagementService;
18+
import org.neo4j.graphdb.GraphDatabaseService;
1219
import org.neo4j.graphdb.QueryExecutionException;
13-
import org.neo4j.test.rule.DbmsRule;
14-
import org.neo4j.test.rule.ImpermanentDbmsRule;
20+
import org.neo4j.test.TestDatabaseManagementServiceBuilder;
1521

1622
import java.lang.reflect.InvocationTargetException;
1723
import java.sql.Connection;
@@ -25,15 +31,21 @@
2531
import java.util.Map;
2632

2733
import static apoc.ApocConfig.apocConfig;
34+
import static apoc.util.ExtendedTestUtil.assertFails;
35+
import static apoc.util.ExtendedTestUtil.getLogFileContent;
2836
import static apoc.util.MapUtil.map;
2937
import static apoc.util.TestUtil.testCall;
3038
import static apoc.util.TestUtil.testResult;
3139
import static org.junit.Assert.assertEquals;
40+
import static org.junit.Assert.assertTrue;
3241

3342
public class JdbcTest extends AbstractJdbcTest {
3443

3544
@Rule
36-
public DbmsRule db = new ImpermanentDbmsRule();
45+
public TemporaryFolder STORE_DIR = new TemporaryFolder();
46+
47+
private GraphDatabaseService db;
48+
private DatabaseManagementService dbms;
3749

3850
private Connection conn;
3951

@@ -47,6 +59,9 @@ public class JdbcTest extends AbstractJdbcTest {
4759

4860
@Before
4961
public void setUp() throws Exception {
62+
dbms = new TestDatabaseManagementServiceBuilder(STORE_DIR.getRoot().toPath()).build();
63+
db = dbms.database(GraphDatabaseSettings.DEFAULT_DATABASE_NAME);
64+
5065
apocConfig().setProperty("apoc.jdbc.derby.url","jdbc:derby:derbyDB");
5166
apocConfig().setProperty("apoc.jdbc.test.sql","SELECT * FROM PERSON");
5267
apocConfig().setProperty("apoc.jdbc.testparams.sql","SELECT * FROM PERSON WHERE NAME = ?");
@@ -77,11 +92,6 @@ public void tearDown() throws SQLException {
7792
System.clearProperty("derby.user.apoc");
7893
}
7994

80-
@AfterAll
81-
public void tearDownAll() {
82-
db.shutdown();
83-
}
84-
8595
@Test
8696
public void testLoadJdbc() throws Exception {
8797
testCall(db, "CALL apoc.load.jdbc('jdbc:derby:derbyDB','PERSON')",
@@ -111,6 +121,21 @@ public void testLoadJdbcParams() throws Exception {
111121
(row) -> assertResult(row));
112122
}
113123

124+
@Test
125+
public void testExceptionAndLogWithObfuscatedUrl() {
126+
String url = "jdbc:ajeje://localhost:3306/data_mart?user=root&password=root";
127+
String errorMsgWithObfuscatedUrl = "No suitable driver found for jdbc:ajeje://*******";
128+
129+
// obfuscated exception
130+
assertFails(db, "CALL apoc.load.jdbc($url,'SELECT * FROM PERSON WHERE NAME = ?',['John'])",
131+
Map.of("url", url),
132+
errorMsgWithObfuscatedUrl
133+
);
134+
135+
// obfuscated log in `debug.log`
136+
assertTrue(getLogFileContent().contains(errorMsgWithObfuscatedUrl));
137+
}
138+
114139
@Test
115140
public void testLoadJdbcParamsWithConfigLocalDateTime() throws Exception {
116141
testCall(db, "CALL apoc.load.jdbc('jdbc:derby:derbyDB','SELECT * FROM PERSON WHERE NAME = ?',['John'])",

extended/src/test/java/apoc/load/LoadLdapTest.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package apoc.load;
22

33

4-
import apoc.util.FileUtils;
54
import apoc.util.TestUtil;
65
import com.unboundid.ldap.sdk.LDAPConnection;
76
import com.unboundid.ldap.sdk.SearchResult;
@@ -18,13 +17,11 @@
1817
import org.zapodot.junit.ldap.EmbeddedLdapRule;
1918
import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder;
2019

21-
import java.io.File;
22-
import java.io.IOException;
23-
import java.nio.file.Files;
2420
import java.util.List;
2521
import java.util.Map;
2622

2723
import static apoc.ApocConfig.apocConfig;
24+
import static apoc.util.ExtendedTestUtil.getLogFileContent;
2825
import static apoc.util.TestUtil.testCall;
2926
import static org.junit.Assert.assertEquals;
3027
import static org.junit.Assert.assertFalse;
@@ -96,15 +93,6 @@ public void testLoadLDAPWithApocConfigWithoutDotBeforeLdapKey() {
9693
assertTrue(getLogFileContent().contains(logWarn));
9794
}
9895

99-
private static String getLogFileContent() {
100-
try {
101-
File logFile = new File(FileUtils.getLogDirectory(), "debug.log");
102-
return Files.readString(logFile.toPath());
103-
} catch (IOException e) {
104-
throw new RuntimeException(e);
105-
}
106-
}
107-
10896
private void testWithStringConfigCommon(String key) {
10997
// set a config `key=localhost:port dns pwd`
11098
String ldapValue = "%s %s %s".formatted(

extended/src/test/java/apoc/util/ExtendedTestUtil.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
import org.neo4j.internal.helpers.collection.Iterators;
1313
import org.neo4j.test.assertion.Assert;
1414

15+
import java.io.File;
16+
import java.io.IOException;
17+
import java.nio.file.Files;
1518
import java.util.Collections;
1619
import java.util.List;
1720
import java.util.Map;
@@ -141,4 +144,13 @@ public static void assertFails(GraphDatabaseService db, String query, Map<String
141144
assertTrue("Actual err. message is: " + actualErrMsg, actualErrMsg.contains(expectedErrMsg));
142145
}
143146
}
147+
148+
public static String getLogFileContent() {
149+
try {
150+
File logFile = new File(FileUtils.getLogDirectory(), "debug.log");
151+
return Files.readString(logFile.toPath());
152+
} catch (IOException e) {
153+
throw new RuntimeException(e);
154+
}
155+
}
144156
}

0 commit comments

Comments
 (0)