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
- Provide a concrete root directory before calling JoinWithinRoot (e.g. filepath.Join(baseDir, pluginRoot)).
- Set the missing config value / environment variable that supplies the root and reload config.
- 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
- Give plugin/base directories a non-empty default in config and validate at startup
- Fail fast on missing environment variables that feed root paths
- Prefer JoinWithinRoot over filepath.Join for any path built from user/plugin-supplied names
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.