Validate archive entries and stage inside the pack root
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful

- Reject archive members that are absolute, escape via .., are special files, or link outside the destination, before anything is written

- Stage under the pack root so the swap is a same-filesystem rename, with an EXDEV copy fallback; propagate registry auth errors and validate dir_name
This commit is contained in:
2026-09-05 16:32:47 +10:00
parent ae951a9ea6
commit ca55c9dd9f
14 changed files with 544 additions and 35 deletions
+15 -4
View File
@@ -136,10 +136,17 @@ the UI stays responsive.
1. Resolve `(name, version)` to a URL and headers.
2. `curl` the archive into `stdpath('cache')/vimpack/archives/`.
3. Verify `sha256` when the spec carries one; a mismatch aborts with expected and got.
4. Extract into `stdpath('cache')/vimpack/stage/`, hoisting a single wrapper directory.
5. Rename into `stdpath('data')/site/pack/vimpack/{start,opt}/<name>`, parking the old
tree aside first and restoring it if the rename fails.
6. Record name, version, sha256 and bucket in `stdpath('data')/vimpack/manifest.json`.
4. List every archive member and reject the archive outright if any entry is absolute,
escapes the destination via `..`, is a device or fifo, or is a link pointing outside
the destination. Nothing is written until the whole listing is clean.
5. Extract into `<pack root>/.vimpack-stage/`, hoisting a single wrapper directory.
Staging lives under the pack root so the swap below is a same-filesystem rename;
`stdpath('cache')` and `stdpath('data')` are frequently separate mounts.
6. Rename into `stdpath('data')/site/pack/vimpack/{start,opt}/<name>`, parking the old
tree aside first and restoring it if the rename fails. Should the two ever land on
different filesystems anyway, the staged tree is copied beside the target and that
copy is renamed into place, so the target is never half-written.
7. Record name, version, sha256 and bucket in `stdpath('data')/vimpack/manifest.json`.
A failure at any step leaves the previous install untouched.
@@ -148,6 +155,10 @@ A failure at any step leaves the previous install untouched.
Lazy loading. `opt = true` places a plugin under `opt/` for `:packadd`; there is no
event, filetype or command trigger machinery.
Concurrent Neovim instances. The manifest is rewritten wholesale at the end of a run, so
two `:VimpackInstall` runs overlapping across processes are last-writer-wins and one set
of manifest updates is lost; the installed trees themselves stay consistent.
## Tests
```sh