chat: enforce shared text policy at UI, sign point, and gossip ingress
CI / check (push) Successful in 3m4s
CI / check (push) Successful in 3m4s
Chat-hardening plan Phase 1. The chat body policy (2,000-char + 8 KiB ceilings, single-pass control/whitespace normalization) moves from the UI layer into src/sanitize.rs and is now enforced at every trust boundary: cap_chat_input bounds the live input (oversized paste), the gossip sign point re-sanitizes so non-UI callers can't bypass policy, and gossip ingress rejects oversized raw text before sanitizing (admit_chat_text) and drops messages with neither visible text nor an attachment. The incoming chat author label now uses the strict name sanitizer until Phase 2 roster-binds it. +8 tests (532 lib green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -819,6 +819,21 @@ impl RoomState for IrohGossipState {
|
||||
a.name = crate::files::sanitize_filename(&a.name);
|
||||
Some(a)
|
||||
});
|
||||
// Chat text policy at INGRESS: reject raw text
|
||||
// over the byte ceiling before spending any
|
||||
// sanitize work on it (a compliant sender
|
||||
// sanitizes before signing), and drop a message
|
||||
// with neither visible text nor an attachment.
|
||||
let Some(text) = crate::sanitize::admit_chat_text(
|
||||
&text,
|
||||
attachment.is_some(),
|
||||
) else {
|
||||
crate::log_msg(&format!(
|
||||
"Dropped out-of-policy chat from author={:?} (oversized or empty)",
|
||||
payload.author
|
||||
));
|
||||
continue;
|
||||
};
|
||||
let _ = event_tx
|
||||
.send(RoomEvent::ChatMessage {
|
||||
from: payload.author,
|
||||
@@ -966,6 +981,14 @@ impl RoomState for IrohGossipState {
|
||||
text: String,
|
||||
attachment: Option<crate::files::ChatAttachment>,
|
||||
) -> Result<(), NetError> {
|
||||
// Enforce the chat text policy at the SIGN point, not only in the UI, so
|
||||
// a future non-UI caller can't sign an out-of-policy body (chat-hardening
|
||||
// plan Phase 1). Idempotent over the UI's own sanitize pass.
|
||||
let text = crate::sanitize::sanitize_chat(&text);
|
||||
if text.is_empty() && attachment.is_none() {
|
||||
// Nothing visible to send — not an error, just nothing to do.
|
||||
return Ok(());
|
||||
}
|
||||
let name = {
|
||||
let guard = self.self_state.lock().unwrap();
|
||||
match guard.as_ref() {
|
||||
|
||||
Reference in New Issue
Block a user