Security ledger event_json.decision says allow on blocked HTTP and DNS requests
- Dominant language
- Rust
- Stars
- 72
- Forks
- 13
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 5
Description
## Observed
On a real VM (Kingslanding run `20260911-162608-1e42bc-focus-test`), a corp rule `http.host == "egress.capsem.test" && http.path == "/secret"` with `action = "block"` refused the request (client got a non-zero exit, nothing reached the upstream transcript), and a `dns.qname.matches(...)` block rule refused a query. The `security_rule_events` rows for both carry the correct row-level `rule_id` and `rule_action = block`, but the stored `event_json.decision.effective` is `"allow"` on every one of those rows:
```
http=[('egress.capsem.test', '/secret', 'allow'), ('egress.capsem.test', '/secret', 'allow'), ...]
dns=[('probe..attacker.test', 'allow'), ...]
```
Network boundary rows do not have this problem: `security_engine/network/ledger.rs` runs `prepare_evaluated_event_for_security_rule_ledger` and `apply_event_decision_to_enforcement` before serializing, so `test_policy.py` can assert `decision.effective == "block"`.
## Why it matters
`event_json` is what a responder reads when the rules have changed since the event. A snapshot that says `allow` for a request the product blocked is wrong evidence, and the two boundary families disagree on what the field means.
## Expected
One contract: `event_json.decision` on a security rule row reflects the effective decision at the time the row was written, for HTTP, DNS, MCP, file and network alike. Either the HTTP/DNS emitters (`TelemetryHook`, DNS handler emission via `emit_evaluated_security_rules`) serialize after applying the decision like the network path, or the field is dropped from non-network rows and documented as row-level `rule_action` only. Tests: ironbank HTTP and DNS block proofs assert the field, and a Rust unit test on the emitter.
Contributor guide
Research direction
Compare security_engine/network/ledger.rs with the HTTP TelemetryHook and DNS emission through emit_evaluated_security_rules, then inspect test_policy.py and the ironbank HTTP/DNS block proofs. Verify where event_json.decision is serialized and how the network path applies the effective decision. Done means the agreed contract is covered by the HTTP and DNS assertions plus a Rust emitter unit test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust
- Domain
- backend, security, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100