chore: 恢复旧布局基线(迁移改动丢失后重建,apps/packages 布局暂存)
This commit is contained in:
1 parent
a9510235bb
commit
456fa6954b
203 files changed
+18787
-48
No files matched your search
@@ -0,0 +1,107 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 危险语句规则:词边界匹配,作用于 StripComments 之后的纯代码文本。
|
||||
// 词表:DROP/TRUNCATE/ALTER/GRANT/REVOKE/CREATE USER|ROLE/RENAME TABLE/
|
||||
// REPLACE INTO/LOAD DATA/INTO OUTFILE|DUMPFILE/xp_cmdshell/COPY..PROGRAM/EXEC/CALL。
|
||||
var dangerPatterns = []struct {
|
||||
re *regexp.Regexp
|
||||
desc string
|
||||
}{
|
||||
{regexp.MustCompile(`\bDROP\b`), "DROP"},
|
||||
{regexp.MustCompile(`\bTRUNCATE\b`), "TRUNCATE"},
|
||||
{regexp.MustCompile(`\bALTER\b`), "ALTER"},
|
||||
{regexp.MustCompile(`\bGRANT\b`), "GRANT"},
|
||||
{regexp.MustCompile(`\bREVOKE\b`), "REVOKE"},
|
||||
{regexp.MustCompile(`\bCREATE\s+(USER|ROLE)\b`), "CREATE USER/ROLE"},
|
||||
{regexp.MustCompile(`\bRENAME\s+TABLE\b`), "RENAME TABLE"},
|
||||
{regexp.MustCompile(`\bREPLACE\s+INTO\b`), "REPLACE INTO"},
|
||||
{regexp.MustCompile(`\bLOAD\s+DATA\b`), "LOAD DATA"},
|
||||
{regexp.MustCompile(`\bINTO\s+(OUTFILE|DUMPFILE)\b`), "INTO OUTFILE/DUMPFILE"},
|
||||
{regexp.MustCompile(`(?i)\bxp_cmdshell\b`), "xp_cmdshell"},
|
||||
{regexp.MustCompile(`\bCOPY\b[^;]*\b(FROM|TO)\s+PROGRAM\b`), "COPY FROM/TO PROGRAM"},
|
||||
}
|
||||
|
||||
var (
|
||||
reWriteNoWhere = regexp.MustCompile(`\b(DELETE|UPDATE)\b`)
|
||||
reWhere = regexp.MustCompile(`\bWHERE\b`)
|
||||
reExecCallHead = regexp.MustCompile(`^(EXEC|EXECUTE|CALL)\b`)
|
||||
)
|
||||
|
||||
// CheckDangerousSQL 服务端权威的危险语句判定(前端预检仅为 UX)。
|
||||
// 逐语句检查;注释/字符串内的关键字不计;MySQL /*! */ 可执行注释内容参与检查;
|
||||
// WITH 包裹的写操作因全文扫描 CTE 体同样命中。
|
||||
// 返回命中的危险原因清单(去重)。
|
||||
func CheckDangerousSQL(driver, sqlText string) []string {
|
||||
_ = driver // 词表当前各驱动通用,保留参数便于后续按方言细化
|
||||
seen := map[string]struct{}{}
|
||||
var hits []string
|
||||
add := func(desc, stmt string) {
|
||||
if _, ok := seen[desc]; ok {
|
||||
return
|
||||
}
|
||||
seen[desc] = struct{}{}
|
||||
hits = append(hits, desc+": "+truncateSQL(stmt, 80))
|
||||
}
|
||||
for _, stmt := range SplitStatements(sqlText) {
|
||||
code := strings.ToUpper(StripComments(stmt))
|
||||
code = strings.TrimSpace(code)
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
for _, p := range dangerPatterns {
|
||||
if p.re.MatchString(code) {
|
||||
add(p.desc, stmt)
|
||||
}
|
||||
}
|
||||
// DELETE/UPDATE 无 WHERE(含 WITH CTE 体内的写操作)
|
||||
if reWriteNoWhere.MatchString(code) && !reWhere.MatchString(code) {
|
||||
kw := reWriteNoWhere.FindString(code)
|
||||
add(kw+" 无 WHERE", stmt)
|
||||
}
|
||||
// 存储过程/动态执行仅匹配语句开头,降低误报
|
||||
if m := reExecCallHead.FindString(code); m != "" {
|
||||
add(m, stmt)
|
||||
}
|
||||
}
|
||||
return hits
|
||||
}
|
||||
|
||||
// IsDangerousSQL 兼容旧调用:命中任意危险规则即 true。
|
||||
func IsDangerousSQL(sql string) bool {
|
||||
return len(CheckDangerousSQL("", sql)) > 0
|
||||
}
|
||||
|
||||
func truncateSQL(s string, n int) string {
|
||||
s = strings.Join(strings.Fields(s), " ")
|
||||
if len(s) <= n {
|
||||
return s
|
||||
}
|
||||
return s[:n] + "…"
|
||||
}
|
||||
|
||||
func stripSQLLead(sql string) string {
|
||||
s := strings.TrimSpace(sql)
|
||||
for {
|
||||
if strings.HasPrefix(s, "--") {
|
||||
if i := strings.IndexByte(s, '\n'); i >= 0 {
|
||||
s = strings.TrimSpace(s[i+1:])
|
||||
continue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if strings.HasPrefix(s, "/*") {
|
||||
if i := strings.Index(s, "*/"); i >= 0 {
|
||||
s = strings.TrimSpace(s[i+2:])
|
||||
continue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
break
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in new issue
Block a user