MCPcopy Create free account
hub / github.com/diillson/chatcli / saveFetchToScratch

Function saveFetchToScratch

cli/plugins/builtin_webfetch.go:219–246  ·  view source on GitHub ↗

saveFetchToScratch writes fullContent to the session scratch dir when parsed.SaveToFile is set, returning the absolute path written (empty when no save was requested). All writes are confined to the scratch dir: only the base name of the caller-supplied save_path is honored, and the resolved path is

(parsed fetchArgs, fullContent string)

Source from the content-addressed store, hash-verified

217// only the base name of the caller-supplied save_path is honored, and the
218// resolved path is re-checked against scratch as defense-in-depth.
219func saveFetchToScratch(parsed fetchArgs, fullContent string) (string, error) {
220 if !parsed.SaveToFile {
221 return "", nil
222 }
223 scratch := os.Getenv("CHATCLI_AGENT_TMPDIR")
224 if scratch == "" {
225 scratch = os.TempDir()
226 }
227 // Take only the base name so we can't be talked into writing /etc/passwd
228 // via an absolute path — matches gosec G703 guidance and how the coder
229 // engine validates agent paths.
230 baseName := filepath.Base(strings.TrimSpace(parsed.SavePath))
231 if baseName == "" || baseName == "." || baseName == string(filepath.Separator) {
232 baseName = fmt.Sprintf("webfetch_%d.txt", time.Now().UnixNano())
233 }
234 // Clean collapses any surviving ../ segments introduced by exotic
235 // basenames on platforms where Base keeps them.
236 cleaned := filepath.Clean(filepath.Join(scratch, baseName))
237 absScratch, _ := filepath.Abs(scratch)
238 absCleaned, _ := filepath.Abs(cleaned)
239 if !strings.HasPrefix(absCleaned, absScratch+string(filepath.Separator)) && absCleaned != absScratch {
240 return "", fmt.Errorf("save_path %q escapes the session scratch directory", parsed.SavePath)
241 }
242 if err := os.WriteFile(cleaned, []byte(fullContent), 0o600); err != nil { //nolint:gosec // path confined to scratch dir above
243 return "", fmt.Errorf("saving response to %s: %w", cleaned, err)
244 }
245 return cleaned, nil
246}
247
248// buildFetchOutput assembles the final string returned to the agent:
249// applies the auto-save preview cap and max_length truncation, then

Callers 2

TestSaveFetchToScratchFunction · 0.85
ExecuteWithStreamMethod · 0.85

Calls 1

ErrorfMethod · 0.80

Tested by 1

TestSaveFetchToScratchFunction · 0.68