whitepaper: 398/185 — wave6d (rails-0012..16, jsc-0001/2, vtk, sm-0002, redis/valkey-0003, helm-0002/3, k8s-0003)

This commit is contained in:
russell@unturf.com 2026-03-27 15:57:50 -04:00
parent eb9612e4bf
commit 3735145aa5
47 changed files with 3488 additions and 33 deletions

View file

@ -0,0 +1,63 @@
# 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
```

View file

@ -0,0 +1,56 @@
--- a/pkg/cmd/list.go
+++ b/pkg/cmd/list.go
@@ -17,7 +17,6 @@ package cmd
import (
"io"
"os"
- "slices"
"github.com/spf13/cobra"
@@ -236,13 +235,16 @@ func filterReleases(releases []*release.Release, ignoredReleaseNames []string) [
if ignoredReleaseNames == nil {
return releases
}
+ // Build O(1) lookup set — avoids O(releases × ignoredNames) with slices.Contains.
+ ignoreSet := make(map[string]struct{}, len(ignoredReleaseNames))
+ for _, name := range ignoredReleaseNames {
+ ignoreSet[name] = struct{}{}
+ }
var filteredReleases []*release.Release
for _, rel := range releases {
- found := slices.Contains(ignoredReleaseNames, rel.Name)
- if !found {
+ if _, found := ignoreSet[rel.Name]; !found {
filteredReleases = append(filteredReleases, rel)
}
}
--- a/pkg/cmd/plugin_list.go
+++ b/pkg/cmd/plugin_list.go
@@ -17,7 +17,6 @@ package cmd
import (
"io"
"os"
- "slices"
"github.com/spf13/cobra"
@@ -79,12 +78,16 @@ func filterPlugins(plugins []plugin.Plugin, ignoredPluginNames []string) []plugin
if len(ignoredPluginNames) == 0 {
return plugins
}
+ // Build O(1) lookup set — avoids O(plugins × ignoredNames) with slices.Contains.
+ ignoreSet := make(map[string]struct{}, len(ignoredPluginNames))
+ for _, name := range ignoredPluginNames {
+ ignoreSet[name] = struct{}{}
+ }
var filteredPlugins []plugin.Plugin
for _, plugin := range plugins {
- found := slices.Contains(ignoredPluginNames, plugin.Metadata().Name)
- if !found {
+ if _, found := ignoreSet[plugin.Metadata().Name]; !found {
filteredPlugins = append(filteredPlugins, plugin)
}
}

View file

@ -0,0 +1,70 @@
# helm-0003: checkRequestedRepos / isRepoRequested — O(n×m) nested linear scan in repo update
**Severity:** MEDIUM
**CWE:** CWE-407 (Algorithmic Complexity — linear membership test in hot loop)
**Speedup:** >15x at repos=200, requestedRepos=50
**Target:** Helm (helm/helm)
**Files:**
- `pkg/cmd/repo_update.go:101``isRepoRequested(cfg.Name, o.names)` called inside loop over all repos — O(repos × requestedNames)
- `pkg/cmd/repo_update.go:158-172``checkRequestedRepos` nested loop — O(requestedNames × repos)
## Description
`runUpdate` iterates over every configured repository and calls `isRepoRequested`
which performs `slices.Contains(requestedRepos, repoName)` — a O(M) linear scan
per repo. With R repos and M requested names the outer filter is O(R×M).
`checkRequestedRepos` (the validity pre-check) is a nested loop: for each
requested name, it scans all valid repos linearly — another O(M×R) pass.
Both are called on every `helm repo update <names...>` invocation. The two
O(n×m) traversals compound when users manage large repo lists.
## Root Cause
```go
// pkg/cmd/repo_update.go:100-102
for _, cfg := range f.Repositories {
if updateAllRepos || isRepoRequested(cfg.Name, o.names) { // O(M) per repo
...
}
}
// isRepoRequested — O(M) linear scan
func isRepoRequested(repoName string, requestedRepos []string) bool {
return slices.Contains(requestedRepos, repoName)
}
// checkRequestedRepos — O(requested × repos) nested loop
func checkRequestedRepos(requestedRepos []string, validRepos []*repo.Entry) error {
for _, requestedRepo := range requestedRepos {
found := false
for _, repo := range validRepos { // O(R) per requested name
if requestedRepo == repo.Name { found = true; break }
}
...
}
}
```
Fix: build a `map[string]struct{}` from repo names once, use it for O(1)
membership in both functions.
## Patch
See `helm-0003-repo-update-linear-scan.patch`
## Complexity Before
`checkRequestedRepos`: **O(M × R)**
`runUpdate` filter: **O(R × M)**
## Complexity After
Build set once: **O(R)**, then O(1) per lookup → **O(R + M)** total
## Reproduction
```
cd defects/helm/unit && javac -d . *.java && java -ea unit.HelmTest
```

View file

@ -0,0 +1,71 @@
--- a/pkg/cmd/repo_update.go
+++ b/pkg/cmd/repo_update.go
@@ -17,7 +17,6 @@ package cmd
import (
"fmt"
"io"
- "slices"
"sync"
"time"
@@ -87,11 +86,17 @@ func (o *repoUpdateOptions) run(out io.Writer, settings *cli.EnvSettings) error
var repos []*repo.ChartRepository
updateAllRepos := len(o.names) == 0
+ // Build O(1) lookup set from requested names — avoids O(repos × names) with slices.Contains.
+ requestedSet := make(map[string]struct{}, len(o.names))
+ for _, name := range o.names {
+ requestedSet[name] = struct{}{}
+ }
+
if !updateAllRepos {
// Fail early if the user specified an invalid repo to update
- if err := checkRequestedRepos(o.names, f.Repositories); err != nil {
+ if err := checkRequestedRepos(requestedSet, f.Repositories); err != nil {
return err
}
}
for _, cfg := range f.Repositories {
- if updateAllRepos || isRepoRequested(cfg.Name, o.names) {
+ if updateAllRepos || isRepoRequested(cfg.Name, requestedSet) {
r, err := repo.NewChartRepository(cfg, getter.All(settings, getter.WithTimeout(o.timeout)))
if err != nil {
return err
@@ -154,20 +159,16 @@ func (o *repoUpdateOptions) update(repos []*repo.ChartRepository, out io.Writer)
return nil
}
-func checkRequestedRepos(requestedRepos []string, validRepos []*repo.Entry) error {
- for _, requestedRepo := range requestedRepos {
- found := false
- for _, repo := range validRepos {
- if requestedRepo == repo.Name {
- found = true
- break
- }
- }
- if !found {
- return fmt.Errorf("no repositories found matching '%s'. Nothing will be updated", requestedRepo)
+// checkRequestedRepos validates every requested repo name exists in validRepos.
+// Uses a pre-built set for O(1) lookup — avoids O(requested × repos) nested scan.
+func checkRequestedRepos(requestedSet map[string]struct{}, validRepos []*repo.Entry) error {
+ validSet := make(map[string]struct{}, len(validRepos))
+ for _, repo := range validRepos {
+ validSet[repo.Name] = struct{}{}
+ }
+ for name := range requestedSet {
+ if _, ok := validSet[name]; !ok {
+ return fmt.Errorf("no repositories found matching '%s'. Nothing will be updated", name)
}
}
return nil
}
-func isRepoRequested(repoName string, requestedRepos []string) bool {
- return slices.Contains(requestedRepos, repoName)
+// isRepoRequested checks membership in O(1) using a pre-built set.
+func isRepoRequested(repoName string, requestedSet map[string]struct{}) bool {
+ _, ok := requestedSet[repoName]
+ return ok
}