diff --git a/server/channels/api4/user.go b/server/channels/api4/user.go index 7a3725d3ec9..584cefd8bbf 100644 --- a/server/channels/api4/user.go +++ b/server/channels/api4/user.go @@ -1923,16 +1923,17 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) { auditRec := c.MakeAuditRecord(model.AuditEventResetPassword, model.AuditStatusFail) defer c.LogAuditRec(auditRec) - c.LogAudit("attempt - token=" + token) + tokenPrefix := token[:5] + c.LogAudit("attempt - token_prefix=" + tokenPrefix) if err := c.App.ResetPasswordFromToken(c.AppContext, token, newPassword); err != nil { - c.LogAudit("fail - token=" + token) + c.LogAudit("fail - token_prefix=" + tokenPrefix) c.Err = err return } auditRec.Success() - c.LogAudit("success - token=" + token) + c.LogAudit("success - token_prefix=" + tokenPrefix) ReturnStatusOK(w) } diff --git a/server/channels/api4/user_test.go b/server/channels/api4/user_test.go index 110e2f7b09f..39f4bcdc4e3 100644 --- a/server/channels/api4/user_test.go +++ b/server/channels/api4/user_test.go @@ -3897,6 +3897,43 @@ func TestResetPassword(t *testing.T) { }) } +func TestResetPasswordAuditDoesNotLeakToken(t *testing.T) { + th := Setup(t).InitBasic(t) + + user := th.BasicUser + + tokenExtra, err := json.Marshal(struct { + UserId string + Email string + }{UserId: user.Id, Email: user.Email}) + require.NoError(t, err) + + token := model.NewToken(model.TokenTypePasswordRecovery, string(tokenExtra)) + require.NoError(t, th.App.Srv().Store().Token().Save(token)) + defer func() { + _ = th.App.Srv().Store().Token().Delete(token.Token) + }() + + _, err = th.Client.ResetPassword(context.Background(), token.Token, "newPassword1!") + require.NoError(t, err) + + audits, appErr := th.App.GetAudits(request.EmptyContext(th.TestLogger), "", 100) + require.Nil(t, appErr) + + found := false + for _, audit := range audits { + if !strings.Contains(audit.Action, "password/reset") { + continue + } + found = true + require.NotContains(t, audit.ExtraInfo, token.Token, + "Full reset token should not appear in audit log ExtraInfo") + require.Contains(t, audit.ExtraInfo, token.Token[:5], + "Audit log should contain the token prefix for correlation") + } + require.True(t, found, "Expected at least one audit entry for password/reset") +} + func TestGetSessions(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic(t)