Safe-by-default model loading
Safe-by-default model loading
Problem
Loading a saved model through the public load() API can run arbitrary code if the checkpoint comes from an untrusted source (shared file, download, etc.). That is the usual pickle/torch unpickling risk, and it affects the normal "save and load a trained model" workflow.
Two places in the codebase:
TorchForecastingModel(darts/models/forecasting/torch_forecasting_model.py, around lines 2388, 2488, 2629): callstorch.load(..., weights_only=False), which turns off PyTorch 2.6+'s safer default.ForecastingModel.load()(darts/models/forecasting/forecasting_model.py, around line 2737): uses rawpickle.loadfor non-torch models.
Both are reachable through the documented API with no extra flags.
What we need from a fix
- Safe for the common case without asking users to configure trust lists.
- Still works with the third-party backends we support (they store things differently, and not all handle save/load themselves).
- Keeps current behavior where possible: creation params (including stateful ones), model attributes, etc. If something has to change, we should call it out explicitly.
Proposed plan
Phase 1: TorchForecastingModel
- Use
torch.load(..., weights_only=True). - Register the classes we actually serialize via
torch.serialization.add_safe_globals([...])(darts types, PyTorch Lightning, whatever the checkpoint needs). - Store model weights, ... as safetensors?
- Drop the global
torch.loadmonkeypatch if we still have it.
This should be a standalone PR and fixes the torch path without changing normal usage.
Phase 2: ForecastingModel
- Replace raw
pickle.loadwith a safer unpickler (for examplefind_classallowlist, ...). - Start with what we know we store:
darts.*, numpy/pandas, sklearn/statsmodels basics, stdlib types we rely on. - Reject obvious bad imports (
os,subprocess,eval, etc.).
For saves that contain something outside the allowlist (custom estimator, third-party stateful object in creation params), fail with a clear message and let the user opt in, e.g. ForecastingModel.load(path, trusted=True). Optional: trusted_classes=[...] for power users.
Suggested order
- PR for torch loading.
- PR for general ForecastingModel loading (iterate on the allowlist with real saved models).
- Docs on new save / load behavior, explain when
trusted,trusted_classesare needed and how to fix failing loading.
Source: unit8co/darts