Skip to content

fix: root case-scoped dashboard widgets at cases instead of alerts - #1115

Open
Scharxi wants to merge 1 commit into
dfir-iris:hotfix_v2.4.29from
Scharxi:fix/issue-1112-dashboard-child-table-fanout
Open

fix: root case-scoped dashboard widgets at cases instead of alerts#1115
Scharxi wants to merge 1 commit into
dfir-iris:hotfix_v2.4.29from
Scharxi:fix/issue-1112-dashboard-child-table-fanout

Conversation

@Scharxi

@Scharxi Scharxi commented Aug 27, 2026

Copy link
Copy Markdown

Summary

Custom Dashboard widgets on case-scoped child tables (case_tasks, case_notes, case_assets, case_iocs, case_events, tags, owner/creator/reviewer, review status, case state) now reflect true per-case data, independent of how many alerts happen to be merged into a case.

What changed

Every widget query was rooted at alerts (_BASE_TABLE = 'alerts'), reaching cases and its child tables via outerjoin(Cases, Alert.cases):

  • A case with 0 merged alerts never produced a base row → invisible, along with everything joined through it.
  • A case with N merged alerts produced N duplicate base rows → every joined child-table row counted N times.

Adds _CASE_ROOTED_TABLES, the set of tables reachable from cases without needing alerts at all. When every field/group_by/filter/time_column a widget references is in that set, the query is rooted at cases directly instead of alerts — no fan-out, no invisibility. Every join lambda for those tables already referenced only Cases, so none needed to change. Widgets touching any alert-only table keep the exact original alerts-rooted path, byte-for-byte — no behavior change for widgets already proven correct.

Also generalizes the access-control filter (previously alert-only) and the existing tags/case_tags scoped-subquery filter (previously assumed cases was always joined, never the base) to both modes.

Adds a regression test creating the exact 4-case / 0-2-2-1-alert scenario from the issue and asserting both a Number widget (count) and a Table widget (per-case rows) reflect true per-case data.

Validation

  • tests/tests.py::test_custom_dashboard_case_child_table_widgets_are_not_affected_by_merged_alert_count (new).
  • Manual: rebuilt the dev stack from this branch, reproduced the exact issue [BUG] Custom Dashboard widgets on case child tables (tasks/notes/asss) miss or duplicate data depending on merged-alert count #1112 repro live (4 cases, 1 task each, merged into 0/2/2/1 alerts) through the actual dashboard UI (not just the API) — Total Tasks renders 4 (not 5), the Tasks per Case table shows each case exactly once including the 0-alert case. Cross-checked an alerts-rooted widget (Total Alerts = 5) and the new cases-rooted count (Total Cases = 5) to confirm the untouched path and the new path are both correct side by side.

Notes for the reviewer

None — no behavior change for existing alert-rooted widgets; this only changes the query root for widgets that touch exclusively case-scoped tables.

Linked

Fixes #1112
Fixes #1091

Ready for review once out of draft.

Every custom dashboard widget query was rooted at `alerts`
(_BASE_TABLE = 'alerts'), reaching `cases` and case-scoped child
tables (case_tasks, case_notes, case_assets, ...) via
outerjoin(Cases, Alert.cases). A case with 0 merged alerts never
produced a base row, so it - and everything joined through it -
never appeared. A case with N merged alerts produced N duplicate
base rows, so every joined child-table row was counted N times.
Reproduces dfir-iris#1112 exactly: 4 cases with 1 task
each, merged into 0/2/2/1 alerts, showed a Total Tasks count of 5
instead of 4.

Adds _CASE_ROOTED_TABLES, the set of tables reachable from `cases`
without needing `alerts` at all (cases itself, case_owner/creator/
reviewer, case_tags/tags, case_assets(+types), case_iocs(+types),
case_events, case_notes, case_tasks, review_status, case_state).
When every field/group_by/filter/time_column a widget references is
in that set, the query is rooted at `cases` directly instead of
`alerts` - no fan-out, no invisibility - while every join lambda for
those tables was already written to reference only `Cases`, so none
of them needed to change. Widgets touching any alert-only table keep
the exact original alerts-rooted path, byte-for-byte, so there is no
behavior change for the widgets already proven correct.

Also generalizes the access-control filter (previously alert-only)
and the existing tags/case_tags scoped-subquery filter (previously
assumed `cases` was always joined, never the base) to both modes.

Adds a regression test creating the exact 4-case/0-2-2-1-alert
scenario from the issue and asserting both a Number widget (count)
and a Table widget (per-case rows) now reflect the true per-case
data instead of the alert-fanout-inflated one.
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • api_*

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75623100-acab-493f-8f3a-bd02c3743bb4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Scharxi
Scharxi marked this pull request as ready for review August 27, 2026 04:05
@whikernel

Copy link
Copy Markdown
Contributor

hi @Scharxi - thanks a lot for the issues and the PRs!

Please note that we are currently migrating to IRIS v3, which not yet published but changes a lot, including a whole new backend and SvelteKit frontend. We'll see how it fits when we go public 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants