Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions pkg/apis/application/v1alpha1/app_project_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,35 @@ func (proj AppProject) IsSourcePermitted(src ApplicationSource) bool {
return anySourceMatched
}

// IsCredentialPermittedForAnySource checks whether a credential template URL is a prefix of any
// permitted source repo. Credential URLs are prefix-based by design (e.g. "reg.example.com"
// provides auth for "reg.example.com/org/chart"), so a credential should be permitted if any
// sourceRepo falls under its URL prefix.
func (proj AppProject) IsCredentialPermittedForAnySource(credURL string) bool {
credNormalized := git.NormalizeGitURL(credURL)
if credNormalized == "" {
return false
}

for _, repoURL := range proj.Spec.SourceRepos {
if repoURL == "*" {
return true
}
if isDenyPattern(repoURL) {
continue
}
normalized := git.NormalizeGitURL(repoURL)
// Strip oci:// scheme for comparison since credential URLs are typically stored without it
normalizedNoScheme := strings.TrimPrefix(normalized, "oci://")
credNoScheme := strings.TrimPrefix(credNormalized, "oci://")
// Check if the sourceRepo URL starts with the credential URL prefix
if strings.HasPrefix(normalizedNoScheme, credNoScheme+"/") || normalizedNoScheme == credNoScheme {
return true
}
}
return false
}

// IsDestinationPermitted validates if the provided application's destination is one of the allowed destinations for the project
func (proj AppProject) IsDestinationPermitted(destCluster *Cluster, destNamespace string, projectClusters func(project string) ([]*Cluster, error)) (bool, error) {
if destCluster == nil {
Expand Down
75 changes: 75 additions & 0 deletions pkg/apis/application/v1alpha1/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,81 @@ func TestAppProject_IsNegatedSourcePermitted(t *testing.T) {
}
}

func TestAppProject_IsCredentialPermittedForAnySource(t *testing.T) {
testData := []struct {
name string
projSources []string
credURL string
isPermitted bool
}{{
name: "wildcard permits any credential",
projSources: []string{"*"},
credURL: "reg.example.com",
isPermitted: true,
}, {
name: "exact match permits credential",
projSources: []string{"reg.example.com"},
credURL: "reg.example.com",
isPermitted: true,
}, {
name: "credential URL is prefix of sourceRepo",
projSources: []string{"reg.example.com/org/charts"},
credURL: "reg.example.com",
isPermitted: true,
}, {
name: "credential URL is prefix of multiple sourceRepos",
projSources: []string{"https://github.com/test/repo.git", "reg.example.com/org/charts"},
credURL: "reg.example.com",
isPermitted: true,
}, {
name: "OCI sourceRepo with credential prefix",
projSources: []string{"oci://reg.example.com/org/charts"},
credURL: "reg.example.com",
isPermitted: true,
}, {
name: "OCI credential URL matches OCI sourceRepo",
projSources: []string{"oci://reg.example.com/org/charts"},
credURL: "oci://reg.example.com",
isPermitted: true,
}, {
name: "credential URL does not match any sourceRepo",
projSources: []string{"https://github.com/test/repo.git"},
credURL: "reg.example.com",
isPermitted: false,
}, {
name: "credential URL is more specific than sourceRepo",
projSources: []string{"reg.example.com"},
credURL: "reg.example.com/org/charts",
isPermitted: false,
}, {
name: "partial hostname match should not permit",
projSources: []string{"reg.example.com-evil/org/charts"},
credURL: "reg.example.com",
isPermitted: false,
}, {
name: "deny pattern does not permit credential",
projSources: []string{"!reg.example.com/org/charts"},
credURL: "reg.example.com",
isPermitted: false,
}, {
name: "empty sourceRepos does not permit credential",
projSources: []string{},
credURL: "reg.example.com",
isPermitted: false,
}}

for _, data := range testData {
t.Run(data.name, func(t *testing.T) {
proj := AppProject{
Spec: AppProjectSpec{
SourceRepos: data.projSources,
},
}
assert.Equal(t, data.isPermitted, proj.IsCredentialPermittedForAnySource(data.credURL))
})
}
}

func TestAppProject_IsDestinationPermitted(t *testing.T) {
t.Parallel()

Expand Down
2 changes: 1 addition & 1 deletion util/argo/argo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ func NormalizeSource(source *argoappv1.ApplicationSource) *argoappv1.Application
func GetPermittedReposCredentials(proj *argoappv1.AppProject, repoCreds []*argoappv1.RepoCreds) ([]*argoappv1.RepoCreds, error) {
var permittedRepoCreds []*argoappv1.RepoCreds
for _, v := range repoCreds {
if proj.IsSourcePermitted(argoappv1.ApplicationSource{RepoURL: v.URL}) {
if proj.IsSourcePermitted(argoappv1.ApplicationSource{RepoURL: v.URL}) || proj.IsCredentialPermittedForAnySource(v.URL) {
permittedRepoCreds = append(permittedRepoCreds, v)
}
}
Expand Down
Loading