flipped-aurora/gin-vue-admin · error

path root is empty

Error message

path root is empty

What it means

JoinWithinRoot safely joins path elements under a root directory, guaranteeing the result stays below the root. It requires a non-empty root; an empty or whitespace-only root returns 'path root is empty' because no containment base exists.

Source

Thrown at server/utils/plugin_security.go:24

	"path/filepath"
	"regexp"
	"strings"
)

var pluginNamePattern = regexp.MustCompile(`^[a-z][a-z0-9_]*$`)

// ValidatePluginName restricts plugin names to lowercase ASCII Go identifiers.
func ValidatePluginName(name string) error {
	if !pluginNamePattern.MatchString(name) || token.IsKeyword(name) {
		return errors.New("invalid plugin name")
	}
	return nil
}

// JoinWithinRoot joins path elements while ensuring the result stays below root.
func JoinWithinRoot(root string, elems ...string) (string, error) {
	if strings.TrimSpace(root) == "" {
		return "", errors.New("path root is empty")
	}
	rootAbs, err := filepath.Abs(root)
	if err != nil {
		return "", errors.New("failed to resolve path root")
	}
	for _, elem := range elems {
		if filepath.IsAbs(elem) || filepath.VolumeName(elem) != "" || strings.HasPrefix(elem, "/") || strings.HasPrefix(elem, `\`) {
			return "", errors.New("path escapes root")
		}
	}
	parts := append([]string{rootAbs}, elems...)
	targetAbs, err := filepath.Abs(filepath.Join(parts...))
	if err != nil {
		return "", errors.New("failed to resolve target path")
	}
	rel, err := filepath.Rel(rootAbs, targetAbs)
	if err != nil || filepath.IsAbs(rel) || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
		return "", errors.New("path escapes root")

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Provide a concrete root directory before calling JoinWithinRoot (e.g. filepath.Join(baseDir, pluginRoot)).
  2. Set the missing config value / environment variable that supplies the root and reload config.
  3. Add an early check or default: if root == "" fall back to a known-safe base directory.

Example fix

// before
root := os.Getenv("PLUGIN_DIR") // ""
p, err := utils.JoinWithinRoot(root, name) // path root is empty
// after
root := os.Getenv("PLUGIN_DIR")
if root == "" {
    root = "./plugin"
}
p, err := utils.JoinWithinRoot(root, name) // ok
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(root) == "" {
    return errors.New("root directory must be configured before joining paths")
}

Try / catch

p, err := utils.JoinWithinRoot(root, elems...)
if err != nil {
    // empty root or failed resolution: fail the operation, never fall back to raw join
    return "", fmt.Errorf("safe join failed: %w", err)
}

Prevention

When it happens

Trigger: Calling JoinWithinRoot("", elem...) or with a root of only spaces/tabs — typically when the root comes from an unset config value or an empty variable.

Common situations: Config key for a plugin/base directory left blank in yaml; environment variable not set so the root resolves to empty string; refactored code passing an unassigned variable; caught in unit tests (TestJoinWithinRoot).

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/83affebd38df338a. Report an issue: GitHub.