mirror of
https://github.com/tlsnotary/proof_viz.git
synced 2026-01-08 22:48:21 -05:00
Use data.redacted() to visualize redacted ranges
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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<u8>,
|
||||
pub redacted_ranges: Vec<Range<usize>>,
|
||||
}
|
||||
|
||||
//split the text in regular text parts and redacted parts
|
||||
fn split_text(text: String) -> Vec<String> {
|
||||
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<u8>,
|
||||
redacted_ranges: &Vec<Range<usize>>,
|
||||
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!("<span style=\"color:red;\">{}</span>", 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!(
|
||||
"<span style=\"color:red;\">{}</span>",
|
||||
get_redacted_string(redacted_char, end - start)
|
||||
)))
|
||||
} else {
|
||||
Html::from(String::from_utf8_lossy(&bytes[start..end]))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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! {
|
||||
<details class="p-4 w-5/6" open={true}>
|
||||
<summary><b>{"Bytes "}{direction}{": " }</b></summary>
|
||||
<div class="bg-black text-white p-4 rounded-md overflow-x-auto">
|
||||
<pre>{redactions_in_red(redacted_transcript)}</pre>
|
||||
<pre>{redactions_in_red(bytes, redacted_ranges, redacted_char)}</pre>
|
||||
</div>
|
||||
</details>
|
||||
}
|
||||
}
|
||||
|
||||
#[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::<String>::new());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Range<usize>> =
|
||||
sent.redacted().clone().iter_ranges().collect();
|
||||
let bytes_recv = String::from_utf8(recv.data().to_vec()).unwrap();
|
||||
let redacted_ranges_recv: Vec<Range<usize>> =
|
||||
recv.redacted().clone().iter_ranges().collect();
|
||||
|
||||
html! {
|
||||
<div class="p-4 flex flex-col justify-center items-center w-full">
|
||||
@@ -106,11 +111,12 @@ pub fn ViewFile(props: &Props) -> Html {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RedactedBytesComponent direction={Direction::Send} bytes={bytes_send} />
|
||||
<RedactedBytesComponent direction={Direction::Send} redacted_char={REDACTED_CHAR} bytes={sent.data().to_vec()} redacted_ranges={redacted_ranges_send} />
|
||||
|
||||
<ContentIFrame bytes={bytes_received.clone()} />
|
||||
<ContentIFrame bytes={bytes_recv.clone()} />
|
||||
|
||||
<RedactedBytesComponent direction={Direction::Received} redacted_char={REDACTED_CHAR} bytes={recv.data().to_vec()} redacted_ranges={redacted_ranges_recv} />
|
||||
|
||||
<RedactedBytesComponent direction={Direction::Received} bytes={bytes_received} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user