package app import ( "fmt" "io" "os" "path/filepath" ) // readFileLimited reads path into memory, refusing files larger than maxBytes. // Prevents OOM on corrupted or unexpectedly large data files. func readFileLimited(path string, maxBytes int64) ([]byte, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() data, err := io.ReadAll(io.LimitReader(f, maxBytes+1)) if err != nil { return nil, err } if int64(len(data)) > maxBytes { return nil, fmt.Errorf("file %s too large (exceeds %d bytes)", path, maxBytes) } return data, nil } func atomicWriteFile(path string, data []byte, perm os.FileMode) error { if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return fmt.Errorf("mkdir %s: %w", filepath.Dir(path), err) } tmpPath := path + ".tmp" f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, perm) if err != nil { return fmt.Errorf("open temp %s: %w", tmpPath, err) } success := false defer func() { _ = f.Close() if !success { _ = os.Remove(tmpPath) } }() if _, err := f.Write(data); err != nil { return fmt.Errorf("write temp %s: %w", tmpPath, err) } if err := f.Sync(); err != nil { return fmt.Errorf("sync temp %s: %w", tmpPath, err) } if err := f.Close(); err != nil { return fmt.Errorf("close temp %s: %w", tmpPath, err) } if err := os.Rename(tmpPath, path); err != nil { return fmt.Errorf("rename %s -> %s: %w", tmpPath, path, err) } if dir, err := os.Open(filepath.Dir(path)); err == nil { _ = dir.Sync() _ = dir.Close() } success = true return nil }