{"record":{"id":"43d9906d2fcc9fdd","repo":"OpenNHP/opennhp","slug":"invalid-resource-id-potential-path-traversal-atta","errorCode":null,"errorMessage":"invalid resource ID: potential path traversal attack","messagePattern":"invalid resource ID: potential path traversal attack","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"endpoints/server/kbs/resource/resource.go","lineNumber":173,"sourceCode":"\tc.JSON(http.StatusOK, response)\n}\n\nfunc loadResource(resourceID string) ([]byte, error) {\n\tabsBaseDir, err := filepath.Abs(baseDir)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fail to get base directory absolute path: %w\", err)\n\t}\n\n\tfullPath := filepath.Join(absBaseDir, resourceID)\n\n\tabsFullPath, err := filepath.Abs(fullPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fail to get resource absolute path: %w\", err)\n\t}\n\n\t// Check if the path is within the base directory to avoid path traversal attack.\n\tif !strings.HasPrefix(absFullPath, absBaseDir) {\n\t\treturn nil, errors.New(\"invalid resource ID: potential path traversal attack\")\n\t}\n\n\tif _, statErr := os.Stat(absFullPath); statErr != nil {\n\t\tif os.IsNotExist(statErr) {\n\t\t\treturn nil, errors.New(\"resource not found\")\n\t\t}\n\t\treturn nil, fmt.Errorf(\"fail to check resource: %w\", statErr)\n\t}\n\n\tdata, err := os.ReadFile(absFullPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fail to read resource: %w\", err)\n\t}\n\treturn data, nil\n}\n\nfunc encryptWithA256GCM(key, plaintext []byte) (ciphertext, iv, tag []byte, err error) {\n\tblock, err := aes.NewCipher(key)","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/kbs/resource/resource.go#L155-L191","documentation":"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.","triggerScenarios":"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.","commonSituations":"Malicious or buggy clients probing the KBS repository API; misconfigured repository names embedding slashes or dots; symlinked files inside the resource directory pointing outside.","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"],"exampleFix":"// before\nGetResource(token, \"default/../../etc/passwd\")\n// after\nGetResource(token, \"default/repo/key\")","handlingStrategy":"validation","validationCode":"resourceID := \"default/repo/key\"\nif strings.Contains(resourceID, \"..\") || strings.HasPrefix(resourceID, \"/\") {\n    return errors.New(\"refusing to request resource ID outside repository\")\n}","typeGuard":"func isSafeResourceID(id string) bool {\n    return id != \"\" && !strings.Contains(id, \"..\") && !strings.HasPrefix(id, \"/\") && filepath.Base(id) != \".\"\n}","tryCatchPattern":"res, err := GetResource(token, id)\nif err != nil {\n    if strings.Contains(err.Error(), \"path traversal\") {\n        log.Errorf(\"sanitize resource ID %q: %v\", id, err)\n    }\n    return err\n}","preventionTips":["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"],"tags":["go","security","path-traversal"],"backgroundTag":"path-traversal-blocked","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}