@@ -44,6 +44,10 @@ const shardFlushRows = 1024
4444
4545var maxShardBytes int64 = 40 * 1024 * 1024
4646
47+ // The share manifest stores compressed media size, not raw size. Keep gzip
48+ // restore/hash paths bounded so a malformed snapshot cannot expand forever.
49+ var maxSharedMediaDecompressedBytes int64 = 1 << 30
50+
4751var SnapshotTables = []string {
4852 "guilds" ,
4953 "channels" ,
@@ -1238,8 +1242,7 @@ func restoreGzipFile(target, source string) error {
12381242 }
12391243 defer func () { _ = gz .Close () }()
12401244 return writeAtomicFile (target , func (tmp * os.File ) error {
1241- _ , err := io .Copy (tmp , gz )
1242- return err
1245+ return copyWithLimit (tmp , gz , maxSharedMediaDecompressedBytes )
12431246 })
12441247}
12451248
@@ -1268,12 +1271,26 @@ func gzipFileSHA256(path string) (string, error) {
12681271 }
12691272 defer func () { _ = gz .Close () }()
12701273 hasher := sha256 .New ()
1271- if _ , err := io . Copy (hasher , gz ); err != nil {
1274+ if err := copyWithLimit (hasher , gz , maxSharedMediaDecompressedBytes ); err != nil {
12721275 return "" , err
12731276 }
12741277 return hex .EncodeToString (hasher .Sum (nil )), nil
12751278}
12761279
1280+ func copyWithLimit (dst io.Writer , src io.Reader , limit int64 ) error {
1281+ if limit <= 0 {
1282+ return errors .New ("media decompression limit must be positive" )
1283+ }
1284+ n , err := io .Copy (dst , io .LimitReader (src , limit + 1 ))
1285+ if err != nil {
1286+ return err
1287+ }
1288+ if n > limit {
1289+ return fmt .Errorf ("media decompressed size exceeds %d bytes" , limit )
1290+ }
1291+ return nil
1292+ }
1293+
12771294func sameFileHash (path , hash string ) bool {
12781295 current , err := fileSHA256 (path )
12791296 return err == nil && current == hash
0 commit comments