diff --git a/pkg/resolution/resolver/git/resolver.go b/pkg/resolution/resolver/git/resolver.go index e81c794613c..3405eff6a78 100644 --- a/pkg/resolution/resolver/git/resolver.go +++ b/pkg/resolution/resolver/git/resolver.go @@ -23,6 +23,7 @@ import ( "fmt" "io" "os" + "regexp" "strings" "time" @@ -142,6 +143,15 @@ func (r *Resolver) Resolve(ctx context.Context, origParams []pipelinev1.Param) ( return r.resolveAPIGit(ctx, params) } +// validateRepoURL validates if the given URL is a valid git, http, https URL or +// starting with a / (a local repository). +func validateRepoURL(url string) bool { + // Explanation: + pattern := `^(/|[^@]+@[^:]+|(git|https?)://)` + re := regexp.MustCompile(pattern) + return re.MatchString(url) +} + func (r *Resolver) resolveAPIGit(ctx context.Context, params map[string]string) (framework.ResolvedResource, error) { // If we got here, the "repo" param was specified, so use the API approach scmType, serverURL, err := r.getSCMTypeAndServerURL(ctx, params) @@ -502,7 +512,11 @@ func populateDefaultParams(ctx context.Context, params []pipelinev1.Param) (map[ return nil, fmt.Errorf("missing required git resolver params: %s", strings.Join(missingParams, ", ")) } - // TODO(sbwsg): validate repo url is well-formed, git:// or https:// + // validate the url params if we are not using the SCM API + if paramsMap[repoParam] == "" && paramsMap[orgParam] == "" && !validateRepoURL(paramsMap[urlParam]) { + return nil, fmt.Errorf("invalid git repository url: %s", paramsMap[urlParam]) + } + // TODO(sbwsg): validate pathInRepo is valid relative pathInRepo return paramsMap, nil } diff --git a/pkg/resolution/resolver/git/resolver_test.go b/pkg/resolution/resolver/git/resolver_test.go index 0c383408b2f..a35ec7646a2 100644 --- a/pkg/resolution/resolver/git/resolver_test.go +++ b/pkg/resolution/resolver/git/resolver_test.go @@ -60,16 +60,84 @@ func TestGetSelector(t *testing.T) { } func TestValidateParams(t *testing.T) { - resolver := Resolver{} - - paramsWithRevision := map[string]string{ - urlParam: "http://foo", - pathParam: "bar", - revisionParam: "baz", + tests := []struct { + name string + wantErr string + params map[string]string + }{ + { + name: "params with revision", + params: map[string]string{ + urlParam: "http://foo/bar/hello/moto", + pathParam: "bar", + revisionParam: "baz", + }, + }, + { + name: "https url", + params: map[string]string{ + urlParam: "https://foo/bar/hello/moto", + pathParam: "bar", + revisionParam: "baz", + }, + }, + { + name: "https url with username password", + params: map[string]string{ + urlParam: "https://user:pass@foo/bar/hello/moto", + pathParam: "bar", + revisionParam: "baz", + }, + }, + { + name: "git server url", + params: map[string]string{ + urlParam: "git://repo/hello/moto", + pathParam: "bar", + revisionParam: "baz", + }, + }, + { + name: "git url from a local repository", + params: map[string]string{ + urlParam: "/tmp/repo", + pathParam: "bar", + revisionParam: "baz", + }, + }, + { + name: "git url from a git ssh repository", + params: map[string]string{ + urlParam: "git@host.com:foo/bar", + pathParam: "bar", + revisionParam: "baz", + }, + }, + { + name: "bad url", + params: map[string]string{ + urlParam: "foo://bar", + pathParam: "path", + revisionParam: "revision", + }, + wantErr: "invalid git repository url: foo://bar", + }, } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resolver := Resolver{} + err := resolver.ValidateParams(context.Background(), toParams(tt.params)) + if tt.wantErr == "" { + if err != nil { + t.Fatalf("unexpected error validating params: %v", err) + } + return + } - if err := resolver.ValidateParams(context.Background(), toParams(paramsWithRevision)); err != nil { - t.Fatalf("unexpected error validating params: %v", err) + if d := cmp.Diff(tt.wantErr, err.Error()); d != "" { + t.Errorf("unexpected error: %s", diff.PrintWantGot(d)) + } + }) } }