docs: add JPEG validation implementation plan for M5Max48

This commit is contained in:
M5Max128
2026-05-27 15:40:15 +08:00
parent a036d985b7
commit ea20e27a4d

View File

@@ -0,0 +1,187 @@
---
title: Thumbnail JPEG Validation Implementation
version: 1.0.0
date: 2026-05-27
author: M5Max128
status: ready_for_implementation
---
# Thumbnail JPEG Validation Implementation
## Overview
Add JPEG quality validation to all ffmpeg image extraction endpoints to prevent:
- Empty images (0 bytes)
- Corrupted JPEG (missing header/footer)
- Incomplete JPEG (truncated output)
## Files to Create/Modify
### 1. Create: `src/core/thumbnail/validator.rs`
```rust
use anyhow::{bail, Result};
pub const JPEG_MIN_SIZE: usize = 100;
pub const JPEG_SOI_MARKER: [u8; 3] = [0xFF, 0xD8, 0xFF];
pub const JPEG_EOI_MARKER: [u8; 2] = [0xFF, 0xD9];
pub fn validate_jpeg(data: &[u8]) -> Result<()> {
if data.len() < JPEG_MIN_SIZE {
bail!("JPEG too small: {} bytes (minimum {})", data.len(), JPEG_MIN_SIZE);
}
if data[0..3] != JPEG_SOI_MARKER {
bail!("Invalid JPEG header: expected {:02X?}, got {:02X?}", JPEG_SOI_MARKER, &data[0..3]);
}
if data[data.len() - 2..] != JPEG_EOI_MARKER {
bail!("Incomplete JPEG: missing EOI marker, got {:02X?}", &data[data.len() - 2..]);
}
Ok(())
}
pub fn is_valid_jpeg(data: &[u8]) -> bool {
validate_jpeg(data).is_ok()
}
pub fn jpeg_size_ok(data: &[u8]) -> bool {
data.len() >= JPEG_MIN_SIZE
}
pub fn jpeg_header_ok(data: &[u8]) -> bool {
data.len() >= 3 && data[0..3] == JPEG_SOI_MARKER
}
pub fn jpeg_footer_ok(data: &[u8]) -> bool {
data.len() >= 2 && data[data.len() - 2..] == JPEG_EOI_MARKER
}
```
### 2. Modify: `src/core/thumbnail/mod.rs`
Add module declaration at line 1:
```rust
pub mod validator;
use anyhow::{Context, Result};
// ... rest of file
```
### 3. Modify: `src/api/media_api.rs`
Location: `face_thumbnail()` function, after ffmpeg output check (around line 754)
Add validation:
```rust
if !output.status.success() {
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
// ADD THIS LINE:
crate::core::thumbnail::validator::validate_jpeg(&output.stdout)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Response::builder()
// ... rest of response
```
### 4. Modify: `src/api/trace_agent_api.rs`
Location: `get_trace_thumbnail()` function, after reading bytes (around line 544)
Add validation:
```rust
let bytes = tokio::fs::read(&tmp).await.map_err(|e| {
(StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})))
})?;
let _ = tokio::fs::remove_file(&tmp).await;
// ADD THIS LINE:
crate::core::thumbnail::validator::validate_jpeg(&bytes)
.map_err(|e| {
(StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})))
})?;
Ok(Response::builder()
// ... rest of response
```
### 5. Modify: `src/core/frame_cache.rs`
Location: `FrameManager::extract()`, when iterating extracted frames (around line 73)
Replace the frame collection logic:
```rust
for entry in &entries {
let fname = entry.file_name();
let fname_str = fname.to_string_lossy();
if let Some(num_str) = fname_str
.strip_prefix("frame_")
.and_then(|s| s.strip_suffix(".jpg"))
{
if let Ok(frame_num) = num_str.parse::<u64>() {
let frame_path = entry.path();
// ADD VALIDATION:
if let Ok(data) = std::fs::read(&frame_path) {
if crate::core::thumbnail::validator::is_valid_jpeg(&data) {
let timestamp = frame_num as f64 / fps;
frames.push(CachedFrame {
path: frame_path,
frame_number: frame_num,
timestamp_secs: timestamp,
});
} else {
info!("[FrameCache] Skipping invalid JPEG: {:?}", frame_path);
}
}
}
}
}
```
## Validation Logic
| Check | Condition | Error if failed |
|-------|-----------|-----------------|
| Minimum size | `len() >= 100` | "JPEG too small" |
| SOI marker | `[0..3] == [0xFF,0xD8,0xFF]` | "Invalid JPEG header" |
| EOI marker | `[-2..] == [0xFF,0xD9]` | "Incomplete JPEG" |
## Testing
After implementation, run:
```bash
source ~/.cargo/env
export MOMENTRY_PYTHON_PATH="/Users/accusys/momentry_core/venv/bin/python"
cargo clippy --lib
cargo test --lib
```
Expected: 220 passed, 0 failed
## Commit Message
```
feat: add JPEG validation to thumbnail endpoints
- Create validator module with JPEG header/footer/size checks
- Add validation to face_thumbnail endpoint
- Add validation to get_trace_thumbnail endpoint
- Filter invalid JPEGs in FrameManager::extract
Prevents serving corrupted/incomplete JPEG images to frontend.
```
## Version History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0.0 | 2026-05-27 | M5Max128 | Implementation plan ready |