# UNDF: UNDF-2026-000000105
# helm-0002: filterReleases / filterPlugins — O(n×m) linear membership in filter loops

**Severity:** MEDIUM
**CWE:** CWE-407 (Algorithmic Complexity — linear membership test in hot loop)
**Speedup:** >20x at R=500 releases, M=100 ignored names
**Target:** Helm (helm/helm)
**Files:**
- `pkg/cmd/list.go:246` — `slices.Contains(ignoredReleaseNames, rel.Name)` inside loop over all releases
- `pkg/cmd/plugin_list.go:88` — `slices.Contains(ignoredPluginNames, plugin.Metadata().Name)` inside loop over all plugins

## Description

`filterReleases` iterates over every release and calls `slices.Contains` on the
ignored-names list for each one. With R releases and M ignored names this is
O(R×M) per `helm list` invocation. `filterPlugins` has the identical structure
for plugin listing.

Both functions are called on every `helm list` / `helm plugin list` command. In
clusters with hundreds of releases and a moderate ignore list the quadratic work
accumulates meaningfully inside the CLI hot-path.

## Root Cause

```go
// pkg/cmd/list.go:244-250
for _, rel := range releases {
    found := slices.Contains(ignoredReleaseNames, rel.Name)  // O(M) per release
    if !found {
        filteredReleases = append(filteredReleases, rel)
    }
}
```

```go
// pkg/cmd/plugin_list.go:86-92
for _, plugin := range plugins {
    found := slices.Contains(ignoredPluginNames, plugin.Metadata().Name)  // O(M) per plugin
    if !found {
        filteredPlugins = append(filteredPlugins, plugin)
    }
}
```

Fix: build a `map[string]struct{}` from the ignored-names slice once before the
loop — O(M) build, O(1) per lookup.

## Patch

See `helm-0002-filter-releases-plugins-set.patch`

## Complexity Before

`filterReleases`: **O(R × M)** — R releases × M ignored names per `helm list`

## Complexity After

Build set once: **O(M)**, then O(1) per release → **O(R + M)**

## Reproduction

```
cd defects/helm/unit && javac -d . *.java && java -ea unit.HelmTest
```
