Validate blob hashes, offsets and lengths from the destination (closes #155)
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 have a decrypted blob written outside the cache directory, or feed a short or negative value that panicked a command. blobDiskCache.path now refuses any key with a path separator, and ReadAt rejects a negative offset or length, bounding so a sum cannot overflow past the check. A new isBlobHash helper gates FetchBlob, shallow and deep verify, and restore: buildBlobIndexes rejects every hash from the snapshot database before any fetch. The blobs/ and metadata/ listings skip a non-conforming name, and short-hash prefixes in log and error text go through a panic-safe shortHash helper. Model: opus-4-8
This commit was merged in pull request #195.
This commit is contained in:
1 parent
d88ed64489
commit
82c51a5337
10 files changed
+549
-53
No files matched your search
@@ -0,0 +1,52 @@
|
||||
package vaultik
|
||||
|
||||
import "errors"
|
||||
|
||||
// blobHashHexLen is the length of a blob hash written as lowercase hex: a
|
||||
// SHA-256 digest is 32 bytes, so 64 characters. Remote snapshot keys are
|
||||
// SHA-256 hashes too and share this exact form.
|
||||
const blobHashHexLen = 64
|
||||
|
||||
// shortHashLen is how many leading characters of a hash appear in log and
|
||||
// error text.
|
||||
const shortHashLen = 16
|
||||
|
||||
// errInvalidBlobHash reports a value used as a blob hash that is not
|
||||
// exactly 64 lowercase hex characters. Restore, verify and prune read
|
||||
// these values back from the destination, which is not trusted, so each
|
||||
// one is checked before it is used to build a path or drive a read.
|
||||
var errInvalidBlobHash = errors.New(
|
||||
"blob hash is not 64 lowercase hex characters")
|
||||
|
||||
// isBlobHash reports whether s is exactly 64 lowercase hex characters.
|
||||
// Every real blob hash and remote snapshot key has this form.
|
||||
//
|
||||
// The check is a plain function, not a method on types.BlobHash: the
|
||||
// packer stores "temp-placeholder-{uuid}" as the hash of an unfinished
|
||||
// blob in the local index, so the type itself must keep accepting values
|
||||
// that are not hashes.
|
||||
func isBlobHash(s string) bool {
|
||||
if len(s) != blobHashHexLen {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, r := range s {
|
||||
if (r < '0' || r > '9') && (r < 'a' || r > 'f') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// shortHash returns the leading part of a hash for log and error text. It
|
||||
// never panics: a string shorter than the prefix is returned whole. A hash
|
||||
// read from the destination may be malformed, and formatting one for a
|
||||
// message must not crash the command.
|
||||
func shortHash(s string) string {
|
||||
if len(s) <= shortHashLen {
|
||||
return s
|
||||
}
|
||||
|
||||
return s[:shortHashLen]
|
||||
}
|
||||
Reference in new issue
Block a user