The packaging update recipes interpolate versions and hashes into sed patterns unescaped
- Dominant language
- Rust
- Stars
- 407
- Forks
- 12
- Avg merge
- 5h 45m
- Merged PRs (30d)
- 30
Description
Every `just update-*` recipe rewrites its manifest with `sed`, and every one of
them drops a value straight into the pattern half of an `s///`:
| `justfile` | pattern |
| --- | --- |
| 45, 66, 81, 145 | `s/$prev_hash/$new_hash/` — homebrew, scoop, winget, flake |
| 59, 74, 129, 159 | `s/{{ prev_version }}/{{ version }}/` — the same four |
A version is a BRE there, not a literal, so `0.3.1` means "`0`, any character,
`3`, any character, `1`". The characters it can reach beyond the version pin it
is meant to rewrite are the ones the same recipe has just written: a hexadecimal
SHA256 is 64 characters of exactly the alphabet those wildcards match, and the
hash substitution runs first.
Nothing is known to have been corrupted by this. Reaching a hash needs a version
whose dots line up with hex digits in a checksum the release happens to have,
and the hash substitution having already put that checksum there. The point is
that whether a release corrupts its own manifest is left to what its checksums
happen to spell.
`$prev_hash` and `$new_hash` are hex and cannot themselves contain a
metacharacter, so those two are only a problem in the other direction — an
unquoted expansion that would break on an unexpected value rather than one that
matches too much.
## What a fix looks like
Match literally. `sed` has no fixed-string mode, so either escape the dots
before interpolating, or anchor the pattern to the line it belongs on
(`^ version "0\.3\.1"` and friends), which also stops a version from being
rewritten anywhere the recipe did not mean.
`winget-release-date` in #415 avoids the class for the check it adds, by
comparing `PackageVersion` as a string rather than matching a pattern. The
substitutions are untouched and predate that PR.
Contributor guide
Assessment
This issue has not been assessed yet.