diff --git a/pkg/webtests/link_sharing_test.go b/pkg/webtests/link_sharing_test.go index f18465acc..6e3f0e7b4 100644 --- a/pkg/webtests/link_sharing_test.go +++ b/pkg/webtests/link_sharing_test.go @@ -20,6 +20,7 @@ import ( "net/url" "testing" + "code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/web/handler" @@ -31,7 +32,7 @@ func TestLinkSharing(t *testing.T) { linkshareRead := &models.LinkSharing{ ID: 1, - Hash: "test1", + Hash: "test", // must match pkg/db/fixtures/link_shares.yml id=1 ProjectID: 1, Permission: models.PermissionRead, SharingType: models.SharingTypeWithoutPassword, @@ -807,4 +808,30 @@ func TestLinkSharing(t *testing.T) { }) }) }) + + // Regression test for GHSA-96q5-xm3p-7m84 / CVE-2026-35594: a still-valid + // link share JWT must be rejected once its DB row is gone. + // + // bootstrapTestRequest reloads fixtures, so the row must be deleted + // AFTER bootstrapping, otherwise the reload restores it. + t.Run("Deleted share rejects its still-valid JWT", func(t *testing.T) { + projectReadAllHandler := handler.WebHandler{ + EmptyStruct: func() handler.CObject { + return &models.Project{} + }, + } + + c, _ := bootstrapTestRequest(t, "GET", "", nil, nil) + addLinkShareTokenToContext(t, linkshareRead, c) + + sess := db.NewSession() + defer sess.Close() + _, err := sess.Where("id = ?", 1).Delete(&models.LinkSharing{}) + require.NoError(t, err) + require.NoError(t, sess.Commit()) + + err = projectReadAllHandler.ReadAllWeb(c) + require.Error(t, err) + assertHandlerErrorCode(t, err, models.ErrCodeLinkShareTokenInvalid) + }) }