Interactive per-check risk score in the node posture tab - #986
Merged
Conversation
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.
Interactive per-check risk score in the node posture tab
Each control in a node's posture tab now has a checkbox. Unchecking one recomputes the risk score, level, and pass/warn/fail counts live in the browser — no request round-trip — so an operator can answer "what's my score if I ignore this check?" on the spot.
How it works
The heavy lifting (parsing raw osquery rows, deciding pass/warn/fail per control) still happens once, server-side, in
pkg/posture. The frontend does not reimplement that evaluation logic — it only re-sums numbers the server already computed:max_scoretoControlResult(pkg/posture/scoring.go): the risk points a control would contribute if it failed, now reported even when it passes. This was the only piece missing —status,score, andseveritywere already in the API response.frontend/src/features/nodes/postureScore.ts: a small pure function mirroring only the aggregation math fromScoreCalculator.Score(earned/possible normalization) andriskLevel's critical/high escalation rule — applied to whichever controls are currently checked.NodeDetailPage.tsx: each control row is a checkbox, checked by default. The gauge, risk badge, and pass/warn/fail counts always reflect the checked subset; with everything checked this is byte-for-byte the server'sPostureScore.Why this is safe from drift
The per-rule
Evaluate()functions (dozens of them, one per compliance control) never leave the Go backend. If a rule's logic ever changes, the frontend doesn't need to change — it only consumes each control's already-decidedstatus/score/max_score.Files
pkg/posture/scoring.go—ControlResult.MaxScorefrontend/src/features/nodes/postureScore.ts(new) —recomputePostureScore,riskLevelFromScore,controlKeyfrontend/src/features/nodes/NodeDetailPage.tsx— checkbox UI, live recompute wiring inPostureScorePanelfrontend/src/api/types.ts—ControlResult.max_scoreosctrl-api.yaml— regenerated (make openapi)Testing
pkg/posture/scoring_test.go: assertsMaxScoresums to the same normalization denominator the score itself was computed from.frontend/src/features/nodes/postureScore.test.ts(new): threshold table, critical/high escalation, empty-selection (no divide-by-zero), and thatrecomputePostureScorereproduces the server score when nothing is excluded.NodeDetailPage.test.tsx: new interaction test — unchecking a failing critical control drops the score/level live, re-checking restores it, andgetNodePostureScoreis called exactly once (proving no extra network round-trip).go build/go test/go vet,make openapi-check, frontend 255 tests,tsc.