VYPR
Medium severity6.5NVD Advisory· Published Mar 26, 2026· Updated Apr 2, 2026

CVE-2026-33528

CVE-2026-33528

Description

GoDoxy is a reverse proxy and container orchestrator for self-hosters. Prior to version 0.27.5, the file content API endpoint at /api/v1/file/content is vulnerable to path traversal. The filename query parameter is passed directly to path.Join(common.ConfigBasePath, filename) where ConfigBasePath = "config" (a relative path). No sanitization or validation is applied beyond checking that the field is non-empty (binding:"required"). An authenticated attacker can use ../ sequences to read or write files outside the intended config/ directory, including TLS private keys, OAuth refresh tokens, and any file accessible to the container's UID. Version 0.27.5 fixes the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/yusing/godoxyGo
< 0.27.50.27.5

Affected products

1
  • cpe:2.3:a:godoxy:godoxy:*:*:*:*:*:go:*:*
    Range: <0.27.5

Patches

1
a541d75bb50f

fix(api/file): prevent path traversal in file API

https://github.com/yusing/godoxyyusingMar 19, 2026via ghsa
2 files changed · +77 1
  • internal/api/v1/file/get.go+9 1 modified
    @@ -1,6 +1,7 @@
     package fileapi
     
     import (
    +	"io"
     	"net/http"
     	"os"
     	"path"
    @@ -44,7 +45,14 @@ func Get(c *gin.Context) {
     		return
     	}
     
    -	content, err := os.ReadFile(request.FileType.GetPath(request.Filename))
    +	f, err := os.OpenInRoot(".", request.FileType.GetPath(request.Filename))
    +	if err != nil {
    +		c.Error(apitypes.InternalServerError(err, "failed to open root"))
    +		return
    +	}
    +	defer f.Close()
    +
    +	content, err := io.ReadAll(f)
     	if err != nil {
     		c.Error(apitypes.InternalServerError(err, "failed to read file"))
     		return
    
  • internal/api/v1/file/get_test.go+68 0 added
    @@ -0,0 +1,68 @@
    +package fileapi_test
    +
    +import (
    +	"net/http"
    +	"net/http/httptest"
    +	"net/url"
    +	"os"
    +	"testing"
    +
    +	"github.com/gin-gonic/gin"
    +	"github.com/stretchr/testify/assert"
    +	"github.com/stretchr/testify/require"
    +	api "github.com/yusing/godoxy/internal/api"
    +	fileapi "github.com/yusing/godoxy/internal/api/v1/file"
    +	"github.com/yusing/goutils/fs"
    +)
    +
    +func TestGet_PathTraversalBlocked(t *testing.T) {
    +	gin.SetMode(gin.TestMode)
    +
    +	files, err := fs.ListFiles("..", 1, false)
    +	require.NoError(t, err)
    +
    +	require.Greater(t, len(files), 0, "no files found")
    +
    +	relativePath := files[0]
    +
    +	fileContent, err := os.ReadFile(relativePath)
    +	require.NoError(t, err)
    +
    +	r := gin.New()
    +	r.Use(api.ErrorHandler())
    +	r.GET("/api/v1/file/content", fileapi.Get)
    +
    +	tests := []struct {
    +		name         string
    +		filename     string
    +		queryEscaped bool
    +	}{
    +		{
    +			name:     "dotdot_traversal",
    +			filename: relativePath,
    +		},
    +		{
    +			name:         "url_encoded_dotdot_traversal",
    +			filename:     relativePath,
    +			queryEscaped: true,
    +		},
    +	}
    +
    +	for _, tt := range tests {
    +		t.Run(tt.name, func(t *testing.T) {
    +			filename := tt.filename
    +			if tt.queryEscaped {
    +				filename = url.QueryEscape(filename)
    +			}
    +
    +			url := "/api/v1/file/content?type=config&filename=" + filename
    +			req := httptest.NewRequest(http.MethodGet, url, nil)
    +			w := httptest.NewRecorder()
    +			r.ServeHTTP(w, req)
    +
    +			// "Blocked" means we should never successfully read the outside file.
    +			assert.NotEqual(t, http.StatusOK, w.Code)
    +			assert.NotEqual(t, fileContent, w.Body.String())
    +		})
    +	}
    +}
    

Vulnerability mechanics

Generated by null/stub 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.