{"record":{"id":"e692b6d54870f3e3","repo":"Tencent/WeKnora","slug":"invalid-file-path-w","errorCode":null,"errorMessage":"invalid file path: %w","messagePattern":"invalid file path: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/ks3.go","lineNumber":223,"sourceCode":"\t\tSourceBucket: ks3aws.String(srcBucket),\n\t\tSourceKey:    ks3aws.String(srcKey),\n\t})\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to copy file in KS3: %w\", err)\n\t}\n\n\tnewPath := fmt.Sprintf(\"%s%s/%s\", ks3Scheme, s.bucketName, destKey)\n\tlogger.Infof(ctx, \"Copied KS3 object %s to %s\", srcPath, newPath)\n\treturn newPath, nil\n}\n\nfunc (s *ks3FileService) GetFile(ctx context.Context, filePath string) (io.ReadCloser, error) {\n\t_, objectKey, err := parseKS3FilePath(filePath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := utils.SafeObjectKey(objectKey); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid file path: %w\", err)\n\t}\n\n\tresp, err := s.client.GetObject(&ks3s3.GetObjectInput{\n\t\tBucket: ks3aws.String(s.bucketName),\n\t\tKey:    ks3aws.String(objectKey),\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get file from KS3: %w\", err)\n\t}\n\n\treturn resp.Body, nil\n}\n\nfunc (s *ks3FileService) DeleteFile(ctx context.Context, filePath string) error {\n\t_, objectKey, err := parseKS3FilePath(filePath)\n\tif err != nil {\n\t\treturn err\n\t}","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/ks3.go#L205-L241","documentation":"ks3FileService.GetFile wraps SafeObjectKey failures with \"invalid file path: %w\" before fetching the object. SafeObjectKey rejects object keys that are empty or contain \"..\" (path traversal). The ks3:// path must already have parsed successfully, so this error means the key inside a well-formed ks3://bucket/key path failed the security sanitization check.","triggerScenarios":"Calling GetFile with a ks3:// path whose object key is empty or contains \"..\", e.g. \"ks3://mybucket/a/../secret\" or \"ks3://mybucket/\". Typically comes from tampered or hand-constructed path strings persisted in the DB.","commonSituations":"Imported knowledge-base records with attacker-influenced file paths; tests injecting traversal keys; migrating records from another backend where keys contained dot-dot segments; string concatenation bugs producing keys like \"tenant/1/../2/file\".","solutions":["Remove any \"..\" segments from the object key; keys are joined by joinKS3Key from pathPrefix/tenantID/knowledgeID/uuid, so store and pass only the path returned by SaveFile/SaveBytes.","Verify the stored file path starts with \"ks3://\" and has both bucket and non-empty key components (parseKS3FilePath already passed, so check the key only).","If paths came from an old schema, run a data cleanup that strips or rejects keys containing \"..\" before calling GetFile.","Log the offending filePath and re-upload the file with SaveFile to get a canonical safe key."],"exampleFix":"// before\nrc, err := svc.GetFile(ctx, \"ks3://bucket/tenant/1/../1/doc.pdf\")\n// after\nrc, err := svc.GetFile(ctx, \"ks3://bucket/prefix/1/knowledge-id/550e8400-e29b-41d4.pdf\")","handlingStrategy":"validation","validationCode":"func validKS3Path(p string) bool {\n\tif !strings.HasPrefix(p, \"ks3://\") { return false }\n\trest := strings.TrimPrefix(p, \"ks3://\")\n\tparts := strings.SplitN(rest, \"/\", 2)\n\treturn len(parts) == 2 && parts[0] != \"\" && parts[1] != \"\" && !strings.Contains(parts[1], \"..\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only pass paths previously returned by SaveFile/SaveBytes/CopyFile","Reject client-supplied file paths containing \"..\" at the API boundary","Never build ks3:// strings by concatenating user input"],"tags":["ks3","object-storage","path-traversal","input-validation","go"],"backgroundTag":"object-key-path-traversal","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}