CVE-2026-29794: Code.vikunja.io/api Vikunja Has A Rate-Limit Bypass Vulnerability
MODERATECVE-2026-29794 is a MODERATE (CVSS 5.3) vikunja has a rate-limit bypass vulnerability (CWE-807: Reliance on Untrusted Inputs in a Security Decision) affecting Code.vikunja.io/api libraries in the Go ecosystem. The issue involves vikunja has a rate-limit bypass for unauthenticated users via spoofed headers. Remediation is available by upgrading affected packages to version 2.2.0.
CVE-2026-29794 at a Glance
Security Impact
CVSS: 5.3
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L).
How to Fix CVE-2026-29794
To remediate this vulnerability, upgrade all affected Code.vikunja.io/api dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade code.vikunja.io/api to 2.2.0
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for code.vikunja.io/api
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-29794, inspect your project dependencies for Code.vikunja.io/api:
inspect dependencies for code.vikunja.io/api
Analyze the terminal output to locate any version of Code.vikunja.io/api in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.2.0), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-807: Reliance on Untrusted Inputs in a Security Decision, where the product uses a protection mechanism that relies on the existence or values of an input, but the input can be modified by an untrusted actor in a way that bypasses the protection mechanism. In Code.vikunja.io/api, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-29794 Affected Code.vikunja.io/api Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
code.vikunja.io/api
|
Go |
≥ 0.8
< 2.2.0
|
2.2.0
|
References
Frequently Asked Questions
What is CVE-2026-29794?
Which Code.vikunja.io/api versions are affected?
What version fixes CVE-2026-29794?
Is code.vikunja.io/api affected?
What is the CVSS score of CVE-2026-29794?
What CWE is associated with CVE-2026-29794?
How can I check Go dependencies for CVE-2026-29794?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-m547-hp4w-j6jx",
"modified" : "2026-03-20T21:28:21Z",
"published" : "2026-03-20T14:41:03Z",
"aliases" : [ "CVE-2026-29794" ],
"summary" : "Vikunja has a Rate-Limit Bypass for Unauthenticated Users via Spoofed Headers",
"details" : "### Summary\nUnauthenticated users are able to bypass the application's built-in rate-limits by spoofing the `X-Forwarded-For` or `X-Real-IP` headers due to the rate-limit relying on the value of `(echo.Context).RealIP`. \n\n### Details\nIn the first file below, the rate-limit for unauthenticated users can be observed being populated with the `ip` value. In the second file, it shows it using the `c.RealIP()` function for the `ip` case. Due to this, the rate-limit will rely on the value of one of the two mentioned headers (`X-Forwarded-For` or `X-Real-IP`). These can be spoofed by users client-side in order to completely bypass any unauthenticated rate-limits in place.\n\nSome reverse proxies like Traefik will overwrite this value by default, but others will not, leaving any deployment that either isn't using a reserve proxy that specifically overwrites the header's value or isn't using a reverse proxy vulnerable. \n\n**File 1:** pkg\\routes\\routes.go:318\n```go\n// This is the group with no auth\n\t// It is its own group to be able to rate limit this based on different heuristics\n\tn := a.Group(\"\")\n\tsetupRateLimit(n, \"ip\")\n\n\t// Docs\n\tn.GET(\"/docs.json\", apiv1.DocsJSON)\n\tn.GET(\"/docs\", apiv1.RedocUI)\n\n\t// Prometheus endpoint\n\tsetupMetrics(n)\n\n\t// Separate route for unauthenticated routes to enable rate limits for it\n\tur := a.Group(\"\")\n\trate := limiter.Rate{\n\t\tPeriod: 60 * time.Second,\n\t\tLimit: config.RateLimitNoAuthRoutesLimit.GetInt64(),\n\t}\n\trateLimiter := createRateLimiter(rate)\n\tur.Use(RateLimit(rateLimiter, \"ip\"))\n```\n**File 2:** pkg\\routes\\rate_limit.go:41\n```go\n// RateLimit is the rate limit middleware\nfunc RateLimit(rateLimiter *limiter.Limiter, rateLimitKind string) echo.MiddlewareFunc {\n\treturn func(next echo.HandlerFunc) echo.HandlerFunc {\n\t\treturn func(c *echo.Context) (err error) {\n\t\t\tvar rateLimitKey string\n\t\t\tswitch rateLimitKind {\n\t\t\tcase \"ip\":\n\t\t\t\trateLimitKey = c.RealIP()\n\t\t\tcase \"user\":\n\t\t\t\tauth, err := auth2.GetAuthFromClaims(c)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Errorf(\"Error getting auth from jwt claims: %v\", err)\n\t\t\t\t}\n\t\t\t\trateLimitKey = \"user_\" + strconv.FormatInt(auth.GetID(), 10)\n\t\t\tdefault:\n\t\t\t\tlog.Errorf(\"Unknown rate limit kind configured: %s\", rateLimitKind)\n\t\t\t}\n```\n\n### PoC\n1. Download and run the default docker compose file via the instructions here: [https://vikunja.io/install/](https://vikunja.io/install/). Do not configure a proxy.\n2. Once running, navigate to the application in a web browser that is using a web proxy, such as Burp Suite.\n3. Attempt to authenticate to the application with an invalid username and password.\n4. In the web proxy's logs, locate the request to the `/api/v1/login` endpoint. Observe that the response contains rate-limit details:\n```http\nHTTP/1.1 403 Forbidden\nCache-Control: no-store\nContent-Type: application/json\nVary: Origin\nX-Ratelimit-Limit: 10\nX-Ratelimit-Remaining: 9\nX-Ratelimit-Reset: 1772224455\nDate: Fri, 27 Feb 2026 20:33:16 GMT\nContent-Length: 54\n\n{\"code\":1011,\"message\":\"Wrong username or password.\"}\n```\n5. Add the `X-Forwarded-For` header with an arbitrary value, like so: `X-Forwarded-For: FakeValue`. Send the request 10 times, or until the rate-limit is at zero.\n6. Modify the `X-Forwarded-For` headers value to be different, like so: `X-Forwarded-For: NewValue`.\n7. Observe that the `X-Ratelimit-Remaining` header's value has reset its countdown and is back at `9`. \n\n### Impact\nUnauthenticated users can abuse endpoints available to them for different potential impacts. The immediate concern would be brute-forcing usernames or specific accounts' passwords. This bypass allows unlimited requests against unauthenticated endpoints.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Go",
"name" : "code.vikunja.io/api"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0.8"
}, {
"fixed" : "2.2.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/go-vikunja/vikunja/security/advisories/GHSA-m547-hp4w-j6jx"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-29794"
}, {
"type" : "WEB",
"url" : "https://github.com/go-vikunja/vikunja/commit/a498dd69915a006c07e9d82660a2185d7e8136ee"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/go-vikunja/vikunja"
}, {
"type" : "WEB",
"url" : "https://vikunja.io/changelog/vikunja-v2.2.0-was-released"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-807" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-03-20T14:41:03Z",
"nvd_published_at" : "2026-03-20T15:16:16Z"
}
}