Bound download expansion and escape control chars on the terminal (closes #164)
check / check (pull_request) Successful in 1m47s
check / check (push) Successful in 3m11s

Objects fetched from the store are untrusted; several decode paths let one expand or print without limit.

- blobgen.LimitReader errors past a byte cap (not io.LimitReader silent EOF). DecodeManifest reads through caps on both compressed input and decompressed output, far above any real manifest, so json.Decode cannot buffer a compressible bomb. FetchAndDecryptBlob bounds decompression to the blob recorded uncompressed_size (not the restoring host blob_size_limit).
- downloadSnapshotDB streams straight from storage to its temp file with io.Copy, replacing two ReadAll calls that held the whole database twice.
- FetchBlob drops the per-blob Stat round-trip, its expectedSize parameter and returned size, all of which only fed a debug log.
- TTYHandler and ui.Writer escape control characters in messages, attribute keys/values, and rendered identifiers/paths before colour codes are applied, so a crafted value cannot drive the terminal.

Model: opus-4-8
This commit was merged in pull request #197.
This commit is contained in:
2026-09-22 17:00:35 +02:00
parent 82c51a5337
commit 1244c9e48d
14 changed files with 431 additions and 87 deletions
+1 -1
View File
@@ -393,7 +393,7 @@ func (v *Vaultik) verifyBlob(
blobInfo snapshot.BlobInfo, db *sql.DB, identities []age.Identity,
) error {
// Download blob using shared fetch method
reader, _, err := v.FetchBlob(v.ctx, blobInfo.Hash, blobInfo.CompressedSize)
reader, err := v.FetchBlob(v.ctx, blobInfo.Hash)
if err != nil {
return fmt.Errorf("failed to download: %w", err)
}