feat(dashboard): per-finding "runtime-blind on this node" caveat (JEF-424)#242
Merged
thejefflarson merged 2 commits intoJul 18, 2026
Conversation
…-424)
A finding whose workload sits on a node the agent can't see (no live eBPF
sensor) reads as calm/propose-only — but that calm is dishonest there: we
can't tell whether the path is being exploited, so blind ≠ green. The
finding-level blind-node caveat (shipped as coverage under JEF-308) now leads
with the crisp "runtime-blind on <node>" framing the ticket names, keeping the
honest tail ("absence of a signal is not evidence of safety") and NAMING the
node.
METADATA ONLY (ADR-0016 — presentation is a view, never a gate): the caveat is
an additive `Option<String>` on `FindingProps`, derived from the SAME
`blind_node_set` the Readiness runtime-corroboration row reads (so the two can
never disagree). It never touches the verdict, the proposed action (cut), or
the report — the finding's decision is unchanged. The client renders it via the
existing `.verdict-caveat` / `role="note"` precedent; the node name is UNTRUSTED
and auto-escaped at render (Preact).
Tests: Rust — a finding on a blind node carries the caveat NAMING the node
("runtime-blind on <node>"); a finding on a healthy/sensored node (and a
corroborated one) does not; and a new honesty test asserts the caveat changes
NOTHING but that one field — same verdict/posture/live-tag, same cut, same
disposition, byte-for-byte-equal report body. JS — the caveat renders as a
`role="note"` and an XSS-laden node name renders as escaped text, never a live
element. Web bundle built from source; `cargo fmt`/`clippy`/`cargo test -p
protector` and `npm test` all green.
Closes JEF-424
Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ind-on-this-node-caveat-where-a
thejefflarson
enabled auto-merge (squash)
July 18, 2026 07:10
thejefflarson
deleted the
thejefflarson/jef-424-per-finding-runtime-blind-on-this-node-caveat-where-a
branch
July 18, 2026 07:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
A finding whose workload sits on a node the agent can't see (no live eBPF sensor) reads as calm / propose-only — but that calm is dishonest there: absence of a corroborating signal is not evidence of safety, so blind ≠ green. This threads the existing per-node
blind_node_set()into the findings snapshot so such a finding carries a finding-level caveat that leads with the crisp "runtime-blind on <node>" framing and NAMES the node.What changed
engine/src/engine/dashboard/view_model/findings.rs— the finding-levelblind_node_caveatnow reads"runtime-blind on <node> — no live sensor here, so absence of a signal is not evidence of safety", keeping the honest tail and naming the node.Option<String>onFindingProps(already#[serde(default)]-shaped), derived from the sameblind_node_setthe Readiness runtime-corroboration row reads, so the two can never disagree. It never touches the verdict, the proposed action (cut), or the report — the decision is unchanged..verdict-caveat/role="note"precedent indetail.jsx; the node name is untrusted and auto-escaped at render (Preact).Honesty invariants
blind_node_setthe Readiness row uses (they never disagree).Tests
view_model/findings/tests.rs): a finding on a blind node carries the caveat naming the node (runtime-blind on <node>); a finding on a healthy/sensored node — and a corroborated one — does not; and a new honesty test asserts the caveat changes NOTHING else (same posture/live-tag/verdict-summary, same cut, same disposition, byte-for-byte-equal report body).test/blind-caveat.test.jsx): the caveat renders as arole="note"; none renders when the server shipsnull; an XSS-laden node name renders as escaped text, never a live element.Checks (all green)
cargo fmt,cargo clippy -p protector --all-targets(clean),cargo test -p protector(775 lib + integration + guards pass).npm run build);npm test(72 pass, incl. 3 new).Scope note
Overlaps with JEF-421 — both touch the dashboard view-model props + client, but different files. Flagged for the architect.
Closes JEF-424
🤖 Generated with Claude Code
https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP