From 099767b76efedaa5d141e5114a1b6b5fa8c22c3a Mon Sep 17 00:00:00 2001 From: aiceflower Date: Thu, 25 Jun 2026 17:51:35 +0800 Subject: [PATCH] #AI COMMIT# [SECURITY][DS] Block JDBC URL-option injection via instance/database field The DB2 SqlConnection classes interpolate the user-supplied instance value directly into the JDBC URL via String.format("jdbc:db2://%s:%s/%s", ...). A value containing URL option separators (e.g. "SAMPLE:traceLevel=1;") becomes "jdbc:db2://host:port/SAMPLE:traceLevel=1;", letting the database segment toggle DB2 driver options (traceLevel/traceFile/traceDirectory/ traceFileAppend) that bypass the Properties denylist. Same class of issue affects SQL Server (';' separator), Oracle (service-name slot), and the mysql:// family. Adds four layers of defense: 1. SecurityUtils.checkDatabaseIsSafe(JdbcDriverType, database) rejects URL-option separators per driver family: DB2 -> : ; ? # & SQLSERVER -> ; ? # & ORACLE -> ? # & PG/MySQL/CK/DM/etc -> ? # & / 2. Expand JDBC_DB2_BLOCKED_PARAMS with traceLevel/traceFile/ traceDirectory/traceFileAppend so Properties-based injection of the same logging options is also blocked (defense in depth). 3. Backfill value_regex for the `instance` field of every JDBC data source in linkis_dml.sql. RegExpParameterValidateStrategy skips validation when value_regex is NULL, so the previous schema offered no first-line defense. New regex: ^[A-Za-z0-9_.-]+$ 4. Same regex backfilled via UPDATE in the 1.9.0 upgrade script for existing installs. 5. 6 new unit tests covering the DB2 database segment, the SQL Server and MySQL variants, benign-database sanity, and the expanded DB2 denylist. All 24 tests in SecurityUtilsTest pass. Co-Authored-By: Claude Opus 4.7 --- .../linkis/common/utils/SecurityUtils.java | 66 +++++++- .../common/utils/SecurityUtilsTest.java | 154 ++++++++++++++++++ linkis-dist/package/db/linkis_dml.sql | 26 +-- .../upgrade/1.9.0_schema/mysql/linkis_dml.sql | 8 + 4 files changed, 239 insertions(+), 15 deletions(-) diff --git a/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java b/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java index e8848aad282..3dca6c1f30a 100644 --- a/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java +++ b/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java @@ -118,7 +118,10 @@ public abstract class SecurityUtils { private static final CommonVars JDBC_DB2_BLOCKED_PARAMS = CommonVars$.MODULE$.apply( "linkis.jdbc.db2.blocked.params", - "clientRerouteServerListJNDIName,enableSeamlessFailover,JNDIName"); + // traceLevel / traceFile / traceDirectory / traceFileAppend let the driver write trace + // output to arbitrary paths; blocked alongside the JNDI-related options. + "clientRerouteServerListJNDIName,enableSeamlessFailover,JNDIName," + + "traceLevel,traceFile,traceDirectory,traceFileAppend"); private static final CommonVars JDBC_ORACLE_BLOCKED_PARAMS = CommonVars$.MODULE$.apply( @@ -503,7 +506,9 @@ public static void checkJdbcConnParams( // 2. Host sanity check: reject hosts that smuggle extra URL segments // (e.g. "host:port/evil?socketFactory=..."). checkHostIsSafe(host); - // 3. Param denylist check (also handles URL-encoded bypass). + // 3. Database sanity check (reject URL option separators in the database segment). + checkDatabaseIsSafe(driverType, database); + // 4. Param denylist check (also handles URL-encoded bypass). checkDriverParams(driverType, extraParams); } @@ -621,6 +626,63 @@ private static void checkHostIsSafe(String host) { } } + /** + * Reject database names that contain URL option separators. The metadata-query / datasource + * manager SqlConnection classes interpolate the user-supplied database/instance segment directly + * into the JDBC URL via String.format, so a value containing a separator character would be + * parsed as an extra URL option. Block the characters that any supported driver family treats as + * a delimiter after the database segment. + */ + private static void checkDatabaseIsSafe(JdbcDriverType driverType, String database) { + if (StringUtils.isBlank(database)) { + return; + } + String trimmed = database.trim(); + String forbidden; + switch (driverType) { + case DB2: + // DB2 URL option syntax is "jdbc:db2://h:p/db:opt1=v1;opt2=v2;". + forbidden = ":;?#&"; + break; + case ORACLE: + // Oracle thin URL is "jdbc:oracle:thin:@//h:p/service" — ':' and '/' are structural, but + // '?' / '&' would only appear if the caller is abusing the service-name slot to inject + // query-style params. '#' is the fragment anchor and a known smuggling trick. + forbidden = "?#&"; + break; + case SQLSERVER: + // SQL Server URL is "jdbc:sqlserver://h:p;databaseName=db;prop=v;" — ';' is the property + // separator, so block it along with the usual query/fragment anchors. + forbidden = ";?#&"; + break; + case POSTGRESQL: + case GREENPLUM: + case KINGBASE: + case CLICKHOUSE: + case DM: + case MYSQL: + case STARROCKS: + default: + // For the mysql:// family the database is followed by '?' for query params; for the others + // a '/' selects a path segment. Block all of the URL-meaningful characters. + forbidden = "?#&/"; + break; + } + for (int i = 0; i < forbidden.length(); i++) { + char c = forbidden.charAt(i); + if (trimmed.indexOf(c) >= 0) { + throw new LinkisSecurityException( + 35000, + "Database name contains forbidden character '" + + c + + "' for driver " + + driverType + + ": " + + trimmed); + } + } + } + private static CommonVars getDriverBlockedConfig(JdbcDriverType driverType) { switch (driverType) { case POSTGRESQL: diff --git a/linkis-commons/linkis-common/src/test/java/org/apache/linkis/common/utils/SecurityUtilsTest.java b/linkis-commons/linkis-common/src/test/java/org/apache/linkis/common/utils/SecurityUtilsTest.java index 860bc8b154b..06cc6d8fb99 100644 --- a/linkis-commons/linkis-common/src/test/java/org/apache/linkis/common/utils/SecurityUtilsTest.java +++ b/linkis-commons/linkis-common/src/test/java/org/apache/linkis/common/utils/SecurityUtilsTest.java @@ -533,4 +533,158 @@ public void testBuildSecureProperties_GlobalForceParamsWinOverUserInput() { Assertions.assertEquals("linkis", props.getProperty("applicationName")); Assertions.assertEquals("u", props.getProperty("user")); } + + // ----------------------- Database name sanitization tests ----------------------- + + @Test + public void testGenericCheck_Db2DatabaseInjection() { + // DB2 URL option syntax uses ':' and ';' as delimiters, so a database name containing them + // would be parsed as extra options. checkDatabaseIsSafe must reject them. + Map params = new HashMap<>(); + params.put("k1", "v1"); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", "SAMPLE:traceLevel=1;", params)); + // Same with a traceFile payload. + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, + "localhost", + 50000, + "u", + "p", + "SAMPLE:traceFile=/tmp/evil;", + params)); + // Single-character URL separators must also be rejected individually. + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", "db;", params)); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", "db:opt=v", params)); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", "db?q=1", params)); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", "db#frag", params)); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", "db&k=v", params)); + } + + @Test + public void testGenericCheck_SqlserverDatabaseInjection() { + // SQL Server property separator is ';', reject it in the database name. + Map params = new HashMap<>(); + params.put("k1", "v1"); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.SQLSERVER, + "localhost", + 1433, + "u", + "p", + "db;trustServerCertificate=true", + params)); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.SQLSERVER, "localhost", 1433, "u", "p", "db?q=1", params)); + } + + @Test + public void testGenericCheck_MysqlAndPostgresDatabaseInjection() { + // The mysql:// family uses '?' / '&' for query params and '#' as the fragment anchor. + Map params = new HashMap<>(); + params.put("k1", "v1"); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.MYSQL, + "localhost", + 3306, + "u", + "p", + "db?autoDeserialize=true", + params)); + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.POSTGRESQL, + "localhost", + 5432, + "u", + "p", + "db?socketFactory=evil", + params)); + // '/' is blocked for the mysql:// family because it selects a different path segment. + Assertions.assertThrows( + LinkisSecurityException.class, + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.POSTGRESQL, "localhost", 5432, "u", "p", "db/other", params)); + } + + @Test + public void testGenericCheck_BenignDatabaseNamesPass() { + // Sanity: legitimate database names for each driver family must still pass. + Map params = new HashMap<>(); + Assertions.assertDoesNotThrow( + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", "SAMPLE", params)); + Assertions.assertDoesNotThrow( + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.ORACLE, "localhost", 1521, "u", "p", "ORCL", params)); + Assertions.assertDoesNotThrow( + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.POSTGRESQL, "localhost", 5432, "u", "p", "postgres", params)); + Assertions.assertDoesNotThrow( + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.MYSQL, "localhost", 3306, "u", "p", "db_name", params)); + Assertions.assertDoesNotThrow( + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.SQLSERVER, "localhost", 1433, "u", "p", "master", params)); + // Blank database names are allowed (some drivers use a default). + Assertions.assertDoesNotThrow( + () -> + SecurityUtils.checkJdbcConnParams( + JdbcDriverType.DB2, "localhost", 50000, "u", "p", " ", params)); + } + + @Test + public void testGenericCheck_Db2TraceParamsInDenylist() { + // The DB2 denylist must reject these even when they arrive via Properties rather than the URL. + assertDriverRejectsParam(JdbcDriverType.DB2, "traceLevel", "1"); + assertDriverRejectsParam(JdbcDriverType.DB2, "traceFile", "/tmp/evil.log"); + assertDriverRejectsParam(JdbcDriverType.DB2, "traceDirectory", "/tmp"); + assertDriverRejectsParam(JdbcDriverType.DB2, "traceFileAppend", "true"); + // JNDI-related params must remain in the denylist. + assertDriverRejectsParam( + JdbcDriverType.DB2, "clientRerouteServerListJNDIName", "ldap://evil/exp"); + } } diff --git a/linkis-dist/package/db/linkis_dml.sql b/linkis-dist/package/db/linkis_dml.sql index 6dd4ab0c860..eb4cb680374 100644 --- a/linkis-dist/package/db/linkis_dml.sql +++ b/linkis-dist/package/db/linkis_dml.sql @@ -679,7 +679,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'sid', 'SID', 'SID', NULL, 'TEXT', NULL, 0, 'SID', 'SID', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'serviceName', 'service_name', 'service_name', NULL, 'TEXT', NULL, 0, 'service_name', 'service_name', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'server', 'server', 'server', NULL, 'TEXT', NULL, 0, 'server', 'server', NULL, NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 0, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 0, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'dm'; INSERT INTO `linkis_ps_dm_datasource_type_key` @@ -691,7 +691,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'kingbase'; INSERT INTO `linkis_ps_dm_datasource_type_key` @@ -703,7 +703,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); -- https://jdbc.postgresql.org/documentation/use/ select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'postgresql'; @@ -716,7 +716,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); -- https://learn.microsoft.com/zh-cn/sql/connect/jdbc/building-the-connection-url?redirectedfrom=MSDN&view=sql-server-ver16 select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'sqlserver'; @@ -729,7 +729,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); -- https://www.ibm.com/docs/en/db2/11.5?topic=cdsudidsdjs-url-format-data-server-driver-jdbc-sqlj-type-4-connectivity select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'db2'; @@ -742,7 +742,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); -- https://greenplum.docs.pivotal.io/6-1/datadirect/datadirect_jdbc.html#topic_ylk_pbx_2bb select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'greenplum'; @@ -755,7 +755,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'doris'; INSERT INTO `linkis_ps_dm_datasource_type_key` @@ -767,7 +767,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); -- https://github.com/ClickHouse/clickhouse-jdbc/tree/master/clickhouse-jdbc select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'clickhouse'; @@ -780,7 +780,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'hive'; @@ -800,7 +800,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 0, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'starrocks'; INSERT INTO `linkis_ps_dm_datasource_type_key` @@ -812,7 +812,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 0, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'gaussdb'; INSERT INTO `linkis_ps_dm_datasource_type_key` @@ -824,7 +824,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'oceanbase'; INSERT INTO `linkis_ps_dm_datasource_type_key` @@ -836,7 +836,7 @@ VALUES (@data_source_type_id, 'address', '地址', 'Address', NULL, 'TEXT', NULL (@data_source_type_id, 'params', '连接参数(Connection params)', 'Connection params', NULL, 'TEXT', NULL, 0, '输入JSON格式(Input JSON format): {"param":"value"}', 'Input JSON format: {"param":"value"}', NULL, NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'username', '用户名(Username)', 'Username', NULL, 'TEXT', NULL, 1, '用户名(Username)', 'Username', '^[0-9A-Za-z_-]+$', NULL, NULL, NULL, now(), now()), (@data_source_type_id, 'password', '密码(Password)', 'Password', NULL, 'PASSWORD', NULL, 1, '密码(Password)', 'Password', '', NULL, NULL, NULL, now(), now()), - (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', NULL, NULL, NULL, NULL, now(), now()); + (@data_source_type_id, 'instance', '实例名(instance)', 'Instance', NULL, 'TEXT', NULL, 1, '实例名(instance)', 'Instance', '^[A-Za-z0-9_.-]+$', NULL, NULL, NULL, NULL, now(), now()); select @data_source_type_id := id from `linkis_ps_dm_datasource_type` where `name` = 'doris'; UPDATE linkis_ps_dm_datasource_type_key SET `require` = 0 WHERE `key` ="password" and `data_source_type_id` = @data_source_type_id; diff --git a/linkis-dist/package/db/upgrade/1.9.0_schema/mysql/linkis_dml.sql b/linkis-dist/package/db/upgrade/1.9.0_schema/mysql/linkis_dml.sql index e5625ca1958..2c95812990a 100644 --- a/linkis-dist/package/db/upgrade/1.9.0_schema/mysql/linkis_dml.sql +++ b/linkis-dist/package/db/upgrade/1.9.0_schema/mysql/linkis_dml.sql @@ -285,3 +285,11 @@ UPDATE `linkis_ps_configuration_config_key` SET default_value = '60' WHERE `key` -- 更新错误码正则表达式 UPDATE linkis_ps_error_code SET error_regex = "The ecm of labels" WHERE error_code = "01001"; +-- Backfill value_regex for JDBC data-source `instance` fields. +-- Restrict the instance field to identifier characters so RegExpParameterValidateStrategy +-- rejects values containing URL option separators before they reach the connection layer. +UPDATE `linkis_ps_dm_datasource_type_key` +SET `value_regex` = '^[A-Za-z0-9_.-]+$' +WHERE `key` = 'instance' + AND `value_regex` IS NULL; +