Reject path traversal in the `transformers_weights` config field
Author: LinZiyuu
Pull request: yes

<!-- ci-dashboard-badge:start -->
[![CI](https://transformers-ci.lor-e.huggingface.cool/badge/pr?pr=46890)](https://transformers-ci.lor-e.huggingface.cool/d/pytest-observability-by-pr/pytest-observability-branch?var-pr=46890)
<!-- ci-dashboard-badge:end -->

`from_pretrained` resolves which checkpoint file to load via `_get_resolved_checkpoint_files`. When a model's `config.json` carries a `transformers_weights` field, that **untrusted** value is joined onto the model directory and opened after only a **suffix check** (it must end `.safetensors` / `.safetensors.index.json`, or equal `adapter_model.bin`) — with no `..` / absolute-path / containment validation (`modeling_utils.py:597`). Because `os.path.join` drops the prefix on an absolute component and `..` walks out of the directory, a malicious **local** model directory makes `AutoModel.from_pretrained(...)` open a `.safetensors`-format file **outside** the model directory (path traversal, CWE-22), with no `trust_remote_code`.

The escape base is `pretrained_model_name_or_path` — the directory the caller chose to load, not a config field — so the file is expected to live inside it; the same containment mistake was fixed on the write side for chat templates (#46191) and Bark voice presets (#46237), and this is the read side in the core loader. The read is constrained to `.safetensors`-format files (no arbitrary-text disclosure) and `weights_only=True` means no code execution, but a crafted local model can still read an out-of-directory checkpoint and acts as a file-existence oracle — in multi-tenant model hosting this can surface another tenant's weights.

The fix validates that the resolved `transformers_weights` path stays inside the model directory (rejecting `..` and absolute paths), leaving the sibling branches (which use hard-coded constants) untouched. A legitimate in-repo filename still loads.

AI assistance was used while drafting this change; I reviewed every line and ran the tests below.

# What does this PR do?

Adds a containment check so a `transformers_weights` value in `config.json` cannot make `from_pretrained` open a `.safetensors` file outside the model directory (path traversal, CWE-22, no `trust_remote_code`), matching the existing write-side traversal fixes (#46191, #46237).

**Tests run** (`torch==2.12.1`):

```
pytest tests/utils/test_modeling_utils.py -k test_transformers_weights_config_field_rejects_path_traversal
# 1 passed (an injected ../ or absolute transformers_weights is rejected;
#           a legitimate in-repo value still loads)
ruff check / ruff format --check    # clean
```

Reported via huntr: https://huntr.com/bounties/bdd08581-3537-4ca1-bd8c-de724529617a

Fixes # (security hardening)

## Before submitting
- [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
- [x] Did you read the [contributor guideline](https://github.com/huggingface/transformers/blob/main/CONTRIBUTING.md#create-a-pull-request), Pull Request section?
- [ ] Was this discussed/approved via a Github issue or the [forum](https://discuss.huggingface.co/)? Please add a link to it if that's the case.
- [ ] Did you make sure to update the documentation with your changes?
- [x] Did you write any new necessary tests?

## Who can review?

- model loading (from pretrained): @CyrilVallez

Comments:
Hey! In #46237 I think we used a different approach so it doesn't break when loading from snapshot dirs, right?

Good catch — you're right, thanks. I'd used os.path.realpath, which follows symlinks, so it would reject loads from a populated HF cache: the snapshot files are symlinks into the sibling blobs/ dir and resolve outside the snapshot dir. Confirmed that locally.

Switched to the same lexical approach as #46237. os.path.abspath + os.path.commonpath (no realpath/resolve, so symlinks aren't followed). It still blocks .. and absolute paths but leaves snapshot loads intact. Added a regression test that loads a symlinked snapshot-style weights file so it can't regress.

Looks good! One last point: Can we get rid of the big Claude comments? They can just be removed entirely, or reduced to a single line that explains we're catching path traversal bugs.

Also this touches core code so cc @cyrilvallez @vasqu if you're okay with it!

 Done — removed the comments. Let me know if anything else is needed!

The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/transformers/pr_46890). All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

<!-- ci-dashboard-recap:start -->

---

### CI recap

**Dashboard:** [View test results in Grafana](https://transformers-ci.lor-e.huggingface.cool/d/pytest-observability-by-pr/pytest-observability-branch?var-pr=46890)
**Latest run:** [28930699035:2](https://github.com/huggingface/transformers/actions/runs/28930699035)
**Result:** `success` | **Jobs:** 15 | **Tests:** 172,360 | **Failures:** 0 | **Duration:** 15h 34m

<!-- ci-dashboard-recap:end -->

Thanks for the review, @Cyrilvallez! Could you merge it when you have a moment? I don't have write access. Appreciate it!

Merging!