Guard chartUrl extraction against a malformed helm index - #1062
Guard chartUrl extraction against a malformed helm index#1062mightbeanshuu wants to merge 1 commit into
Conversation
UpdatePackageData extracted the chart URL from a remote helm repository
index.yaml with unguarded type assertions and index accesses:
urls, ok := pkgEntry.([]interface{})[0].(map[interface{}]interface{})["urls"]
chartUrl, ok := urls.([]interface{})[0].(string)
The comma-ok only covered the final map/type lookups; the .([]interface{})
assertions and the [0] indexes were unchecked. Because this data is parsed
from a remote index.yaml (utils.ReadRemoteFile), a malformed or unusual but
valid-YAML index — a non-list chart entry, an empty version list, or an
empty urls list — panicked the model generator with an index-out-of-range
or type-assertion error.
Guard each assertion and index and return the existing ErrGetChartUrl
instead. Adds a hermetic httptest-based test covering the malformed shapes
and the well-formed happy path.
Signed-off-by: Anshu <amananshu2004@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request improves the robustness of the helm index parsing in generators/artifacthub/package.go by adding proper type assertions and length checks to prevent panics on malformed data. It also adds corresponding unit tests to verify this behavior. The review feedback suggests making the error messages more specific and compliant with Go style guidelines, as well as optimizing the unit tests by reusing a single test server instance across test cases.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| entryVersions, ok := pkgEntry.([]interface{}) | ||
| if !ok || len(entryVersions) == 0 { | ||
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | ||
| } | ||
| firstVersion, ok := entryVersions[0].(map[interface{}]interface{}) | ||
| if !ok { | ||
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | ||
| } | ||
| urls, ok := firstVersion["urls"] | ||
| if urls == nil || !ok { | ||
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | ||
| } | ||
| chartUrl, ok := urls.([]interface{})[0].(string) | ||
| urlList, ok := urls.([]interface{}) | ||
| if !ok || len(urlList) == 0 { | ||
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | ||
| } | ||
| chartUrl, ok := urlList[0].(string) | ||
| if !ok || chartUrl == "" { | ||
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | ||
| } |
There was a problem hiding this comment.
To improve maintainability and make debugging easier, consider providing more specific error messages for each validation failure. Currently, all five checks return the exact same error message ("Cannot extract chartUrl from repository helm index"), making it difficult to pinpoint which validation failed.
Additionally, following the standard Go style guide (Go Code Review Comments), error strings should not be capitalized and should not end with punctuation. "chartUrl" can also be formatted as "chart URL" for better readability.
| entryVersions, ok := pkgEntry.([]interface{}) | |
| if !ok || len(entryVersions) == 0 { | |
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | |
| } | |
| firstVersion, ok := entryVersions[0].(map[interface{}]interface{}) | |
| if !ok { | |
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | |
| } | |
| urls, ok := firstVersion["urls"] | |
| if urls == nil || !ok { | |
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | |
| } | |
| chartUrl, ok := urls.([]interface{})[0].(string) | |
| urlList, ok := urls.([]interface{}) | |
| if !ok || len(urlList) == 0 { | |
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | |
| } | |
| chartUrl, ok := urlList[0].(string) | |
| if !ok || chartUrl == "" { | |
| return ErrGetChartUrl(fmt.Errorf("Cannot extract chartUrl from repository helm index")) | |
| } | |
| entryVersions, ok := pkgEntry.([]interface{}) | |
| if !ok || len(entryVersions) == 0 { | |
| return ErrGetChartUrl(fmt.Errorf("cannot extract chart URL: empty or invalid version list")) | |
| } | |
| firstVersion, ok := entryVersions[0].(map[interface{}]interface{}) | |
| if !ok { | |
| return ErrGetChartUrl(fmt.Errorf("cannot extract chart URL: first version entry is not a map")) | |
| } | |
| urls, ok := firstVersion["urls"] | |
| if urls == nil || !ok { | |
| return ErrGetChartUrl(fmt.Errorf("cannot extract chart URL: missing urls field")) | |
| } | |
| urlList, ok := urls.([]interface{}) | |
| if !ok || len(urlList) == 0 { | |
| return ErrGetChartUrl(fmt.Errorf("cannot extract chart URL: empty or invalid urls list")) | |
| } | |
| chartUrl, ok := urlList[0].(string) | |
| if !ok || chartUrl == "" { | |
| return ErrGetChartUrl(fmt.Errorf("cannot extract chart URL: invalid or empty chart URL string")) | |
| } |
References
- According to the standard Go style guide (Go Code Review Comments), error strings should not be capitalized and should not end with punctuation. (link)
| serve := func(index string) *httptest.Server { | ||
| return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { | ||
| _, _ = io.WriteString(w, index) | ||
| })) | ||
| } |
There was a problem hiding this comment.
Spinning up and tearing down a new httptest.Server for every single test case in the loop is inefficient and can slow down test execution.
Instead, you can spin up a single test server outside the loop and dynamically update the response body for each test case.
Here is how you can refactor the test:
func TestUpdatePackageDataMalformedIndex(t *testing.T) {
var currentIndex string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = io.WriteString(w, currentIndex)
}))
defer srv.Close()
tests := []struct {
name string
index string
expectErr bool
}{
// ... (test cases remain the same)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
currentIndex = tt.index
pkg := AhPackage{Name: "mychart", RepoUrl: srv.URL}
err := pkg.UpdatePackageData()
// ... (assertions remain the same)
})
}
}
Description
UpdatePackageDataextracted the chart URL from a remote helm repositoryindex.yamlwith unguarded type assertions and index accesses (details in #1061):The comma-ok only guarded the final map/type lookups; the
.([]interface{})assertions and the[0]indexes were unchecked. Since this index data is remote/externally controlled (utils.ReadRemoteFile), a malformed but valid-YAML index — a non-list chart entry, an empty version list, or an emptyurlslist — panicked the model generator with an index-out-of-range / type-assertion error.This guards each assertion and index and returns the existing
ErrGetChartUrlinstead. No behavior change for well-formed indexes.Testing
Adds a hermetic
httptest-based test (TestUpdatePackageDataMalformedIndex) covering the malformed shapes (empty version list, non-list entry, emptyurls, non-string url) and the well-formed happy path.go test ./generators/artifacthub/passes;gofmt/go vetclean.Fixes #1061
I noticed #856 and #845 are also open on this file, touching different parts of
UpdatePackageData/GenerateComponents; this change is on the chartUrl-extraction hunk only and should rebase cleanly. Happy to coordinate ordering.