mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-18 07:32:20 +02:00
feat: add configurable tool call blocking and monitoring
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cyberstrike-ai/internal/mcp"
|
||||
)
|
||||
|
||||
const legacyToolGuardPrefix = "工具调用已被安全规则拦截"
|
||||
|
||||
// Only the exact envelope emitted by the old local guard is recognized here.
|
||||
// New executions use the structured marker and never infer policy from text.
|
||||
func isLegacyToolGuardRefusal(text string) bool {
|
||||
if !strings.HasPrefix(text, legacyToolGuardPrefix+":") && !strings.HasPrefix(text, legacyToolGuardPrefix+"\n规则: ") {
|
||||
return false
|
||||
}
|
||||
matchIndex := strings.LastIndex(text, "\n匹配内容: ")
|
||||
if matchIndex < 0 {
|
||||
return false
|
||||
}
|
||||
if _, err := strconv.Unquote(text[matchIndex+len("\n匹配内容: "):]); err != nil {
|
||||
return false
|
||||
}
|
||||
ruleIndex := strings.LastIndex(text[:matchIndex], "\n规则: ")
|
||||
if ruleIndex < 0 {
|
||||
return false
|
||||
}
|
||||
rule := text[ruleIndex+len("\n规则: ") : matchIndex]
|
||||
idIndex := strings.LastIndex(rule, " (")
|
||||
return idIndex > 0 && strings.HasSuffix(rule, ")") && len(rule[idIndex+2:len(rule)-1]) > 0 && !strings.Contains(rule, "\n")
|
||||
}
|
||||
|
||||
// migrateLegacyToolGuardBlocks is idempotent because only failed records qualify.
|
||||
// Keeping status and accumulated failure counts in one transaction makes monitor
|
||||
// filters, badges and statistics agree immediately after upgrading.
|
||||
func (db *DB) migrateLegacyToolGuardBlocks() error {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
rows, err := tx.Query(`SELECT id, tool_name, error, COALESCE(result, '') FROM tool_executions WHERE status = 'failed' AND error LIKE ?`, legacyToolGuardPrefix+"%")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
type record struct{ id, tool, reason, result string }
|
||||
var records []record
|
||||
for rows.Next() {
|
||||
var r record
|
||||
if err := rows.Scan(&r.id, &r.tool, &r.reason, &r.result); err != nil {
|
||||
rows.Close()
|
||||
return err
|
||||
}
|
||||
if isLegacyToolGuardRefusal(r.reason) {
|
||||
records = append(records, r)
|
||||
}
|
||||
}
|
||||
err = rows.Err()
|
||||
rows.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range records {
|
||||
var result mcp.ToolResult
|
||||
_ = json.Unmarshal([]byte(r.result), &result)
|
||||
if len(result.Content) == 0 {
|
||||
result.Content = []mcp.Content{{Type: "text", Text: r.reason}}
|
||||
}
|
||||
result.Blocked, result.IsError = true, true
|
||||
encoded, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.Exec(`UPDATE tool_executions SET status = 'blocked', result = ? WHERE id = ?`, string(encoded), r.id); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.Exec(`UPDATE tool_stats SET failed_calls = MAX(0, failed_calls - 1) WHERE tool_name = ?`, r.tool); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
Reference in New Issue
Block a user