findMatchingRule finds the first matching policy rule for a given audit entry. Rules are evaluated in order; the first matching rule wins. This ports the TS audit-enricher.ts logic: 1. aclName === "all" rules match any domain (used for default deny rules) 2. Protocol matching: HTTPS-only rules won'
(entry AuditLogEntry, rules []PolicyRule)
| 277 | // 3. Observed-decision validation: a rule only "matches" if its action matches the |
| 278 | // observed outcome (allow rule only credited for allowed traffic, deny for denied) |
| 279 | func findMatchingRule(entry AuditLogEntry, rules []PolicyRule) *PolicyRule { |
| 280 | isHTTPS := isEntryHTTPS(entry) |
| 281 | expectedAction := "deny" |
| 282 | if isEntryAllowed(entry) { |
| 283 | expectedAction = "allow" |
| 284 | } |
| 285 | firewallPolicyLog.Printf("Finding matching rule for host=%s, expected_action=%s, rules=%d", entry.Host, expectedAction, len(rules)) |
| 286 | |
| 287 | for i := range rules { |
| 288 | rule := &rules[i] |
| 289 | |
| 290 | // Protocol check |
| 291 | if !protocolMatches(*rule, isHTTPS) { |
| 292 | continue |
| 293 | } |
| 294 | |
| 295 | // aclName "all" is a catch-all rule (typically the default deny) |
| 296 | if rule.ACLName == "all" { |
| 297 | if rule.Action == expectedAction { |
| 298 | firewallPolicyLog.Printf("Matched catch-all rule (action=%s) for host=%s", rule.Action, entry.Host) |
| 299 | return rule |
| 300 | } |
| 301 | continue |
| 302 | } |
| 303 | |
| 304 | // Domain match |
| 305 | if domainMatchesRule(entry.Host, *rule) { |
| 306 | if rule.Action == expectedAction { |
| 307 | firewallPolicyLog.Printf("Matched rule %s (action=%s) for host=%s", rule.ACLName, rule.Action, entry.Host) |
| 308 | return rule |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | firewallPolicyLog.Printf("No matching rule found for host=%s", entry.Host) |
| 313 | return nil |
| 314 | } |
| 315 | |
| 316 | // enrichWithPolicyRules enriches audit log entries with policy rule attribution. |
| 317 | func enrichWithPolicyRules(entries []AuditLogEntry, manifest *PolicyManifest) *PolicyAnalysis { |