aboutsummaryrefslogtreecommitdiffstats
path: root/internal/notify/image_test.go
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-15 18:09:26 +0200
committerDanilo M. <danix@danix.xyz>2026-09-15 18:09:26 +0200
commit2a3916ce192d73bdd6f621cc3842782814351227 (patch)
tree4c275a9cd02cad501a77925cbdd0d18807a15c88 /internal/notify/image_test.go
parent6ad6f122eb2ec9cd8a55c042dffe38db41ed1959 (diff)
downloadnotifyd-2a3916ce192d73bdd6f621cc3842782814351227.tar.gz
notifyd-2a3916ce192d73bdd6f621cc3842782814351227.zip
fix(notify): cap image dimensions and rank icon_data last
A notification is untrusted input. RawImage.PNG computed stride*(Height-1)+Width*Channels in int, so Width=Height=2^31-1 with RowStride=0 wrapped the length expression negative, slipped past the guard, and reached image.NewRGBA, which panics. The D-Bus call path has no recover, so one malformed Notify killed the daemon. Cap Width/Height at 1<<16 before any multiplication and compute the required byte count in int64. image-data, image-path and the deprecated icon_data were read as one tier, with icon_data ahead of image-path, inverting the spec's order. Split the deprecated key into IconDataFromHints and apply tier 1 (image-data/image_data), then image-path, then icon_data. Raw handling is unchanged: encode, hold pendingImage, WriteImage after Add, SetImage.
Diffstat (limited to 'internal/notify/image_test.go')
-rw-r--r--internal/notify/image_test.go30
1 files changed, 27 insertions, 3 deletions
diff --git a/internal/notify/image_test.go b/internal/notify/image_test.go
index 7121f32..00dfb33 100644
--- a/internal/notify/image_test.go
+++ b/internal/notify/image_test.go
@@ -39,14 +39,15 @@ func TestImageDataFromHints(t *testing.T) {
}{
{"image-data wins", map[string]dbus.Variant{
"image-data": rawVariant(2, 1, 8, true, 4, rgba),
+ "icon_data": rawVariant(9, 9, 8, true, 4, rgba),
"image-path": dbus.MakeVariant("/tmp/x.png"),
}, 2, true},
- {"icon_data fallback", map[string]dbus.Variant{
- "icon_data": rawVariant(2, 1, 8, true, 4, rgba),
- }, 2, true},
{"image_data alias", map[string]dbus.Variant{
"image_data": rawVariant(2, 1, 8, true, 4, rgba),
}, 2, true},
+ {"icon_data is not tier 1", map[string]dbus.Variant{
+ "icon_data": rawVariant(2, 1, 8, true, 4, rgba),
+ }, 0, false},
{"absent", map[string]dbus.Variant{}, 0, false},
}
for _, c := range cases {
@@ -62,6 +63,20 @@ func TestImageDataFromHints(t *testing.T) {
}
}
+func TestIconDataFromHints(t *testing.T) {
+ rgba := []byte{255, 0, 0, 255, 0, 255, 0, 255}
+ if got, ok := IconDataFromHints(map[string]dbus.Variant{
+ "icon_data": rawVariant(2, 1, 8, true, 4, rgba),
+ }); !ok || got.Width != 2 {
+ t.Fatalf("icon_data got %v ok=%v", got, ok)
+ }
+ if _, ok := IconDataFromHints(map[string]dbus.Variant{
+ "image-data": rawVariant(2, 1, 8, true, 4, rgba),
+ }); ok {
+ t.Fatal("image-data must not be read as icon_data")
+ }
+}
+
func TestImagePathFromHints(t *testing.T) {
got, ok := ImagePathFromHints(map[string]dbus.Variant{"image-path": dbus.MakeVariant("/tmp/shot.png")})
if !ok || got != "/tmp/shot.png" {
@@ -137,4 +152,13 @@ func TestRawImagePNGRejectsBadData(t *testing.T) {
Data: []byte{0, 0, 0, 255, 255, 255, 255, 255}}).PNG(); err == nil {
t.Fatal("short data must error")
}
+ // Dimensions straight off the bus: with RowStride 0 the stride*(Height-1)
+ // product overflows to negative and used to slip past the guard into
+ // image.NewRGBA. Must error, not panic.
+ if _, err := (&RawImage{
+ Width: 1<<31 - 1, Height: 1<<31 - 1, RowStride: 0,
+ BitsPerSample: 8, Channels: 4, Data: []byte{0, 0, 0, 255},
+ }).PNG(); err == nil {
+ t.Fatal("oversized dimensions must error")
+ }
}