mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-16 01:42:21 +08:00
102 lines
2.4 KiB
Go
102 lines
2.4 KiB
Go
package safety
|
|
|
|
import (
|
|
"strings"
|
|
"unicode"
|
|
|
|
"GoNavi-Wails/internal/ai"
|
|
)
|
|
|
|
// ClassifySQL 分类 SQL 语句的操作类型
|
|
func ClassifySQL(sql string) ai.SQLOperationType {
|
|
keyword := leadingSQLKeyword(sql)
|
|
switch keyword {
|
|
case "select", "with", "show", "describe", "desc", "explain", "pragma", "values":
|
|
return ai.SQLOpQuery
|
|
case "insert", "update", "delete", "replace", "merge", "upsert":
|
|
return ai.SQLOpDML
|
|
case "create", "alter", "drop", "truncate", "rename":
|
|
return ai.SQLOpDDL
|
|
default:
|
|
return ai.SQLOpOther
|
|
}
|
|
}
|
|
|
|
// IsHighRiskSQL 判断 SQL 是否为高风险语句
|
|
func IsHighRiskSQL(sql string) (bool, string) {
|
|
keyword := leadingSQLKeyword(sql)
|
|
normalized := strings.ToLower(sql)
|
|
|
|
switch keyword {
|
|
case "drop":
|
|
return true, "ai_service.backend.warning.sql_drop"
|
|
case "truncate":
|
|
return true, "ai_service.backend.warning.sql_truncate"
|
|
case "delete":
|
|
if !containsWhereClause(normalized) {
|
|
return true, "ai_service.backend.warning.sql_delete_without_where"
|
|
}
|
|
case "update":
|
|
if !containsWhereClause(normalized) {
|
|
return true, "ai_service.backend.warning.sql_update_without_where"
|
|
}
|
|
}
|
|
|
|
return false, ""
|
|
}
|
|
|
|
// containsWhereClause 简单判断 SQL 是否包含 WHERE 子句
|
|
func containsWhereClause(normalizedSQL string) bool {
|
|
return strings.Contains(normalizedSQL, " where ") ||
|
|
strings.Contains(normalizedSQL, "\nwhere ") ||
|
|
strings.Contains(normalizedSQL, "\twhere ")
|
|
}
|
|
|
|
// leadingSQLKeyword 提取 SQL 语句的首个关键字(跳过注释和空白)
|
|
func leadingSQLKeyword(query string) string {
|
|
text := strings.TrimSpace(query)
|
|
for len(text) > 0 {
|
|
trimmed := strings.TrimLeft(text, " \t\r\n")
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
text = trimmed
|
|
|
|
switch {
|
|
case strings.HasPrefix(text, "--"):
|
|
if idx := strings.IndexByte(text, '\n'); idx >= 0 {
|
|
text = text[idx+1:]
|
|
continue
|
|
}
|
|
return ""
|
|
case strings.HasPrefix(text, "#"):
|
|
if idx := strings.IndexByte(text, '\n'); idx >= 0 {
|
|
text = text[idx+1:]
|
|
continue
|
|
}
|
|
return ""
|
|
case strings.HasPrefix(text, "/*"):
|
|
if idx := strings.Index(text, "*/"); idx >= 0 {
|
|
text = text[idx+2:]
|
|
continue
|
|
}
|
|
return ""
|
|
}
|
|
break
|
|
}
|
|
|
|
if text == "" {
|
|
return ""
|
|
}
|
|
for i, r := range text {
|
|
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
|
|
continue
|
|
}
|
|
if i == 0 {
|
|
return ""
|
|
}
|
|
return strings.ToLower(text[:i])
|
|
}
|
|
return strings.ToLower(text)
|
|
}
|