@@ -5,6 +5,7 @@ use std::sync::Arc;
55
66use anyhow:: { anyhow, Context , Result } ;
77use bitcoincore_rpc:: bitcoin:: Amount ;
8+ use futures_util:: TryStreamExt ;
89use http_body_util:: combinators:: BoxBody ;
910use http_body_util:: { BodyExt , Full } ;
1011use hyper:: body:: { Buf , Bytes , Incoming } ;
@@ -17,9 +18,10 @@ use payjoin::bitcoin::FeeRate;
1718use payjoin:: receive:: v1:: { PayjoinProposal , UncheckedProposal } ;
1819use payjoin:: receive:: ReplyableError :: { self , Implementation , V1 } ;
1920use payjoin:: send:: v1:: SenderBuilder ;
20- use payjoin:: { ImplementationError , Uri , UriExt } ;
21+ use payjoin:: { ImplementationError , Uri , UriExt , MAX_CONTENT_LENGTH } ;
2122use tokio:: net:: TcpListener ;
2223use tokio:: sync:: watch;
24+ use tokio_util:: io:: { StreamReader , SyncIoBridge } ;
2325
2426use super :: config:: Config ;
2527use super :: wallet:: BitcoindWallet ;
@@ -88,12 +90,26 @@ impl AppTrait for App {
8890 "Sent fallback transaction hex: {:#}" ,
8991 payjoin:: bitcoin:: consensus:: encode:: serialize_hex( & fallback_tx)
9092 ) ;
91- let psbt = ctx. process_response ( & mut response. bytes ( ) . await ?. to_vec ( ) . as_slice ( ) ) . map_err (
92- |e| {
93- log:: debug!( "Error processing response: {e:?}" ) ;
94- anyhow ! ( "Failed to process response {e}" )
95- } ,
96- ) ?;
93+
94+ if let Some ( content_length) = response. content_length ( ) {
95+ if content_length > MAX_CONTENT_LENGTH as u64 {
96+ return Err ( anyhow ! (
97+ "Response content length exceeded the limit of {MAX_CONTENT_LENGTH} bytes"
98+ ) ) ;
99+ }
100+ }
101+
102+ // Pass the response body to process_response without loading it all into memory.
103+ // This prevents a maliciously crafted response from causing an unbounded allocation.
104+ let stream =
105+ response. bytes_stream ( ) . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ;
106+ let async_reader = StreamReader :: new ( stream) ;
107+ let mut sync_reader = SyncIoBridge :: new ( async_reader) ;
108+
109+ let psbt = ctx. process_response ( & mut sync_reader) . map_err ( |e| {
110+ log:: debug!( "Error processing response: {e:?}" ) ;
111+ anyhow ! ( "Failed to process response {}" , e)
112+ } ) ?;
97113
98114 self . process_pj_response ( psbt) ?;
99115 Ok ( ( ) )
0 commit comments