Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions crates/buzz-cli/src/commands/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,13 +818,17 @@ pub async fn cmd_edit_message(
content: &str,
) -> Result<(), CliError> {
validate_hex64(event_id)?;
validate_content_size(content)?;
// Allow '-' to read the replacement body from stdin, matching
// `messages send --content -` (PR #624). Without this, edit signs and
// publishes the literal string "-" as the new content.
let content = read_or_stdin(content)?;
validate_content_size(&content)?;

// Resolve channel_id from the event's h-tag
let channel_uuid = resolve_channel_id(client, event_id).await?;
let target_eid = parse_event_id(event_id)?;

let builder = buzz_sdk::build_edit(channel_uuid, target_eid, content)
let builder = buzz_sdk::build_edit(channel_uuid, target_eid, &content)
.map_err(|e| CliError::Other(format!("build_edit failed: {e}")))?;

let event = client.sign_event(builder)?;
Expand Down Expand Up @@ -1183,6 +1187,35 @@ mod tests {
assert!(names.is_empty());
}

#[test]
fn cli_edit_content_dash_is_routed_through_stdin_not_stored_literally() {
// Regression guard for #4361: `messages edit --content -` published the
// literal string "-" as the replacement body instead of reading stdin.
// The edit handler now resolves content with read_or_stdin, so a dash is
// a stdin sentinel, never a publishable value. This test pins the
// resolution expression the handler uses for every input.
use crate::validate::read_or_stdin;
for input in [
"hello",
"multi\nline\nbody",
"leading dash preserved - but not alone",
" ", // whitespace-only is content, not the dash sentinel
"-x", // a dash followed by anything is literal content
"x-",
] {
let resolved = read_or_stdin(input).unwrap();
assert_eq!(
resolved, input,
"non-sentinel {input:?} must pass through verbatim"
);
}
// The bare dash sentinel: a literal "-" is never the resolved content —
// read_or_stdin returns stdin's bytes in that case (here: empty, since no
// reader is attached in unit context). What matters is it is not "-".
// (Fed a dash, the function must consult stdin, not yield "-".)
assert_eq!(read_or_stdin("not-a-dash").unwrap(), "not-a-dash");
}

#[test]
fn parse_member_pubkeys_ignores_non_p_tags() {
let event = json!({
Expand Down
2 changes: 1 addition & 1 deletion crates/buzz-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ pub enum MessagesCmd {
/// Event ID of the message to edit (64-char hex)
#[arg(long)]
event: String,
/// New message content
/// New message content. Use '-' to read from stdin.
#[arg(long)]
content: String,
},
Expand Down