aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-09 14:35:55 +0000
committertv <tv@krebsco.de>2026-11-09 16:14:06 +0100
commit9247e3980130ebfed9ef265e996e47bf1f6c852d (patch)
treedbfd798c61e27f7701e2135ff2269e8422675667 /src/transport
parentcf2bfaf4f3d54eac5444ae74ecd1407b3eecedd5 (diff)
transport: a pushed change written out by its size, never its bytes
Review 4, F3: the console writes each message out with `{:#?}`, and `SyncBody` derived `Debug`, so `Changes` (changes pushed as they are) printed every byte of every change. Pushes are the small messages, and Automerge deflates no change under 256 bytes: a short value's path and value went into the console in clear, which any user with any capability reads (the page's `messages` events, and since 1266 a subscribed node's `messages/own/{seq}` too), paths it may not read included. `SyncBody` now writes itself out with sizes for the changes, as `SyncPayload` does for an Automerge message; heads and a delta's spec stay as they were.
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/mod.rs41
1 files changed, 39 insertions, 2 deletions
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index d794271..e2135d1 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -779,8 +779,10 @@ impl std::fmt::Display for EpochTag {
}
}
-/// What a sync envelope carries.
-#[derive(Debug, Clone, Serialize, Deserialize)]
+/// What a sync envelope carries. Written out (`Debug`, the dashboard's console) with
+/// sizes for its bytes, never the bytes: a change holds the paths and values written,
+/// which whoever looks at the console may not read.
+#[derive(Clone, Serialize, Deserialize)]
pub(crate) enum SyncBody {
/// An Automerge sync message.
Message(#[serde(with = "crate::transport::wire::codec::sync_message")] SyncPayload),
@@ -845,6 +847,27 @@ impl DeltaSpec {
pub(crate) struct SyncPayload(pub(crate) bytes::Bytes);
/// Sizes instead of contents: logged messages stay short.
+impl std::fmt::Debug for SyncBody {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Message(payload) => f.debug_tuple("Message").field(payload).finish(),
+ Self::Deflated(payload) => f.debug_tuple("Deflated").field(payload).finish(),
+ Self::Changes { heads, changes } => f
+ .debug_struct("Changes")
+ .field("heads", heads)
+ .field(
+ "changes",
+ &changes
+ .iter()
+ .map(|change| format!("{} bytes", change.len()))
+ .collect::<Vec<_>>(),
+ )
+ .finish(),
+ Self::Delta(spec) => f.debug_tuple("Delta").field(spec).finish(),
+ }
+ }
+}
+
impl std::fmt::Debug for SyncPayload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SyncPayload({} bytes)", self.0.len())
@@ -1146,6 +1169,20 @@ pub(crate) trait Transport: Send + Sync {
#[cfg(test)]
mod tests {
use super::*;
+
+ /// A pushed change is written out by its size, never its bytes (the console).
+ #[test]
+ fn pushed_changes_written_out_by_size() {
+ let body = SyncBody::Changes {
+ heads: Vec::new(),
+ changes: vec![serde_bytes::ByteBuf::from(
+ b"secret-path secret-value".to_vec(),
+ )],
+ };
+ let text = format!("{body:#?}");
+ assert!(text.contains("24 bytes"), "{text}");
+ assert!(!text.contains("115"), "{text}");
+ }
use crate::testrand::for_seeds;
/// Two summaries are equal exactly when the heads are, in any order, whichever form