Skip to content

Commit 72e2387

Browse files
crenshaw-devalexmttodaywasawesome
authored
fix(security): repository.GetDetailedProject exposes repo secrets (#24389)
Signed-off-by: Alexander Matyushentsev <[email protected]> Signed-off-by: Dan Garfield <[email protected]> Signed-off-by: Michael Crenshaw <[email protected]> Co-authored-by: Alexander Matyushentsev <[email protected]> Co-authored-by: Dan Garfield <[email protected]>
1 parent f8eba3e commit 72e2387

File tree

7 files changed

+102
-17
lines changed

7 files changed

+102
-17
lines changed

docs/operator-manual/upgrading/2.13-2.14.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@
33
## Upgraded Helm Version
44

55
Helm was upgraded to 3.16.2 and the skipSchemaValidation Flag was added to
6-
the [CLI and Application CR](https://argo-cd.readthedocs.io/en/latest/user-guide/helm/#helm-skip-schema-validation).
6+
the [CLI and Application CR](https://argo-cd.readthedocs.io/en/latest/user-guide/helm/#helm-skip-schema-validation).
7+
8+
## Breaking Changes
9+
10+
## Sanitized project API response
11+
12+
Due to security reasons ([GHSA-786q-9hcg-v9ff](https://github.com/argoproj/argo-cd/security/advisories/GHSA-786q-9hcg-v9ff)),
13+
the project API response was sanitized to remove sensitive information. This includes
14+
credentials of project-scoped repositories and clusters.

pkg/apis/application/v1alpha1/repository_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,6 @@ func (m *Repository) Sanitized() *Repository {
290290
Repo: m.Repo,
291291
Type: m.Type,
292292
Name: m.Name,
293-
Username: m.Username,
294293
Insecure: m.IsInsecure(),
295294
EnableLFS: m.EnableLFS,
296295
EnableOCI: m.EnableOCI,

pkg/apis/application/v1alpha1/types.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,32 @@ type Cluster struct {
20992099
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,13,opt,name=annotations"`
21002100
}
21012101

2102+
func (c *Cluster) Sanitized() *Cluster {
2103+
return &Cluster{
2104+
ID: c.ID,
2105+
Server: c.Server,
2106+
Name: c.Name,
2107+
Project: c.Project,
2108+
Namespaces: c.Namespaces,
2109+
Shard: c.Shard,
2110+
Labels: c.Labels,
2111+
Annotations: c.Annotations,
2112+
ClusterResources: c.ClusterResources,
2113+
ConnectionState: c.ConnectionState,
2114+
ServerVersion: c.ServerVersion,
2115+
Info: c.Info,
2116+
RefreshRequestedAt: c.RefreshRequestedAt,
2117+
Config: ClusterConfig{
2118+
AWSAuthConfig: c.Config.AWSAuthConfig,
2119+
ProxyUrl: c.Config.ProxyUrl,
2120+
DisableCompression: c.Config.DisableCompression,
2121+
TLSClientConfig: TLSClientConfig{
2122+
Insecure: c.Config.Insecure,
2123+
},
2124+
},
2125+
}
2126+
}
2127+
21022128
// Equals returns true if two cluster objects are considered to be equal
21032129
func (c *Cluster) Equals(other *Cluster) bool {
21042130
if c.Server != other.Server {

pkg/apis/application/v1alpha1/types_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4348,3 +4348,58 @@ func TestCluster_ParseProxyUrl(t *testing.T) {
43484348
}
43494349
}
43504350
}
4351+
4352+
func TestSanitized(t *testing.T) {
4353+
now := metav1.Now()
4354+
cluster := &Cluster{
4355+
ID: "123",
4356+
Server: "https://example.com",
4357+
Name: "example",
4358+
ServerVersion: "v1.0.0",
4359+
Namespaces: []string{"default", "kube-system"},
4360+
Project: "default",
4361+
Labels: map[string]string{
4362+
"env": "production",
4363+
},
4364+
Annotations: map[string]string{
4365+
"annotation-key": "annotation-value",
4366+
},
4367+
ConnectionState: ConnectionState{
4368+
Status: ConnectionStatusSuccessful,
4369+
Message: "Connection successful",
4370+
ModifiedAt: &now,
4371+
},
4372+
Config: ClusterConfig{
4373+
Username: "admin",
4374+
Password: "password123",
4375+
BearerToken: "abc",
4376+
TLSClientConfig: TLSClientConfig{
4377+
Insecure: true,
4378+
},
4379+
ExecProviderConfig: &ExecProviderConfig{
4380+
Command: "test",
4381+
},
4382+
},
4383+
}
4384+
4385+
assert.Equal(t, &Cluster{
4386+
ID: "123",
4387+
Server: "https://example.com",
4388+
Name: "example",
4389+
ServerVersion: "v1.0.0",
4390+
Namespaces: []string{"default", "kube-system"},
4391+
Project: "default",
4392+
Labels: map[string]string{"env": "production"},
4393+
Annotations: map[string]string{"annotation-key": "annotation-value"},
4394+
ConnectionState: ConnectionState{
4395+
Status: ConnectionStatusSuccessful,
4396+
Message: "Connection successful",
4397+
ModifiedAt: &now,
4398+
},
4399+
Config: ClusterConfig{
4400+
TLSClientConfig: TLSClientConfig{
4401+
Insecure: true,
4402+
},
4403+
},
4404+
}, cluster.Sanitized())
4405+
}

server/cluster/cluster.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -471,19 +471,8 @@ func (s *Server) RotateAuth(ctx context.Context, q *cluster.ClusterQuery) (*clus
471471
}
472472

473473
func (s *Server) toAPIResponse(clust *appv1.Cluster) *appv1.Cluster {
474+
clust = clust.Sanitized()
474475
_ = s.cache.GetClusterInfo(clust.Server, &clust.Info)
475-
476-
clust.Config.Password = ""
477-
clust.Config.BearerToken = ""
478-
clust.Config.TLSClientConfig.KeyData = nil
479-
if clust.Config.ExecProviderConfig != nil {
480-
// We can't know what the user has put into args or
481-
// env vars on the exec provider that might be sensitive
482-
// (e.g. --private-key=XXX, PASSWORD=XXX)
483-
// Implicitly assumes the command executable name is non-sensitive
484-
clust.Config.ExecProviderConfig.Env = make(map[string]string)
485-
clust.Config.ExecProviderConfig.Args = nil
486-
}
487476
// populate deprecated fields for backward compatibility
488477
// nolint:staticcheck
489478
clust.ServerVersion = clust.Info.ServerVersion

server/project/project.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,12 +311,20 @@ func (s *Server) GetDetailedProject(ctx context.Context, q *project.ProjectQuery
311311
}
312312
proj.NormalizeJWTTokens()
313313
globalProjects := argo.GetGlobalProjects(proj, listersv1alpha1.NewAppProjectLister(s.projInformer.GetIndexer()), s.settingsMgr)
314+
var apiRepos []*v1alpha1.Repository
315+
for _, repo := range repositories {
316+
apiRepos = append(apiRepos, repo.Normalize().Sanitized())
317+
}
318+
var apiClusters []*v1alpha1.Cluster
319+
for _, cluster := range clusters {
320+
apiClusters = append(apiClusters, cluster.Sanitized())
321+
}
314322

315323
return &project.DetailedProjectsResponse{
316324
GlobalProjects: globalProjects,
317325
Project: proj,
318-
Repositories: repositories,
319-
Clusters: clusters,
326+
Repositories: apiRepos,
327+
Clusters: apiClusters,
320328
}, err
321329
}
322330

server/repository/repository_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ func TestRepositoryServer(t *testing.T) {
315315
testRepo := &appsv1.Repository{
316316
Repo: url,
317317
Type: "git",
318-
Username: "foo",
318+
Username: "",
319319
InheritedCreds: true,
320320
}
321321
db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{testRepo}, nil)

0 commit comments

Comments
 (0)