Files
ltk/tests/security_upload.rs

113 lines
3.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Defensive coverage for the image-upload boundary. The GLES path requires a
// live GL context (out of scope for these CPU-only tests) so the assertions
// below exercise the software backend, which shares the same length-vs-
// dimensions check before delegating to tiny-skia. Both paths:
//
// * refuse the upload when `data.len() != width * height * 4`,
// * refuse zero-area uploads (`width == 0` or `height == 0`),
// * never panic — they log to stderr and return without drawing.
use std::sync::Arc;
use ltk::core::{ RenderOptions, UiSurface };
use ltk::{ column, img_widget, Color, Element };
fn render_image( rgba: Arc<Vec<u8>>, w: u32, h: u32 ) -> UiSurface<()>
{
let mut surface = UiSurface::<()>::new( 64, 64 );
let view: Element<()> = column()
.padding( 0.0 )
.push( img_widget( rgba, w, h ) )
.into();
let _ = surface.render(
&view,
RenderOptions::full_canvas( 64, 64 ).background( Color::BLACK ),
);
surface
}
#[ test ]
fn declared_size_matching_buffer_renders_without_panic()
{
// Sanity baseline: a 4×4 image with the expected 64-byte buffer must
// render through the path that the validation tests below exercise.
let bytes = Arc::new( vec![ 0xFFu8; 4 * 4 * 4 ] );
let _ = render_image( bytes, 4, 4 );
}
#[ test ]
fn shorter_buffer_is_rejected_without_panic()
{
// 10×10 declared but only 16 bytes provided. Without the boundary
// check tiny-skia would walk past the slice end inside its premul
// loop. With the check the renderer logs once and skips the draw.
let bytes = Arc::new( vec![ 0u8; 16 ] );
let _ = render_image( bytes, 10, 10 );
}
#[ test ]
fn longer_buffer_is_rejected_without_panic()
{
// 2×2 declared, 256 bytes provided. The buffer is larger than the
// driver would read, but the size mismatch still fails the strict
// equality and the draw is refused.
let bytes = Arc::new( vec![ 0u8; 256 ] );
let _ = render_image( bytes, 2, 2 );
}
#[ test ]
fn zero_width_image_does_not_blit()
{
// IntSize::from_wh rejects zero dimensions on tiny-skia, but the
// boundary check fires earlier for a uniform error path on both
// backends.
let bytes = Arc::new( vec![] );
let _ = render_image( bytes, 0, 10 );
}
#[ test ]
fn zero_height_image_does_not_blit()
{
let bytes = Arc::new( vec![] );
let _ = render_image( bytes, 10, 0 );
}
#[ test ]
fn non_multiple_of_four_buffer_is_rejected()
{
// 1×1 should be 4 bytes; 3 bytes is shorter and not a multiple of 4 —
// every byte counts.
let bytes = Arc::new( vec![ 0xFF, 0xFF, 0xFF ] );
let _ = render_image( bytes, 1, 1 );
}
#[ test ]
fn dimensions_overflow_usize_are_rejected_without_panic()
{
// `u32::MAX × u32::MAX × 4` overflows. The boundary check uses
// `saturating_mul` (or i64-promotion in the GLES path) so the
// expected length never wraps to a small number that happens to
// match a real buffer length. The provided buffer is small, so the
// equality fails and the draw is refused.
let bytes = Arc::new( vec![ 0u8; 16 ] );
let _ = render_image( bytes, u32::MAX, u32::MAX );
}
#[ test ]
fn renderer_recovers_after_a_rejected_upload()
{
// A frame with a malformed image must not poison the surface. The
// next render with a valid view succeeds.
let bad = Arc::new( vec![ 0u8; 5 ] );
let mut s = render_image( bad, 4, 4 );
let view: Element<()> = column().padding( 0.0 ).into();
let out = s.render(
&view,
RenderOptions::full_canvas( 64, 64 ).background( Color::rgb( 0.0, 1.0, 0.0 ) ),
);
// Tree shape change → full redraw, but the important check is that
// render() returned at all.
let _ = out;
}