diff --git a/Cargo.toml b/Cargo.toml index 6c79b18..a83e3b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ elliptic-curve = { version = "0.13.5", features = ["pkcs8"] } webpki-roots = "0.25.2" wasm-logger = "0.2.0" web-time = "0.2" +tlsn-utils = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "8d8ffe1" } # tlsn-core = { git = "https://github.com/tlsnotary/tlsn", branch = "dev" } [dependencies.tlsn-core] diff --git a/src/components/redacted_bytes_component.rs b/src/components/redacted_bytes_component.rs index a9c7b16..63ad0b4 100644 --- a/src/components/redacted_bytes_component.rs +++ b/src/components/redacted_bytes_component.rs @@ -1,10 +1,8 @@ -use std::fmt; +use std::{fmt, ops::Range}; +use gloo::console::log; use yew::prelude::*; -pub const REDACTED_CHAR: char = '█'; -// pub const REDACTED_CHAR: char = '🙈'; - #[derive(Clone, PartialEq)] pub enum Direction { Send, @@ -23,116 +21,91 @@ impl fmt::Display for Direction { #[derive(Clone, PartialEq, Properties)] pub struct Props { pub direction: Direction, - pub bytes: String, + pub redacted_char: char, + pub bytes: Vec, + pub redacted_ranges: Vec>, } -//split the text in regular text parts and redacted parts -fn split_text(text: String) -> Vec { - let (mut parts, last_part) = text.chars().fold( - (Vec::new(), String::new()), - |(mut acc, mut current_part), c| { - let previous_c = current_part.chars().last().unwrap_or(REDACTED_CHAR); - if (c == REDACTED_CHAR) == (previous_c == REDACTED_CHAR) { - current_part.push(c); - } else { - if !current_part.is_empty() { - acc.push(current_part.clone()); - } - current_part.clear(); - current_part.push(c); - } - (acc, current_part) +fn get_redacted_string(redacted_char: &char, size: usize) -> String { + if true || size < 10 { + redacted_char.to_string().repeat(size) + } else { + format! {"{}...{}", redacted_char.to_string().repeat(3), redacted_char.to_string().repeat(3)} + } +} + +fn redactions_in_red( + bytes: &Vec, + redacted_ranges: &Vec>, + redacted_char: &char, +) -> Html { + if redacted_ranges.is_empty() { + return Html::from(String::from_utf8(bytes.to_vec()).unwrap()); + } + + // create ranges for non redacted parts and store last redacted position + let (non_redacted_ranges, last_redacted_position) = redacted_ranges.iter().fold( + (Vec::new(), 0), // (Accumulator vector, last redacted position) + |(mut acc, last_end), range| { + acc.push(Range { + start: last_end, + end: range.start, + }); + (acc, range.end) }, ); - if !last_part.is_empty() { - parts.push(last_part); - } - parts -} -fn redactions_in_red(text: String) -> Html { - let parts = split_text(text); - - //color redacted parts in red - let html: Html = parts - .iter() - .map(|part| match part { - x if x.starts_with(REDACTED_CHAR) => Html::from_html_unchecked(AttrValue::from( - format!("{}", x), - )), - _ => Html::from(part), + // interweave the redacted and non-redacted ranges + let all_ranges = non_redacted_ranges + .into_iter() + .zip(redacted_ranges.iter()) + .flat_map(|(non_redacted, redacted)| { + vec![ + (non_redacted.start, non_redacted.end, false), + (redacted.start, redacted.end, true), + ] }) - .collect(); + .chain(std::iter::once(( + last_redacted_position, + bytes.len(), + false, + ))); // Handle remaining non-redacted part - html + let html_nodes = all_ranges + .map(|(start, end, is_redacted)| { + if is_redacted { + Html::from_html_unchecked(AttrValue::from(format!( + "{}", + get_redacted_string(redacted_char, end - start) + ))) + } else { + Html::from(String::from_utf8_lossy(&bytes[start..end])) + } + }) + .collect::>(); + + html! { + <> + { for html_nodes } + + } } #[function_component] pub fn RedactedBytesComponent(props: &Props) -> Html { - let Props { direction, bytes } = props; - - let redacted_transcript = bytes.replace('\0', REDACTED_CHAR.to_string().as_str()); + let Props { + direction, + redacted_char, + bytes, + redacted_ranges, + } = props; html! {
{"Bytes "}{direction}{": " }
-
{redactions_in_red(redacted_transcript)}
+
{redactions_in_red(bytes, redacted_ranges, redacted_char)}
} } - -#[cfg(test)] -mod tests { - use super::*; - - fn redacted(length: usize) -> String { - '█'.to_string().repeat(length) - } - - #[test] - fn test_split_text_starts_with_redacted() { - let foobar = String::from("foobar"); - let text = format!("{}{}", redacted(8), &foobar); - let result1 = split_text(text.to_owned()); - assert_eq!(result1, vec![redacted(8), foobar]); - } - - #[test] - fn test_split_text_no_redactions() { - let text = "NoRedactedParts"; - let result = split_text(text.to_owned()); - assert_eq!(result, vec![text]); - } - - #[test] - fn test_split_text_ends_with_redaction() { - let foobar = String::from("foobar"); - let text = format!("{}{}", &foobar, redacted(8)); - let result = split_text(text); - assert_eq!(result, vec![foobar, redacted(8)]); - } - - #[test] - fn test_split_text_multiple() { - let text = format!("foobar {} test {} end", redacted(8), redacted(1)); - let result = split_text(text); - assert_eq!( - result, - vec![ - "foobar ", - redacted(8).as_str(), - " test ", - redacted(1).as_str(), - " end" - ] - ); - } - - #[test] - fn test_split_text_empty() { - let result5 = split_text(String::from("")); - assert_eq!(result5, Vec::::new()); - } -} diff --git a/src/components/view_file.rs b/src/components/view_file.rs index dc3c649..0285aff 100644 --- a/src/components/view_file.rs +++ b/src/components/view_file.rs @@ -1,4 +1,5 @@ extern crate base64; +use std::ops::Range; use std::str; use web_time::Duration; @@ -10,6 +11,8 @@ use crate::components::content_iframe::ContentIFrame; use crate::components::redacted_bytes_component::Direction; use crate::components::redacted_bytes_component::RedactedBytesComponent; +const REDACTED_CHAR: char = '█'; // 'X'. '🙈'; + #[derive(Properties, PartialEq)] pub struct Props { pub name: String, @@ -85,9 +88,11 @@ pub fn ViewFile(props: &Props) -> Html { sent.set_redacted(b'\0'); recv.set_redacted(b'\0'); - let bytes_send = String::from_utf8(sent.data().to_vec()).unwrap(); - - let bytes_received = String::from_utf8(recv.data().to_vec()).unwrap(); + let redacted_ranges_send: Vec> = + sent.redacted().clone().iter_ranges().collect(); + let bytes_recv = String::from_utf8(recv.data().to_vec()).unwrap(); + let redacted_ranges_recv: Vec> = + recv.redacted().clone().iter_ranges().collect(); html! {
@@ -106,11 +111,12 @@ pub fn ViewFile(props: &Props) -> Html {
- + - + + + - } }