{"record":{"id":"55931fff2e6fcbdd","repo":"larksuite/cli","slug":"resolve-save-path-w","errorCode":null,"errorMessage":"resolve save path: %w","messagePattern":"resolve save path: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/common/runner.go","lineNumber":655,"sourceCode":"\t\tif ctx != nil {\n\t\t\tc = ctx.ctx\n\t\t}\n\t\treturn p.ResolveFileIO(c)\n\t}\n\treturn nil\n}\n\n// ResolveSavePath resolves a relative path to a validated absolute path via\n// FileIO.ResolvePath. It returns an error if no FileIO provider is registered\n// or if the path fails validation (e.g. traversal, symlink escape).\nfunc (ctx *RuntimeContext) ResolveSavePath(path string) (string, error) {\n\tfio := ctx.FileIO()\n\tif fio == nil {\n\t\treturn \"\", fmt.Errorf(\"no file I/O provider registered\")\n\t}\n\tresolved, err := fio.ResolvePath(path)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"resolve save path: %w\", err)\n\t}\n\tif resolved == \"\" {\n\t\treturn \"\", fmt.Errorf(\"resolve save path: empty result for %q\", path)\n\t}\n\treturn resolved, nil\n}\n\n// WrapOpenError matches a FileIO.Open/Stat error and wraps it with the\n// caller-provided message prefix.\nfunc WrapOpenError(err error, pathMsg, readMsg string) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif errors.Is(err, fileio.ErrPathValidation) {\n\t\treturn fmt.Errorf(\"%s: %w\", pathMsg, err)\n\t}\n\treturn fmt.Errorf(\"%s: %w\", readMsg, err)\n}","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/common/runner.go#L637-L673","documentation":"Wraps a validation failure from FileIO.ResolvePath while resolving the save path. The provider rejected the path — typical causes are path traversal, symlink escape, or otherwise invalid target — and the cause is preserved via %w. It guards that saves stay inside the validated workspace.","triggerScenarios":"ctx.ResolveSavePath(\"../x\") or any path failing ResolvePath validation: \"..\" escaping the base dir, symlinked paths resolving outside allowed roots, absolute paths in restricted modes, illegal characters.","commonSituations":"Users passing relative paths with \"..\" from a different working directory; saving into symlinked folders pointing outside the workspace; paths referencing other users' directories in shared setups.","solutions":["Fix the input path so it resolves inside the allowed save root (remove \"..\" or symlink hops)","Inspect the wrapped cause to see which validation failed and adjust accordingly","Use runtime.ValidatePath() on candidate paths ahead of time, or let ResolveSavePath normalize by passing a plain relative filename"],"exampleFix":"// before\np, err := ctx.ResolveSavePath(\"../../etc/out.json\")\n// after\np, err := ctx.ResolveSavePath(\"out.json\") // resolves inside validated save root","handlingStrategy":"validation","validationCode":"// Reject obviously escaping paths before calling:\nif strings.Contains(filepath.ToSlash(path), \"..\") {\n\treturn fmt.Errorf(\"path must stay inside the save directory\")\n}\nout, err := ctx.ResolveSavePath(path)","typeGuard":null,"tryCatchPattern":"out, err := ctx.ResolveSavePath(path)\nif err != nil {\n\tvar ve *errs.ValidationError\n\tif errors.As(errors.Unwrap(err), &ve) {\n\t\treturn fmt.Errorf(\"invalid save path %q: %w\", path, err)\n\t}\n\treturn err\n}","preventionTips":["Pass plain relative filenames and let ResolveSavePath normalize them","Avoid symlinks pointing outside the workspace when choosing save locations","Pre-validate candidate paths with runtime.ValidatePath()"],"tags":["go","path-validation","file-io","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}