java-topology/defects/helm/patch/helm-0003-repo-update-linear-scan.md

71 lines
2.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000106
# 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
```