Zip Slip in Buffalo with Cockroachdb
Zip Slip in Buffalo with Cockroachdb — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when an application constructs file paths using untrusted input without proper validation. In the context of Buffalo, a web framework for Go, this typically surfaces during file extraction or upload handling. When a Buffalo application interacts with Cockroachdb, the risk is not that Cockroachdb introduces Zip Slip, but that application code storing or referencing files in Cockroachdb columns (e.g., as BYTEA or via external references) processes untrusted archive contents. If a developer extracts a user-provided archive in Buffalo and uses path components directly to name or locate files that are later linked to Cockroachdb records, malicious paths like ../../../etc/passwd can escape the intended directory. This becomes critical when file metadata (such as filenames or stored paths) are persisted in Cockroachdb and later used in responses or passed to downstream services. An attacker can leverage a crafted archive to traverse directories, overwrite files, or access sensitive system resources, while the associated metadata in Cockroachdb may record or reference the malicious path. Because Buffalo does not inherently sanitize filenames during extraction, and Cockroachdb stores the resulting paths or file identifiers, the vulnerability persists across the web layer and the database layer. The exposure is amplified if the application serves files via endpoints that read from Cockroachdb-stored paths without additional validation, enabling unauthorized file access or inclusion.
Cockroachdb-Specific Remediation in Buffalo — concrete code fixes
To mitigate Zip Slip in a Buffalo application that uses Cockroachdb, you must validate and sanitize file paths before storing or using them, and ensure extraction logic is confined to a safe base directory. Below are concrete remediation steps with Cockroachdb-integrated code examples.
1. Validate and clean paths during archive extraction
Use filepath.Clean and ensure the cleaned path remains within the target directory. Do not trust filenames from the archive.
import (
"archive/zip"
"io"
"os"
"path/filepath"
)
func safeExtract(zipPath, destDir string) error {
r, err := zip.OpenReader(zipPath)
if err != nil {
return err
}
defer r.Close()
for _, f := range r.File {
// Clean and ensure the path stays within destDir
cleanName := filepath.Clean(f.Name)
if !strings.HasPrefix(cleanName, "../") {
// Further guard against zip slip with filepath.Join
target := filepath.Join(destDir, cleanName)
if !strings.HasPrefix(target, filepath.Clean(destDir)+string(os.PathSeparator)) {
return fmt.Errorf("illegal path traversal: %s", f.Name)
}
if f.FileInfo().IsDir() {
os.MkdirAll(target, f.Mode())
continue
}
if err := os.MkdirAll(filepath.Dir(target), f.Mode()); err != nil {
return err
}
src, err := f.Open()
if err != nil {
return err
}
defer src.Close()
dst, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer dst.Close()
if _, err := io.Copy(dst, src); err != nil {
return err
}
}
}
return nil
}
2. Store safe paths in Cockroachdb and use parameterized queries
When persisting file metadata in Cockroachdb, store only the sanitized filename or a generated identifier, and use parameterized statements to avoid injection and ensure consistency.
import (
"context"
"database/sql"
"fmt"
"path/filepath"
_ "github.com/lib/pq"
)
func storeFileMetadata(db *sql.DB, originalName, safeName string, fileSize int64) error {
// Use only the cleaned basename for storage
base := filepath.Base(safeName)
query := `INSERT INTO files (original_name, stored_name, size) VALUES ($1, $2, $3)`
_, err := db.ExecContext(context.Background(), query, originalName, base, fileSize)
if err != nil {
return fmt.Errorf("failed to store metadata: %w", err)
}
return nil
}
// Example table schema in Cockroachdb:
// CREATE TABLE files (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), original_name TEXT, stored_name TEXT, size BIGINT, created_at TIMESTAMPTZ DEFAULT now());
3. Serve files using validated paths from Cockroachdb
When retrieving files, do not directly use stored paths to build filesystem paths; instead, map the stored identifier to a safe location.
func serveFile(db *sql.DB, fileID string) (string, error) {
var storedName string
query := `SELECT stored_name FROM files WHERE id = $1`
if err := db.QueryRow(context.Background(), query, fileID).Scan(&storedName); err != nil {
return "", fmt.Errorf("file not found: %w", err)
}
// Resolve to a controlled directory
baseDir := "/safe/files"
safePath := filepath.Join(baseDir, filepath.Base(storedName))
if !strings.HasPrefix(safePath, filepath.Clean(baseDir)+string(os.PathSeparator)) {
return "", fmt.Errorf("invalid file path")
}
return safePath, nil
}
4. Use Buffalo middleware to reject malicious uploads
Add a request interceptor in middleware.go to inspect uploaded archives before processing.
func ValidateUpload(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Limit upload size and inspect multipart form if needed
r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // 10 MB
next.ServeHTTP(w, r)
})
}
By combining path validation, Cockroachdb metadata storage with safe identifiers, and controlled file serving, you eliminate Zip Slip risks in Buffalo applications while maintaining reliable data storage and retrieval.