Validate blob hashes, offsets and lengths from the destination (closes #155)
check / check (pull_request) Successful in 1m23s

A blob hash read back from the downloaded snapshot database or the store
listing was trusted unchecked. A hostile remote could set a hash such as
"aa/../../etc" and make a decrypted blob be written outside the cache
directory, or feed a short or negative value that panicked a command.

blobDiskCache.path now refuses any key containing a path separator, and
ReadAt rejects a negative offset or length, bounding with
length > size-offset so a sum cannot overflow past the check. A new
isBlobHash helper (a plain function, not a method, since the packer stores
temp-placeholder-{uuid} as a hash) gates FetchBlob, shallow and deep
verify; the blobs/ and metadata/ listings skip a non-conforming name with
a warning; and short-hash prefixes in log and error text go through a
shortHash helper that cannot panic. verify's chunk reader rejects a
negative blob_chunks length and streams the chunk rather than allocating a
database-supplied size.

restore.go and internal/database are left untouched to avoid colliding
with the in-flight issue #156 work; the cache-path and FetchBlob guards
already stop the unsafe write and fetch.

Model: opus-4-8
This commit is contained in:
2026-09-22 13:00:41 +00:00
parent 7e611b95db
commit 075c5e9733
9 changed files with 492 additions and 51 deletions
+23
View File
@@ -25,6 +25,29 @@ release" is exactly the contradiction
# Completed Steps
- 2026-09-22: Validated blob hashes, offsets and lengths read back from
the destination before using them
([issue #155](https://git.eeqj.de/sneak/vaultik/issues/155)). A blob
hash taken from the downloaded database or the store listing was
trusted unchecked, so a hostile remote could drive a decrypted blob to
be written outside the cache directory (a hash like `aa/../../etc`) or
crash a command with a short or negative value. `blobDiskCache.path`
now refuses any key containing a path separator, and `ReadAt` rejects a
negative offset or length and bounds with `length > size-offset` so a
sum cannot overflow past the check. A new `isBlobHash` helper (a plain
function, not a method — the packer stores `temp-placeholder-{uuid}` as
a hash) gates `FetchBlob`, shallow and deep verify; the `blobs/` and
`metadata/` listings skip a non-conforming name with a warning; and
short-hash prefixes in log and error text go through a `shortHash`
helper that cannot panic. `verify`'s chunk reader also rejects a
negative `blob_chunks` length and streams the chunk instead of
allocating a database-supplied size. `restore.go` and
`internal/database` were left untouched to avoid colliding with the
in-flight [issue #156](https://git.eeqj.de/sneak/vaultik/issues/156)
work; the cache-path and `FetchBlob` guards already stop the unsafe
write and fetch, so restore's own `buildBlobIndexes` early check is
deferred as fail-fast defense in depth.
- 2026-09-21: Stopped an interrupted blob upload from making a later
backup deduplicate against data that was never stored
([issue #148](https://git.eeqj.de/sneak/vaultik/issues/148)). The