test(security): webtest that a deleted link share rejects its still-valid JWT

End-to-end regression test for GHSA-96q5-xm3p-7m84 / CVE-2026-35594: mints
a JWT for a link share via the real helper, then deletes the share row and
invokes the real ReadAllWeb handler to prove the full request path (not
just the unit-tested GetLinkShareFromClaims) surfaces the revocation.

Also fixes a pre-existing stale literal in the TestLinkSharing test fixture
struct: linkshareRead declared Hash="test1" while the actual fixture row
id=1 uses Hash="test". The old code never looked at the DB so the mismatch
went unnoticed; after the fix it would cause every link-share webtest that
used linkshareRead to fail hash validation.
This commit is contained in:
kolaente
2026-04-09 17:04:24 +02:00
committed by kolaente
parent e025209e3c
commit 379d8a5c19

View File

@@ -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)
})
}