5
5
import water .parser .ParseDataset ;
6
6
import water .util .Log ;
7
7
8
+ import java .io .UnsupportedEncodingException ;
9
+ import java .net .URI ;
10
+ import java .net .URISyntaxException ;
11
+ import java .net .URLDecoder ;
8
12
import java .sql .*;
13
+ import java .util .Arrays ;
14
+ import java .util .List ;
9
15
import java .util .Objects ;
10
16
import java .util .concurrent .ArrayBlockingQueue ;
11
17
import java .util .concurrent .atomic .AtomicLong ;
18
+ import java .util .regex .Matcher ;
19
+ import java .util .regex .Pattern ;
20
+ import java .util .stream .Collectors ;
21
+ import java .util .stream .Stream ;
12
22
13
23
public class SQLManager {
14
24
@@ -30,6 +40,15 @@ public class SQLManager {
30
40
31
41
private static final String TMP_TABLE_ENABLED = H2O .OptArgs .SYSTEM_PROP_PREFIX + "sql.tmp_table.enabled" ;
32
42
43
+ private static final String DISALLOWED_JDBC_PARAMETERS_PARAM = H2O .OptArgs .SYSTEM_PROP_PREFIX + "sql.jdbc.disallowed.parameters" ;
44
+
45
+ private static final Pattern JDBC_PARAMETERS_REGEX_PATTERN = Pattern .compile ("(?i)[?;&]([a-z]+)=" );
46
+
47
+ private static final List <String > DEFAULT_JDBC_DISALLOWED_PARAMETERS = Stream .of (
48
+ "autoDeserialize" , "queryInterceptors" , "allowLoadLocalInfile" , "allowMultiQueries" , //mysql
49
+ "allowLoadLocalInfileInPath" , "allowUrlInLocalInfile" , "allowPublicKeyRetrieval" , //mysql
50
+ "init" , "script" , "shutdown" //h2
51
+ ).map (String ::toLowerCase ).collect (Collectors .toList ());
33
52
private static AtomicLong NEXT_TABLE_NUM = new AtomicLong (0 );
34
53
35
54
static Key <Frame > nextTableKey (String prefix , String postfix ) {
@@ -58,6 +77,7 @@ public static Job<Frame> importSqlTable(
58
77
final String username , final String password , final String columns ,
59
78
final Boolean useTempTable , final String tempTableName ,
60
79
final SqlFetchMode fetchMode , final Integer numChunksHint ) {
80
+ validateJdbcUrl (connection_url );
61
81
62
82
final Key <Frame > destination_key = nextTableKey (table , "sql_to_hex" );
63
83
final Job <Frame > j = new Job <>(destination_key , Frame .class .getName (), "Import SQL Table" );
@@ -533,6 +553,7 @@ private static int estimateConcurrentConnections(final int cloudSize, final shor
533
553
* @throws SQLException if a database access error occurs or the url is
534
554
*/
535
555
public static Connection getConnectionSafe (String url , String username , String password ) throws SQLException {
556
+ validateJdbcUrl (url );
536
557
initializeDatabaseDriver (getDatabaseType (url ));
537
558
try {
538
559
return DriverManager .getConnection (url , username , password );
@@ -588,6 +609,30 @@ static void initializeDatabaseDriver(String databaseType) {
588
609
}
589
610
}
590
611
612
+ public static void validateJdbcUrl (String jdbcUrl ) throws IllegalArgumentException {
613
+ if (jdbcUrl == null || jdbcUrl .trim ().isEmpty ()) {
614
+ throw new IllegalArgumentException ("JDBC URL is null or empty" );
615
+ }
616
+
617
+ if (!jdbcUrl .toLowerCase ().startsWith ("jdbc:" )) {
618
+ throw new IllegalArgumentException ("JDBC URL must start with 'jdbc:'" );
619
+ }
620
+
621
+ Matcher matcher = JDBC_PARAMETERS_REGEX_PATTERN .matcher (jdbcUrl );
622
+ String property = System .getProperty (DISALLOWED_JDBC_PARAMETERS_PARAM );
623
+ List <String > disallowedParameters = property == null ?
624
+ DEFAULT_JDBC_DISALLOWED_PARAMETERS :
625
+ Arrays .stream (property .split ("," )).map (String ::toLowerCase ).collect (Collectors .toList ());
626
+
627
+ while (matcher .find ()) {
628
+ String key = matcher .group (1 );
629
+ if (disallowedParameters .contains (key .toLowerCase ())) {
630
+ throw new IllegalArgumentException ("Potentially dangerous JDBC parameter found: " + key +
631
+ ". That behavior can be altered by setting " + DISALLOWED_JDBC_PARAMETERS_PARAM + " env variable to another comma separated list." );
632
+ }
633
+ }
634
+ }
635
+
591
636
static class SqlTableToH2OFrameStreaming {
592
637
final String _table , _columns , _databaseType ;
593
638
final int _numCol ;
0 commit comments