Vikunja, an open-source, self-hosted task management tool similar to Todoist, ships with a flaw in its scoped API token enforcement. Tokens granted only the projects.background permission—intended for reading project backgrounds—can delete them via the DELETE endpoint. Tokens with the specific projects.background_delete permission get rejected with a 401. This is an authorization bypass affecting automation scripts and integrations that rely on narrowly scoped tokens.
The issue stems from method-confused permission checks in the API routes. I verified it on commit c5450fb55f5192508638cbb3a6956438452a712e from Vikunja’s repo at code.vikunja.io/api. The routes register distinct permissions:
GET /api/v1/projects/<project>/backgroundrequiresprojects.backgroundDELETE /api/v1/projects/<project>/backgroundrequiresprojects.background_delete
But the enforcement function CanDoAPIRoute() in pkg/models/api_routes.go reconstructs permissions from path segments alone, ignoring the HTTP method. For DELETE requests, it falls back to the parent projects.background, letting mismatched tokens through. The delete handler in pkg/modules/background/handler/background.go then runs unchecked: it verifies project update rights (which the token holder has indirectly), deletes the background file, and clears the BackgroundFileID on the project.
Proof of Concept
Reproduce it like this:
- Log in as a user with update rights on a project that has a custom background.
- Create a scoped API token with
{"projects":["background"]}. - Send the DELETE request:
curl -X DELETE \ -H "Authorization: Bearer <your-token>" \ "https://your-vikunja/api/v1/projects/<project-id>/background"The background deletes successfully.
Compare with a token scoped to {"projects":["background_delete"]}: the same request fails with 401 Unauthorized. Three checks confirm the bug:
- The
/api/v1/routesendpoint lists both permissions distinctly. - Unit tests in the matcher show
CanDoAPIRoute()greenlights DELETE underbackground. - End-to-end web tests prove real deletion with the weak token.
Why This Matters
Vikunja targets privacy-focused users who self-host to avoid SaaS data leaks—over 10,000 stars on GitHub, active since 2019. Scoped tokens let you grant minimal access for bots, CI/CD pipelines, or mobile apps without full user creds. This bug breaks that model: a read-only background token becomes destructive.
Attack requires a valid token from a project updater, so no zero-days from public endpoints. But implications hit real workflows. Say you script a dashboard to pull project visuals: one bad cron job wipes backgrounds across teams. Or third-party tools with narrow scopes escalate silently. In multi-user setups, shared tokens amplify risk—least privilege crumbles.
CVEs aren’t assigned yet, rated medium by the advisory, fair given no RCE or mass compromise. Still, self-hosters should audit tokens now. Patch lands in recent releases; check changelog post-commit. Run /api/v1/routes to inspect your setup. Broader lesson: API auth loves shortcuts like path-only matching, but HTTP methods matter. Tools like Vikunja prove even mature open-source code (v0.22+ era) slips on edge cases.
Fix it by regenerating tokens with full scopes or upgrading. Test your own APIs: do permission checkers respect methods? In crypto/security circles, we see this pattern in wallet APIs or DeFi dashboards—over-permissive tokens lead to drains. Vikunja users, rotate those background tokens today.