diff --git a/Cargo.lock b/Cargo.lock index 4c2826b8..ee9ae00a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,6 +78,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -1500,7 +1509,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.18", "bstr", "fnv", "log", @@ -2507,7 +2516,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "regex-automata", + "regex-automata 0.1.9", ] [[package]] @@ -2516,7 +2525,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.9", ] [[package]] @@ -3443,13 +3452,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ - "aho-corasick", + "aho-corasick 1.1.3", "memchr", - "regex-syntax 0.6.29", + "regex-automata 0.4.7", + "regex-syntax 0.8.2", ] [[package]] @@ -3462,6 +3472,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick 1.1.3", + "memchr", + "regex-syntax 0.8.2", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -4556,6 +4577,7 @@ dependencies = [ "qrcode", "rand 0.8.3", "rand_chacha 0.3.1", + "regex", "reqwest", "rust_decimal", "rust_decimal_macros", diff --git a/swap/Cargo.toml b/swap/Cargo.toml index 6af34a86..27c75f1b 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -42,6 +42,7 @@ proptest = "1" qrcode = "0.14" rand = "0.8" rand_chacha = "0.3" +regex = "1.10" reqwest = { version = "0.12", features = [ "http2", "rustls-tls", "stream", "socks" ], default-features = false } rust_decimal = { version = "1", features = [ "serde-float" ] } rust_decimal_macros = "1" diff --git a/swap/src/common.rs b/swap/src/common.rs index 98b9b99d..79e9aaeb 100644 --- a/swap/src/common.rs +++ b/swap/src/common.rs @@ -1,4 +1,7 @@ +use std::collections::HashMap; + use anyhow::anyhow; +use regex::Regex; const LATEST_RELEASE_URL: &str = "https://github.com/comit-network/xmr-btc-swap/releases/latest"; @@ -33,6 +36,98 @@ fn is_latest_version(current: &str, latest: &str) -> bool { current == latest } +/// Redact logs, etc. by replacing Bitcoin and Monero addresses +/// with generic placeholders. +/// +/// # Example +/// ```rust +/// use swap::common::redact; +/// +/// let redacted = redact("a9165a1e-d26d-4b56-bf6d-ca9658825c44"); +/// assert_eq!(redacted, ""); +/// ``` +pub fn redact(input: &str) -> String { + // Use a hashmap to keep track of which address we replace with which placeholder + let mut replacements: HashMap = HashMap::new(); + + /// Helper function to insert placeholders for all addresses + /// of a specific regex pattern into the hashmap. + fn insert_placeholders( + input: &str, + replacements: &mut HashMap, + pattern: &str, + create_placeholder: impl Fn(usize) -> String + ) -> Result<(), regex::Error> { + // compile the regex pattern + let regex = Regex::new(pattern)?; + + // keep count to generate distinct placeholders + let mut counter: usize = 0; + + // for every matched address check whether we already found it + // and if we didn't, generate a placeholder for it + for address in regex.find_iter(input) { + if replacements.contains_key(address.as_str()) { + replacements.insert(address.as_str().to_owned(), create_placeholder(counter)); + counter += 1; + } + } + + Ok(()) + } + + // TODO: verify regex patterns + + const MONERO_ADDR_REGEX: &'static str = r#"[48][1-9A-HJ-NP-Za-km-z]{94}"#; + const BITCOIN_ADDR_REGEX: &'static str = r#"\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b"#; + // Both XMR and BTC transactions have + // a 64 bit hex id so they aren't distinguishible + const TX_ID_REGEX: &'static str = r#"\b[a-fA-F0-9]{64}\b"#; + const SWAP_ID_REGEX: &'static str = r#"\b[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}\b"#; + + insert_placeholders( + input, + &mut replacements, + MONERO_ADDR_REGEX, + |count| format!("") + ).expect("regex to be valid"); + + insert_placeholders( + input, + &mut replacements, + BITCOIN_ADDR_REGEX, + |count| format!("") + ).expect("regex to be valid"); + + insert_placeholders( + input, + &mut replacements, + TX_ID_REGEX, + |count| format!("") + ).expect("regex to be valid"); + + insert_placeholders( + input, + &mut replacements, + SWAP_ID_REGEX, + |count| format!("") + ).expect("regex to be valid"); + + // allocate string variable to operate on + let mut redacted = input.to_owned(); + + // Finally we go through the input string and replace each occurance of an + // address we want to redact with the corresponding placeholder + for (address, placeholder) in replacements.iter() { + println!("replacing `{address}` with `{placeholder}`"); + redacted = redacted.replace(address, placeholder); + } + + redacted +} + + + #[cfg(test)] mod test { use super::*;