feat: add N14 selector report model
This commit is contained in:
102
internal/uiaselector/report.go
Normal file
102
internal/uiaselector/report.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package uiaselector
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
StrictExitOK = 0
|
||||
StrictExitReportGenerationFailure = 1
|
||||
StrictExitUnmatched = 2
|
||||
StrictExitAmbiguous = 3
|
||||
StrictExitInvalidCatalog = 4
|
||||
)
|
||||
|
||||
type Report struct {
|
||||
OK bool `json:"ok"`
|
||||
SourceDump string `json:"source_dump"`
|
||||
CatalogSize int `json:"catalog_size"`
|
||||
MatchedCount int `json:"matched_count"`
|
||||
UnmatchedCount int `json:"unmatched_count"`
|
||||
AmbiguousCount int `json:"ambiguous_count"`
|
||||
GeneratedAtUTC string `json:"generated_at_utc"`
|
||||
Selectors []SelectorReport `json:"selectors"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Error *ReportError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type ReportError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type SelectorReport struct {
|
||||
SelectorID string `json:"selector_id"`
|
||||
Description string `json:"description"`
|
||||
AllowedUse string `json:"allowed_use"`
|
||||
StabilityScore int `json:"stability_score"`
|
||||
Evidence Evidence `json:"evidence"`
|
||||
Matched bool `json:"matched"`
|
||||
MatchCount int `json:"match_count"`
|
||||
Matches []NodeMatch `json:"matches"`
|
||||
Warnings []string `json:"warnings"`
|
||||
}
|
||||
|
||||
func RunCatalogReport(sourceDump string, root *Node, catalog []Selector, generatedAt time.Time) Report {
|
||||
report := Report{
|
||||
OK: true,
|
||||
SourceDump: sourceDump,
|
||||
CatalogSize: len(catalog),
|
||||
GeneratedAtUTC: generatedAt.UTC().Format(time.RFC3339),
|
||||
Warnings: []string{},
|
||||
}
|
||||
if root == nil {
|
||||
report.OK = false
|
||||
report.Error = &ReportError{Code: "NO_ROOT", Message: "UIA dump root is nil"}
|
||||
return report
|
||||
}
|
||||
|
||||
for _, selector := range catalog {
|
||||
if selector.AllowedUse != AllowedUseReadOnlyLocate {
|
||||
report.OK = false
|
||||
report.Error = &ReportError{Code: "INVALID_CATALOG", Message: "selector is not read-only: " + selector.ID}
|
||||
return report
|
||||
}
|
||||
match := MatchSelector(root, catalog, selector.ID)
|
||||
selectorReport := SelectorReport{
|
||||
SelectorID: selector.ID,
|
||||
Description: selector.Description,
|
||||
AllowedUse: selector.AllowedUse,
|
||||
StabilityScore: selector.StabilityScore,
|
||||
Evidence: selector.Evidence,
|
||||
Matched: match.Matched,
|
||||
MatchCount: match.MatchCount,
|
||||
Matches: match.Matches,
|
||||
Warnings: append([]string{}, match.Warnings...),
|
||||
}
|
||||
report.Selectors = append(report.Selectors, selectorReport)
|
||||
if match.Matched {
|
||||
report.MatchedCount++
|
||||
} else {
|
||||
report.UnmatchedCount++
|
||||
}
|
||||
if match.MatchCount > 1 {
|
||||
report.AmbiguousCount++
|
||||
}
|
||||
}
|
||||
return report
|
||||
}
|
||||
|
||||
func StrictExitCode(report Report) int {
|
||||
if !report.OK {
|
||||
if report.Error != nil && report.Error.Code == "INVALID_CATALOG" {
|
||||
return StrictExitInvalidCatalog
|
||||
}
|
||||
return StrictExitReportGenerationFailure
|
||||
}
|
||||
if report.UnmatchedCount > 0 {
|
||||
return StrictExitUnmatched
|
||||
}
|
||||
if report.AmbiguousCount > 0 {
|
||||
return StrictExitAmbiguous
|
||||
}
|
||||
return StrictExitOK
|
||||
}
|
||||
Reference in New Issue
Block a user