Fix review findings: direction-bind the gossip handshake proof, whitespace-tokenize log redaction, show ranked STATS to viewers, single-pass render, code-before-message email templating

This commit is contained in:
Jean Chevronnet 2026-07-20 17:35:25 +00:00
parent 829a3916fd
commit 621bba00f2
No known key found for this signature in database
5 changed files with 92 additions and 46 deletions

View file

@ -24,13 +24,17 @@ fn brand_mark(brand: &str, accent: &str, logo: &str) -> String {
}
fn render(brand: &str, accent: &str, logo: &str, title: &str, message: &str, code: &str, note: &str) -> String {
// `message` carries the account name (attacker-influenceable). Substitute every
// OTHER slot first and `{{message}}` LAST, so a message that literally contains
// `{{code}}`/`{{note}}` (an account named that) is inserted verbatim rather than
// splicing in the real code.
BASE.replace("{{brand_mark}}", &brand_mark(brand, accent, logo))
.replace("{{brand}}", &escape(brand))
.replace("{{accent}}", &escape(accent))
.replace("{{title}}", &escape(title))
.replace("{{message}}", &escape(message))
.replace("{{code}}", &escape(code))
.replace("{{note}}", &escape(note))
.replace("{{message}}", &escape(message))
}
pub fn reset(brand: &str, accent: &str, logo: &str, account: &str, code: &str, lang: &str) -> Mail {

View file

@ -497,13 +497,29 @@ pub fn render(lang: &str, msgid: &str, args: &[(&str, String)]) -> String {
.and_then(|m| m.get(msgid))
.map(String::as_str)
.unwrap_or(msgid);
if args.is_empty() {
if args.is_empty() || !template.contains('{') {
return template.to_string();
}
let mut out = template.to_string();
for (name, val) in args {
out = out.replace(&format!("{{{name}}}"), val);
// Single left-to-right pass: a substituted value is copied to the output and
// never re-scanned, so a value that itself contains `{name}` (e.g. a nick like
// "{game}") can't trigger a second substitution. An unknown `{x}` (no matching
// arg, e.g. the literal `{ON|OFF}`) is left verbatim, as before.
let mut out = String::with_capacity(template.len() + 16);
let mut rest = template;
while let Some(open) = rest.find('{') {
out.push_str(&rest[..open]);
let Some(rel_close) = rest[open..].find('}') else {
break; // unbalanced '{' — emit the remainder verbatim below
};
let close = open + rel_close;
let name = &rest[open + 1..close];
match args.iter().find(|(n, _)| *n == name) {
Some((_, val)) => out.push_str(val),
None => out.push_str(&rest[open..=close]),
}
rest = &rest[close + 1..];
}
out.push_str(rest);
out
}