Skip to content

fix[frontend+backend](alerts): 'Row to filters' no longer breaks the alert list with invalid filters (#2968) - #2974

Merged
osmontero merged 1 commit into
v11from
fix/row-to-filters
Oct 23, 2026
Merged

osmontero merged 1 commit into
v11from
fix/row-to-filters

Conversation

@osmontero

Copy link
Copy Markdown
Member

fixfrontend+backend: "Row to filters" no longer breaks the alert list with invalid filters (#2968)

Problem

In Threat Management → Alerts, the funnel icon ("Row to filters") lists every
column of an alert with its checkbox checked by default. Clicking "Apply filters"
with that default selection turned every filter into IS_ONE_OF, but only
wrapped the value in a list when it was a non-empty string. So:

  • echoes IS_ONE_OF 0 (bare number)
  • adversary IS_ONE_OF {host: ...} (object)
  • impact IS_ONE_OF {confidentiality: ...} (object)
  • target IS_ONE_OF (null / empty)

were sent to the backend, where FilterType.validate() asserts that
IS_ONE_OF/IS_NOT_ONE_OF values are a List. The failure surfaced as a
500 (not a 400), so the search failed and the alerts page stayed in a
broken state (every later tab/sort/page search failed) until a full reload.

Fix

Frontend (row-to-filters.component.ts) — make the emitted filters always valid:

  • Composite columns (type === OBJECT, e.g. target, adversary, impact)
    are offered unchecked by default (their value is a nested object that
    cannot be a single filter value). The user can still tick them, but then the
    value is sanitized away rather than crashing the list.
  • applyFilter() now runs every value through sanitizeFilterValue(), which
    normalizes scalars (string/number) into a single-element list and returns
    null (→ filter dropped) for the placeholder -, booleans, objects, and
    empty collections. This is the same shape the backend FilterType expects.

Backend (SearchUtil.toQuery) — fail fast with the right status:

  • Validate every filter up front before building the query. A structurally
    invalid filter (e.g. IS_ONE_OF with a non-list value) now throws an
    ApiException (HTTP 400, via the existing global handler) instead of a
    generic RuntimeException (HTTP 500) after the query was half-built.
  • FilterType.validate() uses Spring Assert (which throws
    IllegalArgumentException), so the upfront loop converts that to the 400
    ApiException. The per-operator build* methods re-validate unchanged.

This matches the "UI must not offer an action that only 500s / 400s" principle:
the default selection now produces only valid filters, and any malformed filter
that does reach the backend is reported as a 400, not a 500.

Tests

New SearchUtilInvalidFilterTest (JUnit, alongside the existing
SearchUtilFlattenedTest): object / null / scalar IS_ONE_OF (and
IS_NOT_ONE_OF) all throw ApiException with BAD_REQUEST; a proper list
still builds. Existing SearchUtilFlattenedTest still passes.

Files

  • frontend/.../row-to-filters.component.ts
  • backend/.../service/elasticsearch/SearchUtil.java
  • backend/.../service/elasticsearch/SearchUtilInvalidFilterTest.java (new)

Fixes #2968

@osmontero
osmontero requested a review from a team October 23, 2026 15:47
@github-actions

Copy link
Copy Markdown

❌ Go dependencies check failed

There are outdated Go dependencies, or modules that could not be inspected.
Run bash .github/scripts/go-deps.sh --update --discover locally and
commit the updated go.mod / go.sum files.

Script output
🔍 Discovered 25 Go projects

📦 Dependencies with updates available:

  📁 ./plugins/gcp:
     - google.golang.org/api: v0.299.0 → v0.308.0

  📁 ./plugins/aws:
     - github.com/aws/aws-sdk-go-v2: v1.47.0 → v1.47.4
     - github.com/aws/aws-sdk-go-v2/config: v1.33.5 → v1.33.10
     - github.com/aws/aws-sdk-go-v2/credentials: v1.20.5 → v1.20.10
     - github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs: v1.88.0 → v1.90.2

  📁 ./plugins/alerts:
     - github.com/tidwall/gjson: v1.19.0 → v1.20.0

  📁 ./plugins/events:
     - github.com/tidwall/gjson: v1.19.0 → v1.20.0

  📁 ./plugins/modules-config:
     - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob: v1.8.1 → v1.8.2
     - github.com/aws/aws-sdk-go-v2/config: v1.33.5 → v1.33.10
     - github.com/aws/aws-sdk-go-v2/credentials: v1.20.5 → v1.20.10
     - github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs: v1.88.0 → v1.90.2
     - github.com/aws/aws-sdk-go-v2/service/sts: v1.51.0 → v1.51.5
     - github.com/crowdstrike/gofalcon: v0.22.0 → v0.23.0
     - golang.org/x/sync: v0.23.0 → v0.24.0
     - google.golang.org/api: v0.299.0 → v0.308.0

  📁 ./plugins/azure:
     - github.com/Azure/azure-sdk-for-go/sdk/azcore: v1.23.1 → v1.23.3
     - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob: v1.8.1 → v1.8.2

  📁 ./plugins/crowdstrike:
     - github.com/crowdstrike/gofalcon: v0.22.0 → v0.23.0

  📁 ./plugins/geolocation:
     - github.com/tidwall/gjson: v1.19.0 → v1.20.0

  📁 ./installer:
     - github.com/cloudfoundry/gosigar: v1.3.127 → v1.3.128

  📁 ./agent:
     - github.com/netsampler/goflow2: v1.3.7 → v1.3.8
     - golang.org/x/sys: v0.48.0 → v0.49.0

�[0;31m❌ Please update dependencies before merging.�[0m

@github-actions

Copy link
Copy Markdown

🛑 AI review — Blocking issues

One or more high/critical issues can break things and must be fixed before merging. Details below.

✅ architecture (silas-1.7-pro) — clean

Summary: Fail-fast validation in SearchUtil.toQuery improves error handling without introducing architectural deviations.

No findings.

🛑 bugs (silas-1.7-pro) — blocking — must fix before merge

Summary: New JUnit test is placed under src/main/java, which can break the build or ship test code; validation loop can also NPE on null filter elements.

  • high backend/src/main/java/com/park/utmstack/service/elasticsearch/SearchUtilInvalidFilterTest.java:1 — The new JUnit test class is added to the main source tree (src/main/java) instead of the test source tree (src/test/java). In a standard Java/Maven/Gradle build this can fail compilation if JUnit is only on the test classpath, or otherwise include test classes in the production artifact. Move the file to backend/src/test/java/com/park/utmstack/service/elasticsearch/.
  • medium backend/src/main/java/com/park/utmstack/service/elasticsearch/SearchUtil.java:49 — The added validation loop dereferences filter without checking for null. A List containing a null element can cause a NullPointerException/500 instead of the intended 400 ApiException. Reproduce with a list such as Arrays.asList((FilterType) null) or a JSON filter array containing null. Add a null check and throw the same 400 ApiException.

✅ security (silas-1.7-pro) — clean

Summary: No introduced vulnerabilities or customer-facing information disclosures found; validation now returns 400 for malformed filters.

No findings.

@utmstackprapprover utmstackprapprover Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested — Go dependencies check failed (see above).

@osmontero
osmontero merged commit d50ba8d into v11 Oct 23, 2026
5 of 7 checks passed
@osmontero
osmontero deleted the fix/row-to-filters branch October 23, 2026 15:59
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.

(Threat Management) "Row to filters" with the default selection breaks the alert list: every later search fails until the page is reloaded

1 participant