From 6c779ac03da9228f13215b3e13a91cdcb93f9147 Mon Sep 17 00:00:00 2001 From: Adesh Deshmukh Date: Wed, 17 Jun 2026 08:17:17 +0530 Subject: [PATCH 1/3] fix: preserve mainArtifact default on parse failure in import and import-url Signed-off-by: Adesh Deshmukh --- cmd/import.go | 6 +++--- cmd/importURL.go | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/import.go b/cmd/import.go index 2a8f8731..774b8a14 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -119,15 +119,15 @@ func NewImportCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command sepSpecificationFiles := strings.Split(specificationFiles, ",") for _, f := range sepSpecificationFiles { mainArtifact := true - var err error // Check if mainArtifact flag is provided. if strings.Contains(f, ":") { pathAndMainArtifact := strings.Split(f, ":") f = pathAndMainArtifact[0] - mainArtifact, err = strconv.ParseBool(pathAndMainArtifact[1]) - if err != nil { + if val, parseErr := strconv.ParseBool(pathAndMainArtifact[1]); parseErr != nil { fmt.Printf("Cannot parse '%s' as Bool, default to true\n", pathAndMainArtifact[1]) + } else { + mainArtifact = val } } diff --git a/cmd/importURL.go b/cmd/importURL.go index cb1640f7..1c0b28c8 100644 --- a/cmd/importURL.go +++ b/cmd/importURL.go @@ -108,9 +108,10 @@ func NewImportURLCommand(globalClientOpts *connectors.ClientOptions) *cobra.Comm if n > 2 { val, err := strconv.ParseBool(urlAndMainAtrifactAndSecretName[2]) if err != nil { - fmt.Println(err) + fmt.Printf("Cannot parse '%s' as Bool, default to true\n", urlAndMainAtrifactAndSecretName[2]) + } else { + mainArtifact = val } - mainArtifact = val } if n > 3 { secret = urlAndMainAtrifactAndSecretName[3] From 69e2cd5a23fac53cc2181cc011be49fd59f2a900 Mon Sep 17 00:00:00 2001 From: Adesh Deshmukh Date: Sun, 21 Jun 2026 17:40:30 +0530 Subject: [PATCH 2/3] fix(import): use LastIndex to safely handle paths with colons Signed-off-by: Adesh Deshmukh --- cmd/import.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/import.go b/cmd/import.go index 774b8a14..e1e51a27 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -122,10 +122,11 @@ func NewImportCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command // Check if mainArtifact flag is provided. if strings.Contains(f, ":") { - pathAndMainArtifact := strings.Split(f, ":") - f = pathAndMainArtifact[0] - if val, parseErr := strconv.ParseBool(pathAndMainArtifact[1]); parseErr != nil { - fmt.Printf("Cannot parse '%s' as Bool, default to true\n", pathAndMainArtifact[1]) + lastColon := strings.LastIndex(f, ":") + boolPart := f[lastColon+1:] + f = f[:lastColon] + if val, parseErr := strconv.ParseBool(boolPart); parseErr != nil { + fmt.Printf("Cannot parse '%s' as Bool, default to true\n", boolPart) } else { mainArtifact = val } From c570f437e5bdf7a2795e94f62be98c267c46d1b7 Mon Sep 17 00:00:00 2001 From: Adesh Deshmukh Date: Thu, 16 Jul 2026 19:55:24 +0530 Subject: [PATCH 3/3] fix: preserve mainArtifact default on parse failure and fix URL port corruption cmd/import.go: only truncate path when suffix is a valid boolean cmd/importURL.go: extract parseImportURLArg() with right-to-left scan to handle URLs with ports/paths while fixing bool parse cmd/importURL_test.go: 14 test cases covering URL+port, bool, secret, and malformed bool scenarios Signed-off-by: Adesh Deshmukh --- cmd/import.go | 2 +- cmd/importURL.go | 40 +++++++----- cmd/importURL_test.go | 141 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 cmd/importURL_test.go diff --git a/cmd/import.go b/cmd/import.go index e1e51a27..36c7ac20 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -124,11 +124,11 @@ func NewImportCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command if strings.Contains(f, ":") { lastColon := strings.LastIndex(f, ":") boolPart := f[lastColon+1:] - f = f[:lastColon] if val, parseErr := strconv.ParseBool(boolPart); parseErr != nil { fmt.Printf("Cannot parse '%s' as Bool, default to true\n", boolPart) } else { mainArtifact = val + f = f[:lastColon] } } diff --git a/cmd/importURL.go b/cmd/importURL.go index 1c0b28c8..8757e428 100644 --- a/cmd/importURL.go +++ b/cmd/importURL.go @@ -100,23 +100,7 @@ func NewImportURLCommand(globalClientOpts *connectors.ClientOptions) *cobra.Comm mainArtifact := true secret := "" - // Check if URL starts with https or http - if strings.HasPrefix(f, "https://") || strings.HasPrefix(f, "http://") { - urlAndMainAtrifactAndSecretName := strings.Split(f, ":") - n := len(urlAndMainAtrifactAndSecretName) - f = urlAndMainAtrifactAndSecretName[0] + ":" + urlAndMainAtrifactAndSecretName[1] - if n > 2 { - val, err := strconv.ParseBool(urlAndMainAtrifactAndSecretName[2]) - if err != nil { - fmt.Printf("Cannot parse '%s' as Bool, default to true\n", urlAndMainAtrifactAndSecretName[2]) - } else { - mainArtifact = val - } - } - if n > 3 { - secret = urlAndMainAtrifactAndSecretName[3] - } - } + f, mainArtifact, secret = parseImportURLArg(f) // Try downloading the artifcat msg, err := mc.DownloadArtifact(f, mainArtifact, secret) @@ -131,3 +115,25 @@ func NewImportURLCommand(globalClientOpts *connectors.ClientOptions) *cobra.Comm return importURLCmd } + +func parseImportURLArg(f string) (string, bool, string) { + mainArtifact := true + secret := "" + + if strings.HasPrefix(f, "https://") || strings.HasPrefix(f, "http://") { + parts := strings.Split(f, ":") + n := len(parts) + + for i := n - 1; i >= 2; i-- { + if val, parseErr := strconv.ParseBool(parts[i]); parseErr == nil { + mainArtifact = val + if i+1 < n { + secret = strings.Join(parts[i+1:], ":") + } + f = strings.Join(parts[:i], ":") + break + } + } + } + return f, mainArtifact, secret +} diff --git a/cmd/importURL_test.go b/cmd/importURL_test.go new file mode 100644 index 00000000..abfae865 --- /dev/null +++ b/cmd/importURL_test.go @@ -0,0 +1,141 @@ +/* + * Copyright The Microcks Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseImportURLArg(t *testing.T) { + tests := []struct { + name string + input string + expectedURL string + expectedMainArtifact bool + expectedSecret string + }{ + { + name: "standard URL without suffixes", + input: "https://example.com/openapi.yaml", + expectedURL: "https://example.com/openapi.yaml", + expectedMainArtifact: true, + expectedSecret: "", + }, + { + name: "standard URL with mainArtifact true", + input: "https://example.com/spec1.yaml:true", + expectedURL: "https://example.com/spec1.yaml", + expectedMainArtifact: true, + expectedSecret: "", + }, + { + name: "standard URL with mainArtifact false", + input: "https://example.com/spec1.yaml:false", + expectedURL: "https://example.com/spec1.yaml", + expectedMainArtifact: false, + expectedSecret: "", + }, + { + name: "standard URL with mainArtifact and secret", + input: "https://example.com/spec1.yaml:true:my-secret", + expectedURL: "https://example.com/spec1.yaml", + expectedMainArtifact: true, + expectedSecret: "my-secret", + }, + { + name: "standard URL with mainArtifact false and secret", + input: "https://example.com/spec1.yaml:false:my-secret-token", + expectedURL: "https://example.com/spec1.yaml", + expectedMainArtifact: false, + expectedSecret: "my-secret-token", + }, + { + name: "URL with port and no suffixes", + input: "http://localhost:8585/spec.yaml", + expectedURL: "http://localhost:8585/spec.yaml", + expectedMainArtifact: true, + expectedSecret: "", + }, + { + name: "URL with port and mainArtifact true", + input: "http://localhost:8585/spec.yaml:true", + expectedURL: "http://localhost:8585/spec.yaml", + expectedMainArtifact: true, + expectedSecret: "", + }, + { + name: "URL with port and mainArtifact false", + input: "http://localhost:8585/spec.yaml:false", + expectedURL: "http://localhost:8585/spec.yaml", + expectedMainArtifact: false, + expectedSecret: "", + }, + { + name: "URL with port, mainArtifact and secret", + input: "http://localhost:8585/spec.yaml:true:my-secret-token", + expectedURL: "http://localhost:8585/spec.yaml", + expectedMainArtifact: true, + expectedSecret: "my-secret-token", + }, + { + name: "URL with port, mainArtifact false and secret", + input: "http://localhost:8585/spec.yaml:false:my-secret-token", + expectedURL: "http://localhost:8585/spec.yaml", + expectedMainArtifact: false, + expectedSecret: "my-secret-token", + }, + { + name: "malformed bool suffix preserves URL", + input: "http://localhost:8585/spec.yaml:tru", + expectedURL: "http://localhost:8585/spec.yaml:tru", + expectedMainArtifact: true, + expectedSecret: "", + }, + { + name: "malformed bool with secret preserves URL", + input: "http://localhost:8585/spec.yaml:tru:mysecret", + expectedURL: "http://localhost:8585/spec.yaml:tru:mysecret", + expectedMainArtifact: true, + expectedSecret: "", + }, + { + name: "URL with path and no port", + input: "https://raw.githubusercontent.com/org/repo/main/spec.yaml", + expectedURL: "https://raw.githubusercontent.com/org/repo/main/spec.yaml", + expectedMainArtifact: true, + expectedSecret: "", + }, + { + name: "short URL without path", + input: "http://localhost:true", + expectedURL: "http://localhost", + expectedMainArtifact: true, + expectedSecret: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + url, mainArtifact, secret := parseImportURLArg(tt.input) + assert.Equal(t, tt.expectedURL, url) + assert.Equal(t, tt.expectedMainArtifact, mainArtifact) + assert.Equal(t, tt.expectedSecret, secret) + }) + } +}