VYPR
High severityNVD Advisory· Published Feb 25, 2026· Updated Feb 26, 2026

Vikunja has Path Traversal in CLI Restore

CVE-2026-27819

Description

Vikunja is an open-source self-hosted task management platform. Prior to version 2.0.0, the restoreConfig function in vikunja/pkg/modules/dump/restore.go of the go-vikunja/vikunja repository fails to sanitize file paths within the provided ZIP archive. A maliciously crafted ZIP can bypass the intended extraction directory to overwrite arbitrary files on the host system. Additionally, we’ve discovered that a malformed archive triggers a runtime panic, crashing the process immediately after the database has been wiped permanently. The application trusts the metadata in the ZIP archive. It uses the Name attribute of the zip.File struct directly in os.OpenFile calls without validation, allowing files to be written outside the intended directory. The restoration logic assumes a specific directory structure within the ZIP. When provided with a "minimalist" malicious ZIP, the application fails to validate the length of slices derived from the archive contents. Specifically, at line 154, the code attempts to access an index of len(ms)-2 on an insufficiently populated slice, triggering a panic. Version 2.0.0 fixes the issue.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Vikunja prior to 2.0.0 has a path traversal and DoS in CLI restore: a crafted ZIP can overwrite arbitrary files or crash after wiping the database.

Vulnerability

Overview

CVE-2026-27819 describes two critical flaws in the Vikunja CLI restore functionality (versions prior to 2.0.0). The restoreConfig function in vikunja/pkg/modules/dump/restore.go fails to sanitize file paths from the ZIP archive, directly using the Name attribute of zip.File in os.OpenFile calls. This allows a maliciously crafted ZIP to bypass the intended extraction directory and overwrite arbitrary files on the host system [1][3].

Exploitation and

Attack Surface

An attacker with administrative CLI access (required to run the restore command) can supply a specially crafted ZIP archive. The archive can contain path traversal entries (e.g., ../../../pwned.txt) to write files outside the restore directory. Additionally, a "minimalist" malicious ZIP that lacks the expected to contain a specific directory structure can trigger a runtime panic. The code at line 154 attempts to access index len(ms)-2 on an insufficiently populated slice, causing an index-out-of-range panic [1][3].

Impact

Successful exploitation of the path traversal can lead to arbitrary file overwrite, potentially compromising the host system. The denial-of-service aspect is particularly severe: the restore process wipes the existing database before validating the archive contents. If the archive triggers a panic after the wipe, the application is left in a non-functional state with complete data loss [1][3].

Mitigation

Version 2.0.0 of Vikunja fixes both issues. The commit [4] introduces pre-validation of all table data JSON before wiping the database, and adds proper sanitization of file paths. Users are strongly advised to update immediately [2].

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
code.vikunja.io/apiGo
<= 0.24.6

Affected products

2
  • Vikunja/Vikunjallm-fuzzy
    Range: <2.0.0
  • go-vikunja/vikunjav5
    Range: < 2.0.0

Patches

1
1b3d8dc59cb5

fix(restore): pre-validate all table data JSON before wiping database

https://github.com/go-vikunja/vikunjakolaenteFeb 24, 2026via ghsa
1 file changed · +38 2
  • pkg/modules/dump/restore.go+38 2 modified
    @@ -48,6 +48,19 @@ import (
     const maxConfigSize = 5 * 1024 * 1024    // 5 MB, should be largely enough
     const maxDumpEntrySize = 500 * 1024 * 1024 // 500 MB
     
    +// parseDbFileName validates and extracts the table name from a database dump filename.
    +// Returns the table name and true if valid, or empty string and false if invalid.
    +func parseDbFileName(fname string) (string, bool) {
    +	if !strings.HasSuffix(fname, ".json") {
    +		return "", false
    +	}
    +	tableName := strings.TrimSuffix(fname, ".json")
    +	if tableName == "" || strings.ContainsAny(tableName, "/\\") {
    +		return "", false
    +	}
    +	return tableName, true
    +}
    +
     // Restore takes a zip file name and restores it
     func Restore(filename string, overrideConfig bool) error {
     
    @@ -84,10 +97,11 @@ func Restore(filename string, overrideConfig bool) error {
     		}
     		if strings.HasPrefix(file.Name, "database/") {
     			fname := strings.TrimPrefix(file.Name, "database/")
    -			if !strings.HasSuffix(fname, ".json") || len(fname) <= 5 {
    +			tableName, valid := parseDbFileName(fname)
    +			if !valid {
     				return fmt.Errorf("invalid database file name in zip archive: %q", file.Name)
     			}
    -			dbfiles[fname[:len(fname)-5]] = file
    +			dbfiles[tableName] = file
     			continue
     		}
     		if file.Name == ".env" {
    @@ -166,6 +180,28 @@ func Restore(filename string, overrideConfig bool) error {
     
     	lastMigration := ms[len(ms)-2]
     
    +	// Pre-validate all table data JSON before wiping to avoid leaving the database
    +	// in a destroyed state when the archive contains corrupted table data.
    +	for table, d := range dbfiles {
    +		if table == "migration" {
    +			continue
    +		}
    +		rc, err := d.Open()
    +		if err != nil {
    +			return fmt.Errorf("could not open table data for %s: %w", table, err)
    +		}
    +		var bufValidate bytes.Buffer
    +		if _, err := bufValidate.ReadFrom(io.LimitReader(rc, maxDumpEntrySize)); err != nil {
    +			rc.Close()
    +			return fmt.Errorf("could not read table data for %s: %w", table, err)
    +		}
    +		rc.Close()
    +		var test []map[string]interface{}
    +		if err := json.Unmarshal(bufValidate.Bytes(), &test); err != nil {
    +			return fmt.Errorf("invalid JSON in table data for %s: %w", table, err)
    +		}
    +	}
    +
     	// Start by wiping everything - only after we've validated the archive
     	if err := db.WipeEverything(); err != nil {
     		return fmt.Errorf("could not wipe database: %w", err)
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.