From fa73b6743be24c69b3cc32f749bc3c8ec65a0113 Mon Sep 17 00:00:00 2001 From: Isaiah Lewis Date: Fri, 15 Aug 2025 07:31:30 -0700 Subject: [PATCH 1/3] fix(helm-lint): Add HTTP/HTTPS URL support for json schema references Signed-off-by: Isaiah Lewis --- pkg/chart/v2/util/jsonschema.go | 48 ++++++++++++++++++++++++++++ pkg/chart/v2/util/jsonschema_test.go | 40 +++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/pkg/chart/v2/util/jsonschema.go b/pkg/chart/v2/util/jsonschema.go index 820e5953a..96fd207b9 100644 --- a/pkg/chart/v2/util/jsonschema.go +++ b/pkg/chart/v2/util/jsonschema.go @@ -21,13 +21,53 @@ import ( "errors" "fmt" "log/slog" + "net/http" "strings" + "time" "github.com/santhosh-tekuri/jsonschema/v6" + "helm.sh/helm/v4/internal/version" + chart "helm.sh/helm/v4/pkg/chart/v2" ) +// HTTPURLLoader implements a loader for HTTP/HTTPS URLs +type HTTPURLLoader http.Client + +func (l *HTTPURLLoader) Load(urlStr string) (any, error) { + client := (*http.Client)(l) + + req, err := http.NewRequest(http.MethodGet, urlStr, nil) + if err != nil { + return nil, fmt.Errorf("failed to create HTTP request for %s: %w", urlStr, err) + } + req.Header.Set("User-Agent", version.GetUserAgent()) + + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("HTTP request failed for %s: %w", urlStr, err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP request to %s returned status %d (%s)", urlStr, resp.StatusCode, http.StatusText(resp.StatusCode)) + } + + return jsonschema.UnmarshalJSON(resp.Body) +} + +// newHTTPURLLoader creates a HTTP URL loader with proxy support. +func newHTTPURLLoader() *HTTPURLLoader { + httpLoader := HTTPURLLoader(http.Client{ + Timeout: 15 * time.Second, + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, + }) + return &httpLoader +} + // ValidateAgainstSchema checks that values does not violate the structure laid out in schema func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) error { var sb strings.Builder @@ -71,7 +111,15 @@ func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) (reterr error } slog.Debug("unmarshalled JSON schema", "schema", schemaJSON) + // Configure compiler with loaders for different URL schemes + loader := jsonschema.SchemeURLLoader{ + "file": jsonschema.FileLoader{}, + "http": newHTTPURLLoader(), + "https": newHTTPURLLoader(), + } + compiler := jsonschema.NewCompiler() + compiler.UseLoader(loader) err = compiler.AddResource("file:///values.schema.json", schema) if err != nil { return err diff --git a/pkg/chart/v2/util/jsonschema_test.go b/pkg/chart/v2/util/jsonschema_test.go index 3279eb0db..cd95b7faf 100644 --- a/pkg/chart/v2/util/jsonschema_test.go +++ b/pkg/chart/v2/util/jsonschema_test.go @@ -17,7 +17,10 @@ limitations under the License. package util import ( + "net/http" + "net/http/httptest" "os" + "strings" "testing" chart "helm.sh/helm/v4/pkg/chart/v2" @@ -245,3 +248,40 @@ func TestValidateAgainstSchema2020Negative(t *testing.T) { t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString) } } + +func TestHTTPURLLoader_Load(t *testing.T) { + // Test successful JSON schema loading + t.Run("successful load", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"type": "object", "properties": {"name": {"type": "string"}}}`)) + })) + defer server.Close() + + loader := newHTTPURLLoader() + result, err := loader.Load(server.URL) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + if result == nil { + t.Fatal("Expected result to be non-nil") + } + }) + + t.Run("HTTP error status", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer server.Close() + + loader := newHTTPURLLoader() + _, err := loader.Load(server.URL) + if err == nil { + t.Fatal("Expected error for HTTP 404") + } + if !strings.Contains(err.Error(), "404") { + t.Errorf("Expected error message to contain '404', got: %v", err) + } + }) +} From fb12b44493eb36e12b1af4804051cca01515f5f3 Mon Sep 17 00:00:00 2001 From: Isaiah Lewis Date: Mon, 18 Aug 2025 11:35:59 -0700 Subject: [PATCH 2/3] fix(helm-lint): Add TLSClientConfig Signed-off-by: Isaiah Lewis --- pkg/chart/v2/util/jsonschema.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/chart/v2/util/jsonschema.go b/pkg/chart/v2/util/jsonschema.go index 96fd207b9..0d03db710 100644 --- a/pkg/chart/v2/util/jsonschema.go +++ b/pkg/chart/v2/util/jsonschema.go @@ -18,6 +18,7 @@ package util import ( "bytes" + "crypto/tls" "errors" "fmt" "log/slog" @@ -63,6 +64,7 @@ func newHTTPURLLoader() *HTTPURLLoader { Timeout: 15 * time.Second, Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, + TLSClientConfig: &tls.Config{}, }, }) return &httpLoader From 62e0c78ef8dcfbdaffc44c634088c00f692d8344 Mon Sep 17 00:00:00 2001 From: Isaiah Lewis Date: Tue, 19 Aug 2025 12:35:12 -0700 Subject: [PATCH 3/3] fix(helm-lint): fmt Signed-off-by: Isaiah Lewis --- pkg/chart/v2/util/jsonschema.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chart/v2/util/jsonschema.go b/pkg/chart/v2/util/jsonschema.go index 0d03db710..72e133363 100644 --- a/pkg/chart/v2/util/jsonschema.go +++ b/pkg/chart/v2/util/jsonschema.go @@ -63,7 +63,7 @@ func newHTTPURLLoader() *HTTPURLLoader { httpLoader := HTTPURLLoader(http.Client{ Timeout: 15 * time.Second, Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, + Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{}, }, })