● Notice //GitHub (@shehzade) is temporarily suspended pending an automated false-positive review. All tools are hosted on this Code Mirror and available on request (contact); GitHub links will return once reinstated.
PUBLIC MIRRORGo v1.0

Multi-protocol network egress assessment and DLP verification framework featuring automated L4 sweeps, corporate proxy traversal, and covert protocol smuggling across 20+ channels.

$ git clone https://shehzade.io/repos/contraband.git Copy
Source ZIP (1.93 MB)
Code 174 Commits 1
contraband / pkg / report / scoring.go
← Back to directory
228 lines · 6.8 KB
Raw
1package report
2
3import (
4 "math"
5 "time"
6
7 "github.com/shehzade/contraband/pkg/common"
8)
9
10// PostureLevel classifies enterprise egress exposure risk.
11type PostureLevel string
12
13const (
14 PostureHardened PostureLevel = "Hardened" // 0.0% – 19.9%
15 PostureModerate PostureLevel = "Moderate" // 20.0% – 59.9%
16 PostureCritical PostureLevel = "Critical" // 60.0% – 100.0%
17)
18
19// PostureColor returns the associated hex color code for executive dashboards.
20func (p PostureLevel) PostureColor() string {
21 switch p {
22 case PostureHardened:
23 return "#22c55e" // Green
24 case PostureModerate:
25 return "#eab308" // Yellow / Amber
26 case PostureCritical:
27 return "#ef4444" // Red
28 default:
29 return "#6b7280" // Gray
30 }
31}
32
33// CategoryScore tracks metrics and calculated exposure for an individual assessment category.
34type CategoryScore struct {
35 Category string `json:"category"`
36 Weight float64 `json:"weight"` // 0.30 for Port/DLP, 0.40 for Proto
37 TestedCount int `json:"tested_count"` // Total items tested
38 SuccessCount int `json:"success_count"` // Unfiltered / succeeded / leaked
39 BlockedCount int `json:"blocked_count"` // Blocked by L4/L7/DLP
40 RawScore float64 `json:"raw_score"` // Percentage 0.0 – 100.0
41 WeightedScore float64 `json:"weighted_score"` // RawScore * Weight
42 Tested bool `json:"tested"` // True if TestedCount > 0
43}
44
45// EEIReport encapsulates the complete Egress Exposure Index calculation.
46type EEIReport struct {
47 Score float64 `json:"eei_score"` // Normalized EEI (0.0 – 100.0)
48 RawScore float64 `json:"raw_eei_score"` // Unnormalized EEI
49 Posture PostureLevel `json:"posture"` // Hardened, Moderate, Critical
50 PostureColor string `json:"posture_color"` // Hex color code
51 PortScore CategoryScore `json:"port_score"` // Sport (0.30)
52 ProtoScore CategoryScore `json:"proto_score"` // Sproto (0.40)
53 DLPScore CategoryScore `json:"dlp_score"` // Sdlp (0.30)
54 TotalProbes int `json:"total_probes"`
55 SuccessProbes int `json:"success_probes"`
56 BlockedProbes int `json:"blocked_probes"`
57 GeneratedAt time.Time `json:"generated_at"`
58}
59
60const (
61 WeightPort = 0.30
62 WeightProto = 0.40
63 WeightDLP = 0.30
64
65 ThresholdHardenedMax = 19.99
66 ThresholdModerateMax = 59.99
67)
68
69// ClassifyPosture maps an exposure score to its posture classification.
70func ClassifyPosture(score float64) PostureLevel {
71 if score < 0.0 {
72 score = 0.0
73 }
74 if score > 100.0 {
75 score = 100.0
76 }
77 rounded := math.Round(score*100) / 100
78
79 if rounded <= ThresholdHardenedMax {
80 return PostureHardened
81 }
82 if rounded <= ThresholdModerateMax {
83 return PostureModerate
84 }
85 return PostureCritical
86}
87
88// CalculateEEI processes a list of probe results and calculates the Egress Exposure Index.
89// It mitigates division-by-zero when categories are untested using Normalized Active Weighting.
90func CalculateEEI(probes []common.ProbeResult) *EEIReport {
91 report := &EEIReport{
92 GeneratedAt: time.Now().UTC(),
93 PortScore: CategoryScore{
94 Category: "ports",
95 Weight: WeightPort,
96 },
97 ProtoScore: CategoryScore{
98 Category: "protocols",
99 Weight: WeightProto,
100 },
101 DLPScore: CategoryScore{
102 Category: "dlp",
103 Weight: WeightDLP,
104 },
105 }
106
107 report.TotalProbes = len(probes)
108 if len(probes) == 0 {
109 report.Score = 0.0
110 report.RawScore = 0.0
111 report.Posture = PostureHardened
112 report.PostureColor = report.Posture.PostureColor()
113 return report
114 }
115
116 // 1. Accumulate Port Sweep Metrics
117 testedPorts := make(map[int]bool)
118 unfilteredPorts := make(map[int]bool)
119
120 // 2. Accumulate Protocol Metrics
121 testedProtos := make(map[string]bool)
122 successfulProtos := make(map[string]bool)
123
124 // 3. Accumulate DLP Metrics
125 dlpTested := 0
126 dlpUninspected := 0
127 dlpBlocked := 0
128
129 for _, p := range probes {
130 if p.Verdict.IsSuccess() {
131 report.SuccessProbes++
132 } else {
133 report.BlockedProbes++
134 }
135
136 // Port Sweep Stage
137 if p.Stage == "sweep" || (p.Stage == "" && p.PayloadType == "" && p.TargetPort > 0) || p.Protocol == "tcp" || p.Protocol == "udp" {
138 if p.TargetPort > 0 {
139 testedPorts[p.TargetPort] = true
140 if p.Verdict.IsSuccess() {
141 unfilteredPorts[p.TargetPort] = true
142 }
143 }
144 }
145
146 // Protocol Exfiltration Stage
147 if p.Stage == "exfil" || (p.Stage != "sweep" && p.Protocol != "" && p.Protocol != "tcp" && p.Protocol != "udp") {
148 testedProtos[p.Protocol] = true
149 if p.Verdict.IsSuccess() {
150 successfulProtos[p.Protocol] = true
151 }
152 }
153
154 // Procedural DLP Stage
155 if p.PayloadType != "" || p.Stage == "dlp" {
156 dlpTested++
157 if p.Verdict.IsSuccess() {
158 dlpUninspected++
159 } else {
160 dlpBlocked++
161 }
162 }
163 }
164
165 // Calculate Category 1: Sport
166 report.PortScore.TestedCount = len(testedPorts)
167 report.PortScore.SuccessCount = len(unfilteredPorts)
168 report.PortScore.BlockedCount = len(testedPorts) - len(unfilteredPorts)
169 if report.PortScore.TestedCount > 0 {
170 report.PortScore.Tested = true
171 report.PortScore.RawScore = (float64(report.PortScore.SuccessCount) / float64(report.PortScore.TestedCount)) * 100.0
172 report.PortScore.WeightedScore = report.PortScore.RawScore * WeightPort
173 }
174
175 // Calculate Category 2: Sproto
176 report.ProtoScore.TestedCount = len(testedProtos)
177 report.ProtoScore.SuccessCount = len(successfulProtos)
178 report.ProtoScore.BlockedCount = len(testedProtos) - len(successfulProtos)
179 if report.ProtoScore.TestedCount > 0 {
180 report.ProtoScore.Tested = true
181 report.ProtoScore.RawScore = (float64(report.ProtoScore.SuccessCount) / float64(report.ProtoScore.TestedCount)) * 100.0
182 report.ProtoScore.WeightedScore = report.ProtoScore.RawScore * WeightProto
183 }
184
185 // Calculate Category 3: Sdlp
186 report.DLPScore.TestedCount = dlpTested
187 report.DLPScore.SuccessCount = dlpUninspected
188 report.DLPScore.BlockedCount = dlpBlocked
189 if report.DLPScore.TestedCount > 0 {
190 report.DLPScore.Tested = true
191 report.DLPScore.RawScore = (float64(report.DLPScore.SuccessCount) / float64(report.DLPScore.TestedCount)) * 100.0
192 report.DLPScore.WeightedScore = report.DLPScore.RawScore * WeightDLP
193 }
194
195 // Raw EEI: direct sum of weighted components
196 rawEEI := report.PortScore.WeightedScore + report.ProtoScore.WeightedScore + report.DLPScore.WeightedScore
197 report.RawScore = math.Round(rawEEI*100) / 100
198
199 // Normalized Active EEI (rebalancing when some categories are untested)
200 activeWeight := 0.0
201 if report.PortScore.Tested {
202 activeWeight += WeightPort
203 }
204 if report.ProtoScore.Tested {
205 activeWeight += WeightProto
206 }
207 if report.DLPScore.Tested {
208 activeWeight += WeightDLP
209 }
210
211 if activeWeight > 0.0 {
212 normalizedScore := rawEEI / activeWeight
213 if normalizedScore < 0.0 {
214 normalizedScore = 0.0
215 }
216 if normalizedScore > 100.0 {
217 normalizedScore = 100.0
218 }
219 report.Score = math.Round(normalizedScore*100) / 100
220 } else {
221 report.Score = 0.0
222 }
223
224 report.Posture = ClassifyPosture(report.Score)
225 report.PostureColor = report.Posture.PostureColor()
226
227 return report
228}