[improve] form-urlencode SQL request body in GreptimeSqlQueryExecutor (#4223)

Co-authored-by: Tomsun28 <tomsun28@outlook.com>
This commit is contained in:
Duansg
2026-07-21 23:19:43 +08:00
committed by GitHub
co-authored by Tomsun28
parent c9e3338c9f
commit 0a38a73756
2 changed files with 39 additions and 1 deletions
@@ -38,6 +38,8 @@ import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -79,7 +81,7 @@ public class GreptimeSqlQueryExecutor extends SqlQueryExecutor {
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
String requestBody = "sql=" + queryString;
String requestBody = "sql=" + URLEncoder.encode(queryString, StandardCharsets.UTF_8);
HttpEntity<String> httpEntity = new HttpEntity<>(requestBody, headers);
String url = greptimeProperties.httpEndpoint() + QUERY_PATH;
@@ -20,12 +20,16 @@
package org.apache.hertzbeat.warehouse.db;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -34,6 +38,7 @@ import org.apache.hertzbeat.warehouse.store.history.tsdb.greptime.GreptimeSqlQue
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpEntity;
@@ -90,6 +95,37 @@ class GreptimeSqlQueryExecutorTest {
assertEquals(85.5, result.get(0).get("value"));
}
@Test
@SuppressWarnings("unchecked")
void testRequestBodyIsFormEncoded() {
when(restTemplate.exchange(
any(String.class),
eq(HttpMethod.POST),
any(HttpEntity.class),
eq(GreptimeSqlQueryContent.class)
)).thenReturn(new ResponseEntity<>(new GreptimeSqlQueryContent(), HttpStatus.OK));
// SQL carrying form-urlencoded metacharacters that would pollute the request body
// if concatenated raw: '&' would start a second sql= parameter and override the query.
String sql = "SELECT * FROM t WHERE name = 'x&sql=DROP TABLE t'";
greptimeSqlQueryExecutor.execute(sql);
ArgumentCaptor<HttpEntity<String>> captor = ArgumentCaptor.forClass(HttpEntity.class);
verify(restTemplate).exchange(
any(String.class),
eq(HttpMethod.POST),
captor.capture(),
eq(GreptimeSqlQueryContent.class));
String body = captor.getValue().getBody();
assertNotNull(body);
// The body must be a single sql= parameter; the '&' inside the SQL must be encoded.
String encodedValue = body.substring("sql=".length());
assertFalse(encodedValue.contains("&"), "raw '&' in body causes HTTP parameter pollution");
// Decoding the value must yield exactly the original SQL, lossless round-trip.
assertEquals(sql, URLDecoder.decode(encodedValue, StandardCharsets.UTF_8));
}
@Test
void testExecuteError() {
// Mock error response