mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-06-27 16:29:59 +02:00
162 lines
4.9 KiB
Go
162 lines
4.9 KiB
Go
package project
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"cyberstrike-ai/internal/config"
|
|
"cyberstrike-ai/internal/database"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestFormatIncomingLinksHint(t *testing.T) {
|
|
t.Parallel()
|
|
hint := FormatIncomingLinksHint([]*database.ProjectFactEdge{
|
|
{EdgeType: "discovered_on", SourceFactKey: "finding/x", Confidence: "tentative"},
|
|
})
|
|
if !strings.Contains(hint, "入边:") {
|
|
t.Fatalf("expected 入边 label: %q", hint)
|
|
}
|
|
if !strings.Contains(hint, "discovered_on←finding/x") {
|
|
t.Fatalf("unexpected hint: %q", hint)
|
|
}
|
|
if !strings.Contains(hint, "tentative") {
|
|
t.Fatalf("expected tentative in hint: %q", hint)
|
|
}
|
|
}
|
|
|
|
func TestFormatIncomingLinksHint_allEdges(t *testing.T) {
|
|
t.Parallel()
|
|
edges := make([]*database.ProjectFactEdge, 0, 5)
|
|
for i := 1; i <= 5; i++ {
|
|
edges = append(edges, &database.ProjectFactEdge{
|
|
EdgeType: "discovered_on",
|
|
SourceFactKey: fmt.Sprintf("finding/f%d", i),
|
|
Confidence: "tentative",
|
|
})
|
|
}
|
|
hint := FormatIncomingLinksHint(edges)
|
|
if strings.Contains(hint, "+") {
|
|
t.Fatalf("should not truncate with +N: %q", hint)
|
|
}
|
|
for i := 1; i <= 5; i++ {
|
|
if !strings.Contains(hint, fmt.Sprintf("finding/f%d", i)) {
|
|
t.Fatalf("missing edge f%d in hint: %q", i, hint)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFormatFactIndexLinksHint_incomingOnly(t *testing.T) {
|
|
t.Parallel()
|
|
in := []*database.ProjectFactEdge{
|
|
{EdgeType: "discovered_on", SourceFactKey: "target/dev", Confidence: "tentative"},
|
|
{EdgeType: "exploits", SourceFactKey: "exploit/rce", Confidence: "confirmed"},
|
|
}
|
|
hint := FormatFactIndexLinksHint("finding/sqli", in)
|
|
if !strings.Contains(hint, "关系边:") {
|
|
t.Fatalf("missing 关系边 label: %q", hint)
|
|
}
|
|
if !strings.Contains(hint, "discovered_on←target/dev") {
|
|
t.Fatalf("missing discovered_on: %q", hint)
|
|
}
|
|
if !strings.Contains(hint, "exploits←exploit/rce") {
|
|
t.Fatalf("missing exploits: %q", hint)
|
|
}
|
|
if strings.Contains(hint, "出边") || strings.Contains(hint, "入边") {
|
|
t.Fatalf("should not use legacy 出边/入边 labels: %q", hint)
|
|
}
|
|
}
|
|
|
|
func TestFormatFactIndexLinksHint_includesAuxiliaryEdgeTypes(t *testing.T) {
|
|
t.Parallel()
|
|
in := []*database.ProjectFactEdge{{EdgeType: "supports", SourceFactKey: "note/log"}}
|
|
hint := FormatFactIndexLinksHint("finding/x", in)
|
|
if !strings.Contains(hint, "supports←note/log") {
|
|
t.Fatalf("supports edge should be included: %q", hint)
|
|
}
|
|
}
|
|
|
|
func TestBuildFactPathOverviewSection(t *testing.T) {
|
|
t.Parallel()
|
|
edges := []*database.ProjectFactEdge{
|
|
{EdgeType: "discovered_on", SourceFactKey: "target/dev", TargetFactKey: "finding/sqli", Confidence: "tentative"},
|
|
{EdgeType: "exploits", SourceFactKey: "exploit/rce", TargetFactKey: "finding/sqli", Confidence: "confirmed"},
|
|
{EdgeType: "supports", SourceFactKey: "note/log", TargetFactKey: "finding/sqli"},
|
|
}
|
|
keys := map[string]struct{}{
|
|
"target/dev": {}, "finding/sqli": {}, "exploit/rce": {}, "note/log": {},
|
|
}
|
|
section := BuildFactPathOverviewSection(edges, keys, 800)
|
|
if !strings.Contains(section, "### 攻击路径(事实关系)") {
|
|
t.Fatalf("missing header: %q", section)
|
|
}
|
|
if !strings.Contains(section, "target/dev → finding/sqli") {
|
|
t.Fatalf("missing discovered_on line: %q", section)
|
|
}
|
|
if !strings.Contains(section, "exploit/rce → finding/sqli") {
|
|
t.Fatalf("missing exploits line: %q", section)
|
|
}
|
|
if !strings.Contains(section, "note/log → finding/sqli") {
|
|
t.Fatalf("supports edge should be included: %q", section)
|
|
}
|
|
}
|
|
|
|
func TestBuildFactIndexBlock_withLinksAndPathOverview(t *testing.T) {
|
|
t.Parallel()
|
|
dbPath := filepath.Join(t.TempDir(), "facts.db")
|
|
db, err := database.NewDB(dbPath, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
|
|
proj, err := db.CreateProject(&database.Project{Name: "path-proj"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = db.UpsertProjectFact(&database.ProjectFact{
|
|
ProjectID: proj.ID,
|
|
FactKey: "target/dev",
|
|
Category: "target",
|
|
Summary: "dev 子域",
|
|
Confidence: "confirmed",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = db.UpsertProjectFact(&database.ProjectFact{
|
|
ProjectID: proj.ID,
|
|
FactKey: "finding/sqli",
|
|
Category: "finding",
|
|
Summary: "时间盲注",
|
|
Confidence: "tentative",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = db.AddProjectFactEdge(proj.ID, database.ProjectFactEdgeInput{
|
|
To: "finding/sqli",
|
|
Type: "discovered_on",
|
|
}, "target/dev", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block, err := BuildFactIndexBlock(db, proj.ID, config.ProjectConfig{Enabled: true, FactIndexMaxRunes: 6500, FactIndexPathMaxRunes: 1000})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(block, "关系边: discovered_on←target/dev") {
|
|
t.Fatalf("finding line should include relation hint: %q", block)
|
|
}
|
|
if !strings.Contains(block, "### 攻击路径(事实关系)") {
|
|
t.Fatalf("missing relation overview: %q", block)
|
|
}
|
|
if !strings.Contains(block, "target/dev → finding/sqli") {
|
|
t.Fatalf("missing overview edge: %q", block)
|
|
}
|
|
}
|