mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-11-14 16:27:01 +01:00
184 lines
7.6 KiB
Go
184 lines
7.6 KiB
Go
package database
|
||
|
||
import (
|
||
"encoding/json"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestVulnerabilityWorkspaceTargetsAndLegacyUpgrade(t *testing.T) {
|
||
db := newRBACTestDB(t)
|
||
v, err := db.CreateVulnerability(&Vulnerability{Title: "multiple", Severity: "high", Target: "https://api.example.co.uk/login;https://store.example.co.uk:8443/orders, http://192.0.2.5:8080/"})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if len(v.Targets) != 3 || v.Targets[0].Domain != "example.co.uk" || v.Targets[2].Domain != "192.0.2.5" {
|
||
t.Fatalf("targets: %+v", v.Targets)
|
||
}
|
||
if _, err := db.Exec("UPDATE vulnerabilities SET targets_json='[]',targets_version=0 WHERE id=?", v.ID); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
for i := 0; i < 2; i++ {
|
||
if err := db.initTables(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
count, err := db.CountVulnerabilityGroupsForAccess(VulnerabilityListFilter{}, RBACListAccess{}, "host")
|
||
if err != nil || count != 2 {
|
||
t.Fatalf("host count: %d %v", count, err)
|
||
}
|
||
rows, err := db.ListVulnerabilitiesByGroupForAccess(2, 0, VulnerabilityListFilter{SummaryOnly: true}, RBACListAccess{}, "host", "")
|
||
if err != nil || len(rows) != 2 || rows[0].ID != v.ID || rows[1].ID != v.ID {
|
||
t.Fatalf("group page: %+v %v", rows, err)
|
||
}
|
||
matched, err := db.ListVulnerabilitiesByGroupForAccess(20, 0, VulnerabilityListFilter{ID: v.ID, HostQuery: "store."}, RBACListAccess{}, "host", "")
|
||
if err != nil || len(matched) != 1 || *matched[0].TargetHost != "store.example.co.uk" {
|
||
t.Fatalf("host search: %+v %v", matched, err)
|
||
}
|
||
}
|
||
|
||
func TestVulnerabilityDomainGroupsPreferNamesAndRepairStoredTargets(t *testing.T) {
|
||
db := newRBACTestDB(t)
|
||
raw := "https://vpn.example.com/login (真实 IP 192.0.2.25,Citrix Gateway 26.6.1.20,Keycloak 4.4.0.final,Ivanti Connect Secure 22.x,getFunction.php)"
|
||
v, err := db.CreateVulnerability(&Vulnerability{Title: "domain with infrastructure notes", Severity: "high", Target: raw})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if len(v.Targets) != 2 || v.Targets[0].Host != "vpn.example.com" || !v.Targets[0].GroupHost || v.Targets[1].Host != "192.0.2.25" || v.Targets[1].GroupHost {
|
||
t.Fatalf("parsed targets: %+v", v.Targets)
|
||
}
|
||
if _, err := db.Exec(`UPDATE vulnerabilities SET targets_json='[{"host":"22.x"}]',targets_version=1 WHERE id=?`, v.ID); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := db.backfillVulnerabilityTargets(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
ip, err := db.CreateVulnerability(&Vulnerability{Title: "IP only", Severity: "high", Target: "192.0.2.5:8080"})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
rows, err := db.ListVulnerabilitiesByGroupForAccess(20, 0, VulnerabilityListFilter{}, RBACListAccess{}, "host", "")
|
||
if err != nil || len(rows) != 2 || rows[0].ID != v.ID || rows[1].ID != ip.ID {
|
||
t.Fatalf("domain-first groups: %+v %v", rows, err)
|
||
}
|
||
got, _ := db.GetVulnerability(v.ID)
|
||
if got.Target != raw || len(got.Targets) != 2 {
|
||
t.Fatalf("backfill changed raw target: %+v", got)
|
||
}
|
||
}
|
||
|
||
func TestVulnerabilityTargetParserIgnoresRelativeEndpoints(t *testing.T) {
|
||
if got := vulnerabilityTargetsJSON("GET /api/config.php HTTP/1.1"); got != "[]" {
|
||
t.Fatalf("relative endpoint parsed as host: %s", got)
|
||
}
|
||
var targets []VulnerabilityTarget
|
||
if err := json.Unmarshal([]byte(vulnerabilityTargetsJSON("https://a.example.com/a,https://b.example.com/b")), &targets); err != nil || len(targets) != 2 {
|
||
t.Fatalf("comma-separated targets: %+v %v", targets, err)
|
||
}
|
||
}
|
||
|
||
func TestVulnerabilityWorkspaceMergeSplitAndScope(t *testing.T) {
|
||
db := newRBACTestDB(t)
|
||
a, _ := db.CreateVulnerability(&Vulnerability{Title: "report A", Severity: "high", Evidence: "request A"})
|
||
b, _ := db.CreateVulnerability(&Vulnerability{Title: "report B", Severity: "medium", Evidence: "request B"})
|
||
if _, err := db.MergeVulnerabilities([]string{a.ID, b.ID}, "issue", "same root cause", "reader", RBACListAccess{UserID: "outsider", Scope: RBACScopeAssigned}); err == nil {
|
||
t.Fatal("unauthorized merge")
|
||
}
|
||
id, err := db.MergeVulnerabilities([]string{a.ID, b.ID}, "issue", "same root cause", "operator", RBACListAccess{})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
rows, err := db.ListVulnerabilitiesByGroupForAccess(1, 0, VulnerabilityListFilter{SummaryOnly: true}, RBACListAccess{}, "case", "")
|
||
if err != nil || len(rows) != 2 || rows[0].CaseID != id || rows[0].Evidence != "" || !rows[0].SummaryOnly {
|
||
t.Fatalf("case summaries: %+v %v", rows, err)
|
||
}
|
||
stats, err := db.GetVulnerabilityStats(VulnerabilityListFilter{})
|
||
if err != nil || stats["distinct"] != 1 || stats["total"] != 2 {
|
||
t.Fatalf("stats: %+v %v", stats, err)
|
||
}
|
||
got, err := db.GetVulnerability(a.ID)
|
||
if err != nil || got.Evidence != "request A" {
|
||
t.Fatal("original evidence changed", err)
|
||
}
|
||
if _, err := db.MergeVulnerabilities([]string{a.ID, b.ID}, "again", "retry", "operator", RBACListAccess{}); err == nil {
|
||
t.Fatal("accepted already merged records")
|
||
}
|
||
if err := db.DissolveVulnerabilityCase(id, "different causes", "outsider", RBACListAccess{UserID: "outsider", Scope: RBACScopeAssigned}); err == nil {
|
||
t.Fatal("unauthorized split")
|
||
}
|
||
if err := db.DissolveVulnerabilityCase(id, "different causes", "operator", RBACListAccess{}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
got, _ = db.GetVulnerability(a.ID)
|
||
if got.CaseID != "" || got.Evidence != "request A" {
|
||
t.Fatal("split damaged original")
|
||
}
|
||
history, err := db.VulnerabilityHistory(a.ID)
|
||
if err != nil || len(history) != 2 {
|
||
t.Fatalf("history: %+v %v", history, err)
|
||
}
|
||
}
|
||
|
||
func TestVulnerabilityWorkspaceRetestIdempotencyAndSnapshot(t *testing.T) {
|
||
db := newRBACTestDB(t)
|
||
source, _ := db.CreateConversation("source", ConversationCreateMeta{})
|
||
v, _ := db.CreateVulnerability(&Vulnerability{Title: "finding", Evidence: "original proof", Severity: "high", ConversationID: source.ID})
|
||
branch, err := db.CreateVulnerabilityRetest(v, ConversationCreateMeta{})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var raw string
|
||
if err := db.QueryRow("SELECT snapshot_json FROM vulnerability_retests WHERE conversation_id=?", branch.ID).Scan(&raw); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var snapshot Vulnerability
|
||
if err := json.Unmarshal([]byte(raw), &snapshot); err != nil || snapshot.Evidence != "original proof" {
|
||
t.Fatal("snapshot", err)
|
||
}
|
||
if err := db.UpdateVulnerabilityRetestResult(v.ID, "fixed", "403 verified", branch.ID); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
v.Status = "confirmed"
|
||
v.Evidence = "new proof"
|
||
if err := db.UpdateVulnerability(v.ID, v); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := db.UpdateVulnerabilityRetestResult(v.ID, "fixed", "403 verified", branch.ID); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
got, _ := db.GetVulnerability(v.ID)
|
||
if got.Status != "confirmed" || strings.Count(got.RetestLog, "403 verified") != 1 {
|
||
t.Fatalf("retry changed state: %+v", got)
|
||
}
|
||
history, err := db.VulnerabilityHistory(v.ID)
|
||
if err != nil || len(history) != 1 || history[0].Status != "fixed" {
|
||
t.Fatalf("history: %+v %v", history, err)
|
||
}
|
||
}
|
||
|
||
func TestVulnerabilityWorkspaceProjectBoundary(t *testing.T) {
|
||
db := newRBACTestDB(t)
|
||
p1, _ := db.CreateProject(&Project{Name: "one"})
|
||
p2, _ := db.CreateProject(&Project{Name: "two"})
|
||
a, _ := db.CreateVulnerability(&Vulnerability{Title: "a", Severity: "high", ProjectID: p1.ID})
|
||
b, _ := db.CreateVulnerability(&Vulnerability{Title: "b", Severity: "high", ProjectID: p2.ID})
|
||
if _, err := db.MergeVulnerabilities([]string{a.ID, b.ID}, "issue", "similar", "op", RBACListAccess{}); err == nil {
|
||
t.Fatal("cross project merge")
|
||
}
|
||
b.ProjectID = p1.ID
|
||
if err := db.UpdateVulnerability(b.ID, b); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if _, err := db.MergeVulnerabilities([]string{a.ID, b.ID}, "issue", "same", "op", RBACListAccess{}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
b.ProjectID = p2.ID
|
||
if err := db.UpdateVulnerability(b.ID, b); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
got, _ := db.GetVulnerability(b.ID)
|
||
if got.CaseID != "" {
|
||
t.Fatal("project move retained case")
|
||
}
|
||
}
|