package aitools import "testing" func TestConfirmBindHashStableAndSensitive(t *testing.T) { a := ConfirmBindHash("c1", "INSERT INTO t VALUES (1)") b := ConfirmBindHash("c1", " INSERT INTO t VALUES (1) ") if a != b { t.Fatalf("normalize mismatch %s vs %s", a, b) } c := ConfirmBindHash("c1", "INSERT INTO t VALUES (2)") if a == c { t.Fatal("different sql should differ") } if err := VerifyConfirmBind("c1", "INSERT INTO t VALUES (1)", a); err != nil { t.Fatal(err) } if err := VerifyConfirmBind("c1", "INSERT INTO t VALUES (2)", a); err == nil { t.Fatal("want mismatch") } if err := VerifyConfirmBind("c1", "INSERT INTO t VALUES (1)", ""); err == nil { t.Fatal("want empty bind error") } } func TestNeedConfirmIncludesBind(t *testing.T) { rt := NewRuntime(Policy{ Mode: ModeReadWrite, AllowedConnectionIDs: []string{"c1"}, DefaultLimit: 100, MaxLimit: 500, }) rt.RejectCallerConfirm = true rt.FindConn = func(id string) (*SavedConnection, error) { return &SavedConnection{ID: id, Driver: "mysql", Host: "127.0.0.1"}, nil } rt.Audit = func(AuditEntry) {} out, err := rt.Call(nil, ToolExecuteQuery, map[string]any{ "connection_id": "c1", "sql": "DELETE FROM users WHERE id=1", }) if err != nil { t.Fatal(err) } m, ok := out.(map[string]any) if !ok || m["needConfirm"] != true { t.Fatalf("%v", out) } bind, _ := m["confirmBind"].(string) if bind == "" { t.Fatal("missing confirmBind") } want := ConfirmBindHash("c1", "DELETE FROM users WHERE id=1") if bind != want { t.Fatalf("bind %s want %s", bind, want) } }