Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions parsers/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,29 @@ type Framework struct {
Froms map[From]bool `json:"froms,omitempty"`
Tags []string `json:"tags,omitempty"`
IsFocus bool `json:"is_focus,omitempty"`
Judge *Judgement `json:"judge,omitempty"` // 判定层(fingers/judge)的结论, nil 表示未经判定
MatchDetail *MatchDetail `json:"matcher,omitempty"`
*Attributes `json:"attributes,omitempty"`
}

// Judgement is a judgement layer's ruling on the claim that a framework
// produced the response (see github.com/chainreactors/fingers/judge).
// Rejected and duplicate entries are kept so callers can explain them;
// Frameworks.Accepted drops them.
type Judgement struct {
// Option is the claim option the ruling picked, such as "declared"
// (the response names the product in a header or cookie, decided without
// a model), "running", "mentioned" or "unrelated".
Option string `json:"option,omitempty"`
// Outcome is what Option means for the claim: "holds", "refuted" or
// "insufficient" (the evidence decides neither way).
Outcome string `json:"outcome,omitempty"`
Confidence float64 `json:"confidence,omitempty"` // the provider's confidence in Option; 1 for code's rulings
Evidence []string `json:"evidence,omitempty"` // the response excerpts the claim was ruled on
Rejected bool `json:"rejected,omitempty"` // the action taken: not reported (refuted, or insufficient where so configured)
Duplicate bool `json:"duplicate,omitempty"` // another engine's spelling of a product kept under another name
}

// MatchDetail describes which rule and matcher produced a hit.
type MatchDetail struct {
RuleIndex int `json:"rule_index,omitempty"`
Expand Down Expand Up @@ -227,6 +246,9 @@ func (fs Frameworks) Add(other *Framework) bool {
}
frame.Tags = iutils.StringsUnique(append(frame.Tags, other.Tags...))
frame.UpdateAttributes(other.Attributes)
if frame.Judge == nil {
frame.Judge = other.Judge
}
return false
} else {
fs[other.Name] = other
Expand Down Expand Up @@ -337,3 +359,15 @@ func (fs Frameworks) HasFrom(from string) bool {
}
return false
}

// Accepted returns the frameworks without judged false positives and
// duplicate spellings. Frameworks that were not judged are kept.
func (fs Frameworks) Accepted() Frameworks {
out := make(Frameworks, len(fs))
for k, f := range fs {
if f != nil && (f.Judge == nil || !f.Judge.Rejected && !f.Judge.Duplicate) {
out[k] = f
}
}
return out
}
43 changes: 43 additions & 0 deletions parsers/framework_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package parsers

import (
"encoding/json"
"testing"
)

func TestFrameworksJudgement(t *testing.T) {
fs := Frameworks{}
fs.Add(&Framework{Name: "nginx", Judge: &Judgement{Option: "running", Outcome: "holds", Evidence: []string{"Server: nginx"}, Confidence: 0.9}})
fs.Add(&Framework{Name: "jenkins", Judge: &Judgement{Option: "insufficient", Outcome: "insufficient"}})
fs.Add(&Framework{Name: "tomcat", Judge: &Judgement{Rejected: true}})
fs.Add(&Framework{Name: "apache-tomcat", Judge: &Judgement{Duplicate: true}})
fs.Add(&Framework{Name: "unjudged"})

accepted := fs.Accepted()
if len(accepted) != 3 || accepted["tomcat"] != nil || accepted["apache-tomcat"] != nil || accepted["unjudged"] == nil {
t.Fatalf("accepted %v", accepted.GetNames())
}

data, err := json.Marshal(fs["nginx"])
if err != nil {
t.Fatal(err)
}
var back Framework
if err := json.Unmarshal(data, &back); err != nil || back.Judge == nil || back.Judge.Outcome != "holds" || back.Judge.Option != "running" || len(back.Judge.Evidence) != 1 || back.Judge.Confidence != 0.9 {
t.Fatalf("round trip %s -> %+v", data, back.Judge)
}
if data, _ := json.Marshal(fs["unjudged"]); string(data) != `{"name":"unjudged"}` {
t.Fatalf("unjudged serializes as %s", data)
}
}

func TestFrameworksAddKeepsJudgement(t *testing.T) {
fs := Frameworks{}
fs.Add(NewFramework("nginx", FrameFromDefault))
judged := NewFramework("nginx", FrameFromDefault)
judged.Judge = &Judgement{Option: "running", Outcome: "holds"}
fs.Add(judged)
if fs["nginx"].Judge == nil {
t.Fatal("judgement lost on merge")
}
}