Skip to content

Commit 4fc5cdf

Browse files
Backport ClickHouse#89970 to 25.8: Fix URL validation in MongoDB engine
1 parent 2d49011 commit 4fc5cdf

File tree

2 files changed

+39
-6
lines changed

2 files changed

+39
-6
lines changed

src/Storages/StorageMongoDB.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ namespace Setting
5959
extern const SettingsBool mongodb_throw_on_unsupported_query;
6060
}
6161

62+
static constexpr const char * MONGODB_RESERVED_CHARS = "!?#/'\",;:$&()[]*+=@";
63+
6264
void MongoDBConfiguration::checkHosts(const ContextPtr & context) const
6365
{
6466
// Because domain records will be resolved inside the driver, we can't check resolved IPs for our restrictions.
@@ -108,6 +110,13 @@ Pipe StorageMongoDB::read(
108110
std::move(options), std::make_shared<const Block>(std::move(sample_block)), max_block_size));
109111
}
110112

113+
static String encodeString(const String & str)
114+
{
115+
String encoded;
116+
Poco::URI::encode(str, MONGODB_RESERVED_CHARS, encoded);
117+
return encoded;
118+
}
119+
111120
static MongoDBConfiguration getConfigurationImpl(const StorageID * table_id, ASTs engine_args, ContextPtr context, bool allow_excessive_path_in_host)
112121
{
113122
MongoDBConfiguration configuration;
@@ -124,10 +133,12 @@ static MongoDBConfiguration getConfigurationImpl(const StorageID * table_id, AST
124133
"host", "port", "user", "password", "database", "collection"}, {"options", "oid_columns"});
125134
String user = named_collection->get<String>("user");
126135
String auth_string;
127-
String escaped_password;
128-
Poco::URI::encode(named_collection->get<String>("password"), "!?#/'\",;:$&()[]*+=@", escaped_password);
129136
if (!user.empty())
130-
auth_string = fmt::format("{}:{}@", user, escaped_password);
137+
{
138+
String escaped_user = encodeString(user);
139+
String escaped_password = encodeString(named_collection->get<String>("password"));
140+
auth_string = fmt::format("{}:{}@", escaped_user, escaped_password);
141+
}
131142
configuration.uri = std::make_unique<mongocxx::uri>(fmt::format("mongodb://{}{}:{}/{}?{}",
132143
auth_string,
133144
named_collection->get<String>("host"),
@@ -154,10 +165,12 @@ static MongoDBConfiguration getConfigurationImpl(const StorageID * table_id, AST
154165

155166
String user = checkAndGetLiteralArgument<String>(engine_args[3], "user");
156167
String auth_string;
157-
String escaped_password;
158-
Poco::URI::encode(checkAndGetLiteralArgument<String>(engine_args[4], "password"), "!?#/'\",;:$&()[]*+=@", escaped_password);
159168
if (!user.empty())
160-
auth_string = fmt::format("{}:{}@", user, escaped_password);
169+
{
170+
String escaped_user = encodeString(user);
171+
String escaped_password = encodeString(checkAndGetLiteralArgument<String>(engine_args[4], "password"));
172+
auth_string = fmt::format("{}:{}@", escaped_user, escaped_password);
173+
}
161174

162175
auto host_port = checkAndGetLiteralArgument<String>(engine_args[0], "host:port");
163176
auto database_name = checkAndGetLiteralArgument<String>(engine_args[1], "database");

tests/integration/test_storage_mongodb/test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,3 +1471,23 @@ def test_numbers_parsing(started_cluster):
14711471

14721472
node.query("DROP TABLE numbers_parsing_table")
14731473
numbers_parsing_table.drop()
1474+
1475+
1476+
def test_url_validation(started_cluster):
1477+
mongo_connection = get_mongo_connection(started_cluster)
1478+
db = mongo_connection["test"]
1479+
db.command("dropAllUsersFromDatabase")
1480+
db.command("createUser", "[email protected]", pwd=mongo_pass, roles=["readWrite"])
1481+
drop_mongo_collection_if_exists(db, "url_validation_table")
1482+
url_validation_table = db["url_validation_table"]
1483+
data = []
1484+
for i in range(0, 100):
1485+
data.append({"key": i, "data": hex(i * i)})
1486+
url_validation_table.insert_many(data)
1487+
1488+
node = started_cluster.instances["node"]
1489+
node.query(
1490+
f"CREATE OR REPLACE TABLE url_validation_table(key UInt64, data String) ENGINE = MongoDB('mongo1', 'test', 'url_validation_table', '[email protected]', '{mongo_pass}')"
1491+
)
1492+
1493+
assert node.query("SELECT COUNT() FROM url_validation_table") == "100\n"

0 commit comments

Comments
 (0)