forked from bitcoinafterlife/bal-server
fix: per-endpoint rate limits, request timeout and input hardening
bal-server: - Wire independent actix-governor buckets per endpoint class (pushtxs, searchtx, info/stats, default) so polling a cheap endpoint can no longer exhaust the push-txs budget; previously every limit was dead config - Enforce BAL_SERVER_ACTIX_TIMEOUT_SECS via a request-timeout middleware that returns 408 Request Timeout, covering slow request bodies - De-duplicate txids within a single /pushtxs request body - Replace the byte-slice xpub detection (panic on non-ASCII) with a safe prefix check bal-pusher: - Handle malformed/empty ZMQ frames without indexing panics or UTF-8 expect - Return an error from sign_message and skip the stats report instead of panicking when the private key cannot be parsed validation / db: - Reject IPv4-mapped IPv6 targets (::ffff:a.b.c.d) in is_valid_welist_url to close an SSRF bypass, with unit tests - Use Path::starts_with for the SQLite forbidden-directory check so paths like /etcfoo are not misclassified misc: - Add explicit secret patterns to .gitignore and sync the docs
This commit is contained in:
@@ -65,6 +65,19 @@ pub fn is_valid_welist_url(url_str: &str) -> bool {
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// IPv4-mapped IPv6 addresses (e.g. `::ffff:127.0.0.1`) are
|
||||
// routed to the embedded IPv4 address on dual-stack hosts, so
|
||||
// they must be validated against the IPv4 rules as well.
|
||||
if let Some(v4) = v6.to_ipv4_mapped()
|
||||
&& (v4.is_loopback()
|
||||
|| v4.is_private()
|
||||
|| v4.is_link_local()
|
||||
|| v4.is_unspecified()
|
||||
|| v4.is_multicast())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,6 +167,38 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ipv4_mapped_ipv6() {
|
||||
assert!(
|
||||
!is_valid_welist_url("https://[::ffff:127.0.0.1]"),
|
||||
"IPv4-mapped loopback should be blocked"
|
||||
);
|
||||
assert!(
|
||||
!is_valid_welist_url("https://[::ffff:10.0.0.1]"),
|
||||
"IPv4-mapped private address should be blocked"
|
||||
);
|
||||
assert!(
|
||||
!is_valid_welist_url("https://[::ffff:172.16.0.1]"),
|
||||
"IPv4-mapped private address should be blocked"
|
||||
);
|
||||
assert!(
|
||||
!is_valid_welist_url("https://[::ffff:192.168.1.1]"),
|
||||
"IPv4-mapped private address should be blocked"
|
||||
);
|
||||
assert!(
|
||||
!is_valid_welist_url("https://[::ffff:169.254.169.254]"),
|
||||
"IPv4-mapped link-local address should be blocked"
|
||||
);
|
||||
assert!(
|
||||
!is_valid_welist_url("https://[::ffff:0.0.0.0]"),
|
||||
"IPv4-mapped unspecified address should be blocked"
|
||||
);
|
||||
assert!(
|
||||
is_valid_welist_url("https://[::ffff:8.8.8.8]"),
|
||||
"IPv4-mapped public address should be allowed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_malformed_urls() {
|
||||
assert!(!is_valid_welist_url("not a url"));
|
||||
|
||||
Reference in New Issue
Block a user