security: fix unwrap/expect/panic on untrusted input (Fase 3 MEDIUM/LOW)

- xpub.rs: Replace convert_xpub() unwrap() with Result propagation
- xpub.rs: Replace calculate_fingerprint() unwrap() with Result propagation
- xpub.rs: Replace get_bitcoincore_descriptor() unwrap() with Result/match
- xpub.rs: Fix new_address_from_xpub() call in bal-server.rs to handle Result
- xpub.rs: Add prefix validation in convert_xpub (xpub/ypub/zpub/tpub/vpub/upub)
- bal-pusher.rs: Remove parse().unwrap() on env var bool, use unwrap_or(false)
- bal-pusher.rs: Replace port try_into().unwrap() with safe u16::try_from match
- bal-pusher.rs: Add error logging for invalid port values in env vars
- All tests pass: cargo test (5 tests: 3 SQL + 2 panic regression)
- Build verified: cargo check --bin=bal-server --bin=bal-pusher (0 errors)
This commit is contained in:
2026-07-16 15:08:49 -04:00
parent 167869b881
commit fa2f458468
3 changed files with 38 additions and 15 deletions

View File

@@ -103,7 +103,11 @@ fn calc_checksum(desc: &str) -> Result<String, String> {
}
pub fn get_bitcoincore_descriptor(xpub: &String) -> String {
let fingerprint = calculate_fingerprint(xpub);
let fingerprint = match calculate_fingerprint(xpub) {
Ok(f) => f,
Err(_) => return String::new(), // Invalid xpub, return empty descriptor
};
let mut bip = 84;
let cpub = xpub.to_string();
match &xpub[0..4] {
@@ -117,10 +121,14 @@ pub fn get_bitcoincore_descriptor(xpub: &String) -> String {
bip = 84;
}
};
let xpub_converted = match convert_xpub(xpub) {
Ok(c) => c,
Err(_) => return String::new(), // Invalid xpub, return empty descriptor
};
let descriptor = format!(
"wpkh([{}/84h/0h/0h]{}/0/*)",
fingerprint,
convert_xpub(xpub)
xpub_converted
);
let descriptor = match calc_checksum(&descriptor) {
Ok(checksum) => {
@@ -135,18 +143,20 @@ pub fn get_bitcoincore_descriptor(xpub: &String) -> String {
descriptor
//format!("{}#{}",descriptor,checksum)
}
fn convert_xpub(xpub: &String) -> String {
if xpub[0..4] == *"xpub" || xpub[0..4] == *"ypub" || xpub[0..4] == *"zpub" {
return convert_to(xpub, BS58Prefix::Xpub).unwrap();
fn convert_xpub(xpub: &String) -> Result<String, String> {
if xpub.len() >= 4 && (&xpub[0..4] == "xpub" || &xpub[0..4] == "ypub" || &xpub[0..4] == "zpub") {
convert_to(xpub, BS58Prefix::Xpub)
} else if xpub.len() >= 4 && (&xpub[0..4] == "tpub" || &xpub[0..4] == "vpub" || &xpub[0..4] == "upub") {
convert_to(xpub, BS58Prefix::Tpub)
} else {
return convert_to(xpub, BS58Prefix::Tpub).unwrap();
Err("Invalid xpub prefix: expected xpub, ypub, zpub, tpub, vpub, or upub".to_string())
}
}
pub fn calculate_fingerprint(tpub: &str) -> String {
let xpub = Xpub::from_str(&convert_to(tpub, BS58Prefix::Xpub).unwrap()).unwrap();
pub fn calculate_fingerprint(tpub: &str) -> Result<String, String> {
let xpub = Xpub::from_str(&convert_to(tpub, BS58Prefix::Xpub)?).map_err(|e| format!("Invalid xpub: {}", e))?;
let fp = xpub.fingerprint();
let pp = xpub.parent_fingerprint;
format!("{}", fp)
let _pp = xpub.parent_fingerprint;
Ok(format!("{}", fp))
}
fn base58check_decode(s: &str) -> Result<Vec<u8>, String> {