OpenNHP/opennhp · error
invalid resource ID: potential path traversal attack
Error message
invalid resource ID: potential path traversal attack
What it means
loadResource resolves the requested resource ID into an absolute path under a fixed base directory; if the resolved path does not start with the base dir prefix, it is treated as a path traversal attempt (e.g. ../ or absolute paths outside the resource root) and rejected for security.
Solutions
- Use a canonical resource ID without '..', leading slashes, or backslashes
- Recreate any symlinks in the resource dir so targets live inside the base directory
- URL-decode and normalize IDs client-side before requesting
- If a legit layout trips this, relocate resources under the configured base dir rather than bypassing the check
Example fix
// before GetResource(token, "default/../../etc/passwd") // after GetResource(token, "default/repo/key")
Defensive patterns
Strategy: validation
Validate before calling
resourceID := "default/repo/key"
if strings.Contains(resourceID, "..") || strings.HasPrefix(resourceID, "/") {
return errors.New("refusing to request resource ID outside repository")
} Type guard
func isSafeResourceID(id string) bool {
return id != "" && !strings.Contains(id, "..") && !strings.HasPrefix(id, "/") && filepath.Base(id) != "."
} Try / catch
res, err := GetResource(token, id)
if err != nil {
if strings.Contains(err.Error(), "path traversal") {
log.Errorf("sanitize resource ID %q: %v", id, err)
}
return err
} Prevention
- Normalize and reject '..' client-side before sending requests
- Never build resource IDs from raw user input without sanitization
- Keep repository files physically inside the base dir; avoid outbound symlinks
When it happens
Trigger: GetResource is called with a resource ID containing '..' segments, leading '/', URL-encoded traversal (%2e%2e), or symlinks causing the absolute path to escape the base directory.
Common situations: Malicious or buggy clients probing the KBS repository API; misconfigured repository names embedding slashes or dots; symlinked files inside the resource directory pointing outside.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- invalid file name
- unknown remote provider
- unknown remote provider
- unsupported key type, expect RSA
- JWT signing key is not initialized
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/43d9906d2fcc9fdd.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/server/kbs/resource/resource.go:173
c.JSON(http.StatusOK, response)
}
func loadResource(resourceID string) ([]byte, error) {
absBaseDir, err := filepath.Abs(baseDir)
if err != nil {
return nil, fmt.Errorf("fail to get base directory absolute path: %w", err)
}
fullPath := filepath.Join(absBaseDir, resourceID)
absFullPath, err := filepath.Abs(fullPath)
if err != nil {
return nil, fmt.Errorf("fail to get resource absolute path: %w", err)
}
// Check if the path is within the base directory to avoid path traversal attack.
if !strings.HasPrefix(absFullPath, absBaseDir) {
return nil, errors.New("invalid resource ID: potential path traversal attack")
}
if _, statErr := os.Stat(absFullPath); statErr != nil {
if os.IsNotExist(statErr) {
return nil, errors.New("resource not found")
}
return nil, fmt.Errorf("fail to check resource: %w", statErr)
}
data, err := os.ReadFile(absFullPath)
if err != nil {
return nil, fmt.Errorf("fail to read resource: %w", err)
}
return data, nil
}
func encryptWithA256GCM(key, plaintext []byte) (ciphertext, iv, tag []byte, err error) {
block, err := aes.NewCipher(key)View on GitHub (pinned to 6e04ca5ff0)