XTLS/Xray-core · error
asset path must stay in asset directory
Error message
asset path must stay in asset directory
What it means
Returned by getAssetFileLocation (backing ResolveAsset/StatAsset) when the requested relative path fails filepath.IsLocal or is exactly '.'. This is a path-traversal guard: asset reads must stay inside the platform asset directory, so absolute paths, '..' escapes, Windows drive letters, or empty/'.' inputs are rejected.
Source
Thrown at common/platform/filesystem/file.go:57
if err != nil {
return nil, err
}
return NewFileReader(path)
}
func StatAsset(file string) (os.FileInfo, error) {
_, info, err := getAssetFileLocation(file)
return info, err
}
func ResolveAsset(file string) (string, error) {
path, _, err := getAssetFileLocation(file)
return path, err
}
func getAssetFileLocation(file string) (string, os.FileInfo, error) {
if !filepath.IsLocal(file) || file == "." {
return "", nil, errors.New("asset path must stay in asset directory")
}
local, err := filepath.Localize(file)
if err != nil {
return "", nil, err
}
path := platform.GetAssetLocation(local)
info, err := os.Stat(path)
if err != nil {
return "", nil, err
}
if !info.Mode().IsRegular() {
return "", nil, errors.New("asset is not a regular file")
}
return path, info, nil
}
func ReadCert(file string) ([]byte, error) {
if filepath.IsAbs(file) {View on GitHub (pinned to 7d214f8b09)
Solutions
- Pass only a bare filename (e.g. 'geosite.dat'), not an absolute or parent-relative path
- Sanitize any user-controlled asset name with filepath.Base() before calling the API
- If the asset truly lives elsewhere, read it directly with filesystem.ReadFile instead of the asset resolver
Example fix
// before path, err := filesystem.ResolveAsset(userProvidedName) // "../secret.dat" // after path, err := filesystem.ResolveAsset(filepath.Base(userProvidedName))
Defensive patterns
Strategy: validation
Validate before calling
name := filepath.Base(userInput)
if !filepath.IsLocal(name) || name == "." || name == string(filepath.Separator) {
return errors.New("asset name must be a plain filename")
} Type guard
func isSafeAssetName(name string) bool { return filepath.IsLocal(name) && name != "." && filepath.Base(name) == name } Prevention
- Always sanitize external filenames with filepath.Base
- Treat this error as a security signal - log the offending path
When it happens
Trigger: Calling ResolveAsset/StatAsset with a path containing '..' (e.g. '../xray.geoip.dat'), an absolute path ('/etc/passwd'), a UNC/drive path on Windows, or the literal '.'.
Common situations: Geodata/rule asset filenames built from untrusted config input, config values written as absolute paths when the API expects a bare filename, or sanitization bugs that let user-supplied names traverse directories.
Related errors
- the source address is not from local machine.
- asset is not a regular file
- invalid geodata asset file:
- both file and bytes are empty.
- not a Service.
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/63291c1c25e0753b.
Report an issue: GitHub.