FINERACT-2081: ESAPI.encoder().encodeForSQL is deprecated, so replaced the method with parameterized queries based solution with user input validation
This commit is contained in:
+88
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.dataqueries.domain;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Enumeration of valid report types for whitelist validation.
|
||||
*
|
||||
* This enum provides a secure whitelist of allowed report types to prevent SQL injection attacks in the reporting
|
||||
* system. Only these predefined types are allowed in report queries.
|
||||
*/
|
||||
public enum ReportType {
|
||||
|
||||
/**
|
||||
* Standard report type for retrieving report data
|
||||
*/
|
||||
REPORT("report"),
|
||||
|
||||
/**
|
||||
* Parameter type for retrieving parameter definitions and possible values
|
||||
*/
|
||||
PARAMETER("parameter");
|
||||
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Cached set of all valid values for efficient validation
|
||||
*/
|
||||
private static final Set<String> VALID_VALUES = Arrays.stream(values()).map(ReportType::getValue).collect(Collectors.toSet());
|
||||
|
||||
ReportType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a given report type is in the whitelist.
|
||||
*
|
||||
* @param type
|
||||
* the report type to validate
|
||||
* @return true if the type is valid, false otherwise
|
||||
*/
|
||||
public static boolean isValidType(String type) {
|
||||
return type != null && !type.trim().isEmpty() && VALID_VALUES.contains(type.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReportType enum value for a given string.
|
||||
*
|
||||
* @param type
|
||||
* the report type string
|
||||
* @return the corresponding ReportType enum value
|
||||
* @throws IllegalArgumentException
|
||||
* if the type is not valid
|
||||
*/
|
||||
public static ReportType fromValue(String type) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Report type cannot be null");
|
||||
}
|
||||
|
||||
String lowerType = type.toLowerCase(Locale.ROOT);
|
||||
return Arrays.stream(values()).filter(rt -> rt.getValue().equals(lowerType)).findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid report type: " + type));
|
||||
}
|
||||
}
|
||||
+25
@@ -21,4 +21,29 @@ package org.apache.fineract.infrastructure.security.service;
|
||||
public interface SqlInjectionPreventerService {
|
||||
|
||||
String encodeSql(String literal);
|
||||
|
||||
/**
|
||||
* Validates and quotes a database identifier (table name, column name) using database-specific quoting rules. This
|
||||
* method ensures that identifiers are safely quoted to prevent SQL injection attacks.
|
||||
*
|
||||
* @param identifier
|
||||
* the database identifier to quote
|
||||
* @param allowedValues
|
||||
* optional set of allowed values for whitelist validation
|
||||
* @return the properly quoted identifier safe for use in SQL queries
|
||||
* @throws IllegalArgumentException
|
||||
* if the identifier is invalid or not in the whitelist (if provided)
|
||||
*/
|
||||
String quoteIdentifier(String identifier, java.util.Set<String> allowedValues);
|
||||
|
||||
/**
|
||||
* Validates and quotes a database identifier without whitelist validation.
|
||||
*
|
||||
* @param identifier
|
||||
* the database identifier to quote
|
||||
* @return the properly quoted identifier safe for use in SQL queries
|
||||
* @throws IllegalArgumentException
|
||||
* if the identifier is null or empty
|
||||
*/
|
||||
String quoteIdentifier(String identifier);
|
||||
}
|
||||
|
||||
+11
-5
@@ -34,6 +34,7 @@ import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
@@ -59,16 +60,19 @@ public class ColumnValidator {
|
||||
ResultSet resultSet = dbMetaData.getColumns(null, null, entry.getKey(), null);
|
||||
Set<String> tableColumns = getTableColumns(resultSet);
|
||||
if (!columns.isEmpty() && tableColumns.isEmpty()) {
|
||||
throw new SQLInjectionException();
|
||||
throw new PlatformApiDataValidationException("error.msg.invalid.table.column", "Invalid table or column name detected",
|
||||
entry.getKey(), columns);
|
||||
}
|
||||
for (String requestedColumn : columns) {
|
||||
if (!tableColumns.contains(requestedColumn)) {
|
||||
throw new SQLInjectionException();
|
||||
throw new PlatformApiDataValidationException("error.msg.invalid.table.column", "Invalid table column name detected",
|
||||
entry.getKey(), requestedColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new SQLInjectionException(e);
|
||||
throw new PlatformApiDataValidationException("error.msg.database.access.error",
|
||||
"Database access error during column validation", e.getMessage(), e);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
DataSourceUtils.releaseConnection(connection, jdbcTemplate.getDataSource());
|
||||
@@ -120,7 +124,8 @@ public class ColumnValidator {
|
||||
Set<String> columns = entry.getValue();
|
||||
tableColumnMap.put(schema.substring(startPos, index).trim(), columns);
|
||||
} else {
|
||||
throw new SQLInjectionException();
|
||||
throw new PlatformApiDataValidationException("error.msg.invalid.table.alias", "Invalid table alias in SQL query",
|
||||
entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +147,8 @@ public class ColumnValidator {
|
||||
tableColumnMap.put(tableColumn[0], columns);
|
||||
}
|
||||
} else {
|
||||
throw new SQLInjectionException();
|
||||
throw new PlatformApiDataValidationException("error.msg.invalid.table.column.format",
|
||||
"Invalid table.column format in operand", operand);
|
||||
}
|
||||
}
|
||||
return tableColumnMap;
|
||||
|
||||
Reference in New Issue
Block a user