From c479df17e4b34cb7783a5d786f79768038abbb72 Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Wed, 14 Oct 2026 21:21:53 +0200 Subject: [PATCH] fix: hide app/client token in update methods --- api/application.go | 15 ++++++++------- api/application_test.go | 11 ++++++++++- api/client.go | 13 +++++++++---- api/client_test.go | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/api/application.go b/api/application.go index 7617da0de..5313368ba 100644 --- a/api/application.go +++ b/api/application.go @@ -109,8 +109,9 @@ func (a *ApplicationAPI) CreateApplication(ctx *gin.Context) { handleApplicationError(ctx, err) return } + sanitizeApplication(&app) app.Token = tokenPrivate - ctx.JSON(200, withResolvedImage(&app)) + ctx.JSON(200, &app) } } @@ -145,8 +146,7 @@ func (a *ApplicationAPI) GetApplications(ctx *gin.Context) { return } for _, app := range apps { - app.Token = "" - withResolvedImage(app) + sanitizeApplication(app) } ctx.JSON(200, apps) } @@ -274,7 +274,7 @@ func (a *ApplicationAPI) UpdateApplication(ctx *gin.Context) { handleApplicationError(ctx, err) return } - ctx.JSON(200, withResolvedImage(app)) + ctx.JSON(200, sanitizeApplication(app)) } } else { ctx.AbortWithError(404, fmt.Errorf("app with id %d doesn't exists", id)) @@ -467,7 +467,7 @@ func (a *ApplicationAPI) UploadApplicationImage(ctx *gin.Context) { if oldImage != "" { os.Remove(a.ImageDir + oldImage) } - ctx.JSON(200, withResolvedImage(app)) + ctx.JSON(200, sanitizeApplication(app)) } else { ctx.AbortWithError(404, fmt.Errorf("app with id %d doesn't exists", id)) } @@ -531,14 +531,15 @@ func (a *ApplicationAPI) RemoveApplicationImage(ctx *gin.Context) { return } os.Remove(a.ImageDir + image) - ctx.JSON(200, withResolvedImage(app)) + ctx.JSON(200, sanitizeApplication(app)) } else { ctx.AbortWithError(404, fmt.Errorf("app with id %d doesn't exists", id)) } }) } -func withResolvedImage(app *model.Application) *model.Application { +func sanitizeApplication(app *model.Application) *model.Application { + app.Token = "" if app.Image == "" { // This must stay in sync with the isDefaultImage check in ui/src/application/Applications.tsx. app.Image = "static/defaultapp.png" diff --git a/api/application_test.go b/api/application_test.go index dfa8edf52..b54f74169 100644 --- a/api/application_test.go +++ b/api/application_test.go @@ -591,7 +591,7 @@ func (s *ApplicationSuite) Test_RemoveAppImage_expectSuccess() { s.db.User(5) imageFile := "existing.png" - s.db.CreateApplication(&model.Application{UserID: 5, ID: 1, Image: imageFile}) + s.db.CreateApplication(&model.Application{UserID: 5, ID: 1, Token: "AQm3x-Zp9LwKt7RbN2cYf5h", Image: imageFile}) fakeImage(s.T(), s.imageDir.Path(imageFile)) test.WithUser(s.ctx, 5) @@ -603,6 +603,7 @@ func (s *ApplicationSuite) Test_RemoveAppImage_expectSuccess() { assert.True(s.T(), os.IsNotExist(err)) assert.Equal(s.T(), 200, s.recorder.Code) + assert.NotContains(s.T(), s.recorder.Body.String(), "AQm3x-Zp9LwKt7RbN2cYf5h") } func (s *ApplicationSuite) Test_UpdateApplicationNameAndDescription_expectSuccess() { @@ -624,6 +625,14 @@ func (s *ApplicationSuite) Test_UpdateApplicationNameAndDescription_expectSucces } assert.Equal(s.T(), 200, s.recorder.Code) + test.BodyEquals(s.T(), &model.Application{ + ID: 2, + Name: "new_name", + Description: "new_description_text", + Image: "static/defaultapp.png", + SortKey: "a0", + CreatedAt: testdb.Now, + }, s.recorder) if app, err := s.db.GetApplicationByID(2); assert.NoError(s.T(), err) { assert.Equal(s.T(), expected, app) } diff --git a/api/client.go b/api/client.go index 4d33ee5cf..58d203475 100644 --- a/api/client.go +++ b/api/client.go @@ -106,6 +106,7 @@ func (a *ClientAPI) UpdateClient(ctx *gin.Context) { if success := successOrAbort(ctx, 500, a.DB.UpdateClient(client)); !success { return } + sanitizeClient(client, time.Now()) ctx.JSON(200, client) } } else { @@ -200,14 +201,18 @@ func (a *ClientAPI) GetClients(ctx *gin.Context) { } now := time.Now() for _, client := range clients { - client.Token = "" - if client.ElevatedUntil != nil && !now.Before(*client.ElevatedUntil) { - client.ElevatedUntil = nil - } + sanitizeClient(client, now) } ctx.JSON(200, clients) } +func sanitizeClient(client *model.Client, now time.Time) { + client.Token = "" + if client.ElevatedUntil != nil && !now.Before(*client.ElevatedUntil) { + client.ElevatedUntil = nil + } +} + // DeleteClient deletes a client by its id. // swagger:operation DELETE /client/{id} client deleteClient // diff --git a/api/client_test.go b/api/client_test.go index 7c9ae60a1..b498c1d08 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -254,11 +254,26 @@ func (s *ClientSuite) Test_UpdateClient_expectSuccess() { } assert.Equal(s.T(), 200, s.recorder.Code) + test.BodyEquals(s.T(), &model.Client{ID: 1, Name: "firefox", CreatedAt: testdb.Now}, s.recorder) if client, err := s.db.GetClientByID(1); assert.NoError(s.T(), err) { assert.Equal(s.T(), expected, client) } } +func (s *ClientSuite) Test_UpdateClient_doesNotExposeToken_andNormalizesElevatedUntil() { + expired := testdb.Now.Add(-time.Minute) + s.db.User(5).ClientWithToken(1, "CVHA0kbE4q8f2TmLnXu7yRs") + assert.NoError(s.T(), s.db.UpdateClientElevatedUntil(1, &expired)) + + test.WithUser(s.ctx, 5) + s.withFormData("name=firefox") + s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} + s.a.UpdateClient(s.ctx) + + assert.Equal(s.T(), 200, s.recorder.Code) + test.BodyEquals(s.T(), &model.Client{ID: 1, Name: "firefox", CreatedAt: testdb.Now}, s.recorder) +} + func (s *ClientSuite) Test_UpdateClient_expectNotFound() { test.WithUser(s.ctx, 5) s.ctx.Params = gin.Params{{Key: "id", Value: "2"}}