Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tinyengine.it.dynamic.dao;

import com.tinyengine.it.dynamic.util.SQLIdentifierValidator;
import org.apache.ibatis.jdbc.SQL;

import java.util.List;
Expand All @@ -9,15 +10,15 @@ public class DynamicSqlProvider {

public String select(Map<String, Object> params) {
String tableName = (String) params.get("tableName");
SQLIdentifierValidator.isValidIdentifier(tableName);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Throw when the table name is invalid.

These calls ignore the boolean return value, so invalid table names still flow into FROM / INSERT_INTO / UPDATE / DELETE_FROM. The duplicate call in update() should also be removed.

Proposed fix
 public class DynamicSqlProvider {
+
+	private void validateTableName(String tableName) {
+		if (!SQLIdentifierValidator.isValidIdentifier(tableName)) {
+			throw new IllegalArgumentException("表名格式不正确: " + tableName);
+		}
+	}
 
 	public String select(Map<String, Object> params) {
 		String tableName = (String) params.get("tableName");
-		SQLIdentifierValidator.isValidIdentifier(tableName);
+		validateTableName(tableName);
@@
 	public String insert(Map<String, Object> params) {
 		String tableName = (String) params.get("tableName");
-		SQLIdentifierValidator.isValidIdentifier(tableName);
+		validateTableName(tableName);
@@
 	public String update(Map<String, Object> params) {
 		String tableName = (String) params.get("tableName");
-		SQLIdentifierValidator.isValidIdentifier(tableName);
-
-		SQLIdentifierValidator.isValidIdentifier(tableName);
+		validateTableName(tableName);
@@
 	public String delete(Map<String, Object> params) {
 		String tableName = (String) params.get("tableName");
-		SQLIdentifierValidator.isValidIdentifier(tableName);
+		validateTableName(tableName);

Also applies to: 56-58, 74-78, 100-102

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@base/src/main/java/com/tinyengine/it/dynamic/dao/DynamicSqlProvider.java`
around lines 11 - 13, The calls to
SQLIdentifierValidator.isValidIdentifier(tableName) in DynamicSqlProvider
(notably in select, insert, update, and delete methods) ignore the boolean
result so invalid identifiers still proceed; change each call to check the
boolean and throw an IllegalArgumentException (or similar runtime exception)
when it returns false, and remove the duplicate isValidIdentifier call in
update(); update all occurrences referenced (the calls around lines 11-13,
56-58, 74-78, 100-102) so every method validates and fails fast on an invalid
tableName.


List<String> fields = (List<String>) params.get("fields");
Map<String, Object> conditions = (Map<String, Object>) params.get("conditions");
Integer pageNum = (Integer) params.get("pageNum");
Integer pageSize = (Integer) params.get("pageSize");
String orderBy = (String) params.get("orderBy");
String orderType = (String) params.get("orderType");

SQL sql = new SQL();

// 选择字段
if (fields != null && !fields.isEmpty()) {
for (String field : fields) {
Expand All @@ -29,6 +30,8 @@ public String select(Map<String, Object> params) {

sql.FROM(tableName);



// 条件
if (conditions != null && !conditions.isEmpty()) {
for (Map.Entry<String, Object> entry : conditions.entrySet()) {
Expand All @@ -52,6 +55,8 @@ public String select(Map<String, Object> params) {

public String insert(Map<String, Object> params) {
String tableName = (String) params.get("tableName");
SQLIdentifierValidator.isValidIdentifier(tableName);

Map<String, Object> data = (Map<String, Object>) params.get("data");

SQL sql = new SQL();
Expand All @@ -68,6 +73,9 @@ public String insert(Map<String, Object> params) {

public String update(Map<String, Object> params) {
String tableName = (String) params.get("tableName");
SQLIdentifierValidator.isValidIdentifier(tableName);

SQLIdentifierValidator.isValidIdentifier(tableName);
Map<String, Object> data = (Map<String, Object>) params.get("data");
Map<String, Object> conditions = (Map<String, Object>) params.get("conditions");

Expand All @@ -91,6 +99,8 @@ public String update(Map<String, Object> params) {

public String delete(Map<String, Object> params) {
String tableName = (String) params.get("tableName");
SQLIdentifierValidator.isValidIdentifier(tableName);

Map<String, Object> conditions = (Map<String, Object>) params.get("conditions");

SQL sql = new SQL();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package com.tinyengine.it.dynamic.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Data;

import java.util.List;
import java.util.Map;

@Data
public class DynamicQuery {

@NotBlank(message = "表英文名不能为空")
@Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "模型名称格式不正确")
private String nameEn; // 表名
private String nameCh; // 表中文名
@Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "字段名称格式不正确")
private List<String> fields; // 查询字段
Comment on lines +16 to 17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Jakarta Bean Validation @pattern supported validated types and container element validation List<@Pattern String>

💡 Result:

Jakarta Bean Validation @Pattern supports validation of CharSequence types, specifically String and other CharSequence implementations. It does not directly support other primitive or object types like Integer or List. Container element validation for List<@Pattern String> is supported in Bean Validation 2.0+ (including Jakarta Bean Validation 3.0). Constraints like @Pattern can be placed on the type parameter of generic containers such as List, Set, Map (values), arrays, and Optional. Built-in value extractors handle Iterable (including List), arrays, Map values/keys, and Optional. Example: private List<@Pattern(regexp = "[a-zA-Z0-9]*") String> myList; This validates each String element in the List against the regex. Null elements are valid per @Pattern spec. Empty collections skip element validation. This works via value extraction: the validation engine uses extractors for List/Iterable to pull each String and apply the @Pattern constraint.

Citations:


🏁 Script executed:

cat -n base/src/main/java/com/tinyengine/it/dynamic/dto/DynamicQuery.java | head -50

Repository: opentiny/tiny-engine-backend-java

Length of output: 1145


Apply @Pattern to the list elements, not the List itself.

@Pattern on List<String> does not validate each field name; use container-element validation so each requested field is checked against the pattern.

Proposed fix
-	`@Pattern`(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "字段名称格式不正确")
-	private List<String> fields;       // 查询字段
+	private List<@Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "字段名称格式不正确") String> fields;       // 查询字段
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "字段名称格式不正确")
private List<String> fields; // 查询字段
private List<@Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "字段名称格式不正确") String> fields; // 查询字段
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@base/src/main/java/com/tinyengine/it/dynamic/dto/DynamicQuery.java` around
lines 16 - 17, The `@Pattern` is currently applied to the List field itself and
therefore won't validate each element; update the declaration in class
DynamicQuery so the pattern is applied to the list's element type (i.e., change
the annotation placement to the container-element form so each String in fields
is validated with the regexp "^[a-zA-Z_][a-zA-Z0-9_]*$" and message
"字段名称格式不正确"). Ensure you import the correct javax.validation.constraints.Pattern
(or jakarta equivalent) and keep the field name fields and type List<String>
unchanged.

private Map<String, Object> params; // 查询条件
private Integer currentPage = 1; // 页码
private Integer pageSize = 10; // 每页大小
@Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "排序字段格式不正确")
private String orderBy; // 排序字段
@Pattern(regexp = "ASC|DESC", message = "排序方式必须为ASC或DESC")
private String orderType = "ASC"; // 排序方式
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.tinyengine.it.common.context.LoginUserContext;
import com.tinyengine.it.dynamic.dao.ModelDataDao;
import com.tinyengine.it.dynamic.dto.*;
import com.tinyengine.it.dynamic.util.SQLIdentifierValidator;
import com.tinyengine.it.model.entity.Model;
import com.tinyengine.it.service.material.ModelService;
import jakarta.transaction.Transactional;
Expand Down Expand Up @@ -37,12 +38,11 @@ public List<JSONObject> query(DynamicQuery dto) {
String tableName = getTableName(dto.getNameEn());
Map<String, Object> params = new HashMap<>();
params.put("tableName", tableName);
params.put("fields", dto.getFields());
params.put("conditions", dto.getParams());
params.put("fields", dto.getFields());
params.put("pageNum", dto.getCurrentPage());
params.put("pageSize", dto.getPageSize());
params.put("orderBy", dto.getOrderBy());
params.put("orderType", dto.getOrderType());


return dynamicDao.select(params);
}
Expand Down Expand Up @@ -78,6 +78,10 @@ public Map<String, Object> queryWithPage(DynamicQuery dto) {
if( dto.getPageSize() == null || dto.getPageSize() <= 0) {
dto.setPageSize(10);
}
List<String> fields = dto.getFields();
// 验证字段列表
validateFields(fields);
// 验证表和数据
validateTableExists(dto.getNameEn());
validateTableAndData(dto.getNameEn(), dto.getParams());
List<JSONObject> list = query(dto);
Expand Down Expand Up @@ -222,7 +226,21 @@ private void validateTableAndData(String tableName, Map<String, Object> data) {
// 验证字段名格式
for (String field : data.keySet()) {
if (!field.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
throw new IllegalArgumentException("字段名格式不正确: " + field);
throw new IllegalArgumentException("查询字段名格式不正确: " + field);
}
}
}

/**
* 验证字段列表
* @param fields
*/
private void validateFields(List<String> fields) {
if (fields != null) {
for (String field : fields) {
if (!field.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
throw new IllegalArgumentException("Field name format is invalid: " + field);
}
}
}
}
Comment on lines +238 to 246

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the identifier validator here to handle nulls consistently.

field.matches(...) throws NullPointerException for a null element; SQLIdentifierValidator.isValidIdentifier() already handles null/blank values safely.

Proposed fix
 	private void validateFields(List<String> fields) {
 		if (fields != null) {
 			for (String field : fields) {
-				if (!field.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
+				if (!SQLIdentifierValidator.isValidIdentifier(field)) {
 					throw new IllegalArgumentException("Field name format is invalid: " + field);
 				}
 			}
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@base/src/main/java/com/tinyengine/it/dynamic/service/DynamicService.java`
around lines 238 - 246, validateFields currently uses field.matches(...) which
throws NPE for null elements; replace that regex check with a call to
SQLIdentifierValidator.isValidIdentifier(field) so null/blank values are handled
consistently. In method validateFields(List<String> fields) iterate as before
but use SQLIdentifierValidator.isValidIdentifier(field) and throw the same
IllegalArgumentException("Field name format is invalid: " + field) when it
returns false; keep the outer null check for fields.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.tinyengine.it.dynamic.util;

public class SQLIdentifierValidator {

private static final String IDENTIFIER_REGEX = "^[a-zA-Z_][a-zA-Z0-9_]*$";

/**
* Validates a SQL identifier (e.g., table name, column name).
*
* @param identifier the identifier to validate
* @return true if valid, false otherwise
*/
public static boolean isValidIdentifier(String identifier) {
if (identifier == null || identifier.trim().isEmpty()) {
return false;
}
return identifier.matches(IDENTIFIER_REGEX);
}

/**
* Validates a list of SQL identifiers.
*
* @param identifiers the list of identifiers to validate
* @throws IllegalArgumentException if any identifier is invalid
*/
public static void validateIdentifiers(Iterable<String> identifiers) {
if (identifiers != null) {
for (String identifier : identifiers) {
if (!isValidIdentifier(identifier)) {
throw new IllegalArgumentException("Invalid SQL identifier: " + identifier);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ public void addInterceptors(InterceptorRegistry registry) {
"/app-center/api/ai/chat",
"/app-center/api/chat/completions",
// 图片文件资源下载
"/material-center/api/resource/download/*",
//模型驱动
"/platform-center/api/model-data/**"
"/material-center/api/resource/download/*"
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ public class JwtUtil {
private TokenBlacklistService tokenBlacklistService;

private static final long EXPIRATION_TIME = 21600000L; // 6小时 = 6 * 60 * 60 * 1000 = 21600000 毫秒
private static final String DEFAULT_SECRET = "tiny-engine-backend-secret-key-at-jwt-login";

// 避免启动时环境变量未加载的问题
private static String getSecretString() {
return Optional.ofNullable(System.getenv("SECRET_STRING"))
.orElse(DEFAULT_SECRET);
return System.getenv("SECRET_STRING");
}

public static SecretKey getSecretKey() {
Expand Down
Loading