tessel 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fcc9cfc07acd3c42b52031c1b3bfe996985fdd8c7d65dd26695c3f17160f48c7
4
- data.tar.gz: 2b3ace72f2d8d8e6dea5693ee21fa22ff6d69d5751442c7aea0a4e9a4a917f16
3
+ metadata.gz: 57081a7b90e42e245d8ef8122d836932f1064a7d3a77b6215572e686c2df4bb0
4
+ data.tar.gz: 46d5bfddf74bab962b5a6214f2c9d2ccc97ccc5c8343de81fa319c8f652f107e
5
5
  SHA512:
6
- metadata.gz: c200a0eac8eeb61aa8001896113b166eb6b7452d67e7e037cb80e115c02024067640d6cdb7cd1f700f656223f4bc4c3c5d18bcec77e52b57f4fc7a834977bce7
7
- data.tar.gz: 80798691e8c58e53314305dd9c7e4be06b889dbcf6899ff04d5f83b8297edcc98edf56bdacbcdbf53603e217215feab4b327bd2543ce0388ed4592f4a4d93960
6
+ metadata.gz: 8d8ebb312c5b64c97ecf15633adabf33ad64d4bddd225541002e6a4194cf2bc0da786c6b668c3ace818e493eb7216d88f0bd9ab09fbca3e30227b8dd754197ac
7
+ data.tar.gz: 1a805ae47035578ffdc854e39185fda0f025bde5b9169166a035f38bd406006fac6eb815911cca511e0081664cd8979af0b65315b49583ddc853e244184f0303
data/.codespellignore ADDED
@@ -0,0 +1 @@
1
+ paeth
data/.yamllint ADDED
@@ -0,0 +1,16 @@
1
+ extends: default
2
+ ignore: |
3
+ build/
4
+ node_modules/
5
+ runtime/build/
6
+ tmp/
7
+ **/*.dSYM/**
8
+
9
+ rules:
10
+ comments:
11
+ min-spaces-from-content: 1
12
+ document-start: disable
13
+ line-length:
14
+ max: 200
15
+ truthy:
16
+ check-keys: false
data/CHANGELOG.md CHANGED
@@ -2,11 +2,14 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.2.1] - 2026-09-26
6
+
7
+ - Limit decoded PNG text metadata to prevent excessive memory use.
8
+
5
9
  ## [0.2.0] - 2026-09-25
6
10
 
7
11
  - Add shared median-cut quantization and indexed color dithering.
8
12
  - Add fixed 256-color and web-safe palettes.
9
- - Update gem author metadata.
10
13
 
11
14
  ## [0.1.0] - 2026-09-23
12
15
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tessel
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/tessel.rb CHANGED
@@ -422,10 +422,12 @@ module Tessel
422
422
  module_function
423
423
 
424
424
  ADAM7 = [[0, 0, 8, 8], [4, 0, 8, 8], [0, 4, 4, 8], [2, 0, 4, 4], [0, 2, 2, 4], [1, 0, 2, 2], [0, 1, 1, 2]].freeze
425
+ MAX_METADATA_BYTES = 1_048_576
425
426
 
426
- def decode(bytes, max_pixels: 16384 * 16384, verify_crc: true)
427
+ def decode(bytes, max_pixels: 16384 * 16384, max_metadata_bytes: MAX_METADATA_BYTES, verify_crc: true)
427
428
  bytes = String(bytes).b
428
429
  raise DecodeError, "invalid PNG signature" unless bytes.start_with?(SIGNATURE)
430
+ raise ArgumentError, "max_metadata_bytes must be non-negative" unless max_metadata_bytes.is_a?(Integer) && !max_metadata_bytes.negative?
429
431
 
430
432
  chunks = []
431
433
  offset = SIGNATURE.bytesize
@@ -472,12 +474,12 @@ module Tessel
472
474
  idat_ended = true
473
475
  end
474
476
  end
475
- parse(chunks, max_pixels: max_pixels)
477
+ parse(chunks, max_pixels: max_pixels, max_metadata_bytes: max_metadata_bytes)
476
478
  rescue Zlib::Error => e
477
479
  raise DecodeError, "invalid PNG data: #{e.message}"
478
480
  end
479
481
 
480
- def parse(chunks, max_pixels:)
482
+ def parse(chunks, max_pixels:, max_metadata_bytes:)
481
483
  raise DecodeError, "PNG is missing IHDR" unless chunks.first&.first == "IHDR"
482
484
  raise DecodeError, "invalid IHDR length" unless chunks.first[1].bytesize == 13
483
485
  width, height, bit_depth, color_type, compression, filter_method, interlace = chunks.first[1].unpack("N2C5")
@@ -561,7 +563,7 @@ module Tessel
561
563
  end
562
564
  end
563
565
  end
564
- Image.from_rgba(width, height, rgba, metadata: parse_metadata(chunks))
566
+ Image.from_rgba(width, height, rgba, metadata: parse_metadata(chunks, max_metadata_bytes))
565
567
  end
566
568
  private_class_method :parse
567
569
 
@@ -620,12 +622,14 @@ module Tessel
620
622
  end
621
623
  private_class_method :pixel_rgba
622
624
 
623
- def parse_metadata(chunks)
625
+ def parse_metadata(chunks, remaining)
624
626
  chunks.each_with_object({}) do |(type, data), metadata|
625
627
  case type
626
628
  when "tEXt"
627
629
  key, value = data.split("\0", 2)
628
630
  raise DecodeError, "invalid PNG tEXt chunk" unless key && !key.empty? && value
631
+ raise LimitError, "PNG metadata exceeds limit" if key.bytesize + value.bytesize > remaining
632
+ remaining -= key.bytesize + value.bytesize
629
633
  metadata[key.force_encoding("ISO-8859-1").encode("UTF-8")] = value.force_encoding("ISO-8859-1").encode("UTF-8")
630
634
  when "iTXt"
631
635
  key, rest = data.split("\0", 2)
@@ -637,7 +641,10 @@ module Tessel
637
641
  translated_end = text.index("\0", language_end + 1) if language_end
638
642
  raise DecodeError, "invalid PNG iTXt chunk" unless translated_end
639
643
  value = text.byteslice(translated_end + 1..)
640
- value = Zlib::Inflate.inflate(value) if compressed == 1
644
+ raise LimitError, "PNG metadata exceeds limit" if key.bytesize > remaining
645
+ value = inflate_metadata(value, remaining - key.bytesize) if compressed == 1
646
+ raise LimitError, "PNG metadata exceeds limit" if value.bytesize > remaining - key.bytesize
647
+ remaining -= key.bytesize + value.bytesize
641
648
  value.force_encoding("UTF-8")
642
649
  raise DecodeError, "invalid PNG iTXt text" unless value.valid_encoding?
643
650
  key.force_encoding("UTF-8")
@@ -647,6 +654,23 @@ module Tessel
647
654
  end
648
655
  end
649
656
  private_class_method :parse_metadata
657
+
658
+ def inflate_metadata(data, limit)
659
+ output = "".b
660
+ inflater = Zlib::Inflate.new
661
+ append = lambda do |part|
662
+ raise LimitError, "PNG metadata exceeds limit" if part.bytesize > limit - output.bytesize
663
+ output << part
664
+ end
665
+ begin
666
+ (0...data.bytesize).step(1024) { |offset| inflater.inflate(data.byteslice(offset, 1024), &append) }
667
+ inflater.finish(&append)
668
+ ensure
669
+ inflater.close
670
+ end
671
+ output
672
+ end
673
+ private_class_method :inflate_metadata
650
674
  end
651
675
 
652
676
  module_function
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tessel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-09-25 00:00:00.000000000 Z
11
+ date: 2026-09-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A dependency-light PNG reader/writer and shared RGBA8 pixel surface.
14
14
  email:
@@ -17,6 +17,8 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".codespellignore"
21
+ - ".yamllint"
20
22
  - CHANGELOG.md
21
23
  - LICENSE.txt
22
24
  - README.md