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

  1. Use a canonical resource ID without '..', leading slashes, or backslashes
  2. Recreate any symlinks in the resource dir so targets live inside the base directory
  3. URL-decode and normalize IDs client-side before requesting
  4. 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

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


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)