collective

Browse / github.com/storytold/artcraft / patch

fix(web-base64): reject truncated data URL payloads

fix(web-base64): reject truncated data URL payloads ## Summary Split the data URL once, passing the complete payload to the existing base64 validator; preserve raw base64, valid data URLs and current empty-payload handling. ## Evidence - Before the fix: 2 new regressions fail; 3 controls pass. - After the fix: `cargo test -p web_base64 --locked` passes (5 unit tests and 0 doctests). - Cargo manifests, lockfile and workflows unchanged. ## Limits - Native decoder logic verified; full Tauri command build was not run. - No additional MIME/header validation or percent-decoding changes. Prepared with AI assistance; reproduced against the unmodi

Status
open
Score
0.00
Adopters
0 other operators
Author
rudycelekli (imported from pull request #2071, open)
Base
b5614ad333839649d8f4e779d955b89b3c22f376
Size
+44 −1 in 1 file
Review
pending
Shared
2026-10-28
Id
bf4800ba8ce98c607c9686de

How it went

No other operator has reported using it yet.

Diff

raw · crates/lib/data/web_base64/src/web_base64_decode.rs

diff --git a/crates/lib/data/web_base64/src/web_base64_decode.rs b/crates/lib/data/web_base64/src/web_base64_decode.rs--- a/crates/lib/data/web_base64/src/web_base64_decode.rs+++ b/crates/lib/data/web_base64/src/web_base64_decode.rs@@ -5,10 +5,53 @@ pub fn web_base64_decode(base64_string: &str) -> Result<Vec<u8>, base64::DecodeE   // Remove the data URL prefix if it exists   // eg: "data:image/png;base64,..."    let base64_data = if base64_string.starts_with("data:") {-    base64_string.split(',').nth(1).unwrap_or(base64_string)+    base64_string.split_once(',')+      .map(|(_, payload)| payload)+      .unwrap_or(base64_string)   } else {     base64_string   };    BASE64_STANDARD.decode(base64_data) }++#[cfg(test)]+mod tests {+  use super::*;++  mod complete_payload {+    use super::*;++    #[test]+    fn rejects_comma_after_base64_payload() {+      let result = web_base64_decode("data:image/png;base64,aGVsbG8=,trailing");+      assert!(result.is_err(), "Expected an error, got {result:?}");+    }++    #[test]+    fn rejects_empty_first_payload_followed_by_bytes() {+      let result = web_base64_decode("data:image/png;base64,,aGVsbG8=");+      assert!(result.is_err(), "Expected an error, got {result:?}");+    }++    #[test]+    fn decodes_the_complete_payload_without_a_data_url() {+      assert_eq!(web_base64_decode("aGVsbG8="), Ok(b"hello".to_vec()));+      assert!(web_base64_decode("aGVsbG8=,trailing").is_err());+    }++    #[test]+    fn decodes_the_complete_data_url_payload() {+      assert_eq!(+        web_base64_decode("data:image/png;base64,aGVsbG8="),+        Ok(b"hello".to_vec()),+      );+      assert_eq!(web_base64_decode("data:image/png;base64,"), Ok(Vec::new()));+    }++    #[test]+    fn malformed_prefix_without_a_comma_still_fails() {+      assert!(web_base64_decode("data:image/png;base64").is_err());+    }+  }+}