Skip to content

Limit response sizes for v1#586

Merged
spacebear21 merged 2 commits intopayjoin:masterfrom
shinghim:483-response-size
May 21, 2025
Merged

Limit response sizes for v1#586
spacebear21 merged 2 commits intopayjoin:masterfrom
shinghim:483-response-size

Conversation

@shinghim
Copy link
Contributor

Add content length checks in v1 send and payjoin-cli.

Fixes #483

@shinghim shinghim force-pushed the 483-response-size branch 2 times, most recently from e8b8c6d to f484552 Compare March 17, 2025 13:29
@coveralls
Copy link
Collaborator

coveralls commented Mar 17, 2025

Pull Request Test Coverage Report for Build 15169166399

Details

  • 41 of 48 (85.42%) changed or added relevant lines in 2 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.09%) to 83.525%

Changes Missing Coverage Covered Lines Changed/Added Lines %
payjoin/src/send/error.rs 0 2 0.0%
payjoin/src/send/v1.rs 41 46 89.13%
Files with Coverage Reduction New Missed Lines %
payjoin/src/send/error.rs 2 39.72%
Totals Coverage Status
Change from base Build 15167733837: 0.09%
Covered Lines: 5947
Relevant Lines: 7120

💛 - Coveralls

@shinghim shinghim force-pushed the 483-response-size branch from f484552 to 6ecf617 Compare March 17, 2025 13:32
@shinghim shinghim marked this pull request as ready for review March 17, 2025 13:38
Copy link
Collaborator

@benalleng benalleng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, I left a comment about cleaning up the test to make sure we don't get any false positives. The private errors can make it difficult to drill down to get the precise error but for now I think string matches are ok.

@benalleng

This comment was marked as resolved.

@shinghim shinghim force-pushed the 483-response-size branch 4 times, most recently from c94ce03 to 0937da7 Compare March 20, 2025 03:25
Copy link
Collaborator

@benalleng benalleng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for cleaning up the naming on those test-utils consts! I have another approach that I have decided I think is better with regards to the test cases. Sorry to lead you on a goose chase with these

Comment on lines 384 to 405
let expected_error =
"The receiver sent an invalid response: couldn't decode as PSBT or JSON";
match response {
Ok(_) => panic!("Expected error, got success"),
Err(error) => {
assert!(matches!(error, ResponseError::Validation(_)));
assert_eq!(format!("{}", error), expected_error)
}
}

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error is being mapped to InternalValidationError::Parse, so we won't see the same error message from the rust std lib. Instead, we'll see

let expected_error =
            "The receiver sent an invalid response: couldn't decode as PSBT or JSON";

defined here:

impl fmt::Display for ValidationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use InternalValidationError::*;

        match &self.0 {
            Parse => write!(f, "couldn't decode as PSBT or JSON",),
            Io(e) => write!(f, "couldn't read PSBT: {}", e),
            ContentTooLarge => write!(f, "content is larger than {} bytes", MAX_CONTENT_LENGTH),
            Proposal(e) => write!(f, "proposal PSBT error: {}", e),
            #[cfg(feature = "v2")]
            V2Encapsulation(e) => write!(f, "v2 encapsulation error: {}", e),
        }
    }
}

Copy link
Collaborator

@benalleng benalleng Mar 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right, I got my wires crossed somehow. So we could use the same match code as above in that case to not rely on this expected_error string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Good idea on comparing the strings since the PartialEq stuff is kinda hairy with the internal/external stuff

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My last suggestion as it was just pointed out to me as a better practice is Ithat we should probably improve the panic strings here when something goes wrong. Something like.
Ok(_) => panic!("PSBT is malformed and should error") and
Ok(_) => panic!("Response string is malformed and should error")

@shinghim shinghim force-pushed the 483-response-size branch 3 times, most recently from 7c5ff17 to 3725c02 Compare March 27, 2025 02:02
Copy link
Collaborator

@benalleng benalleng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK everything is looking good. I maybe would like a unit test specifically for the MAX_CONTENT_LENGTH but that's something we can catch when we come back through for send mutants

Copy link
Collaborator

@spacebear21 spacebear21 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 3725c02

@DanGould
Copy link
Contributor

rats. This one needs rebase

@shinghim shinghim force-pushed the 483-response-size branch from 3725c02 to 2f430ef Compare March 30, 2025 15:18
@shinghim
Copy link
Contributor Author

done 👍🏼

Copy link
Contributor

@DanGould DanGould left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the rebase. I do have one additional comment you might be able to clear up for me

Comment on lines 26 to 27
#[cfg(feature = "v1")]
pub(crate) const MAX_CONTENT_LENGTH: usize = 4_000_000 * 4 / 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I assume this mitigation would need to be implemented on all senders, wouldn't it make more sense to expose the constant defined in payjoin::send::v1 and use that here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this at the lib level cause it made sense to me that if we were sending something with the max size of MAX_CONTENT_LENGTH, we would also receive something with that same limit. Exposing it in payjoin and using it here makes sense and would be better to just have it defined in one place

Comment on lines 71 to 74
/// 4M block size limit with base64 encoding overhead => maximum reasonable size of content-length
/// 4_000_000 * 4 / 3 fits in u32
#[cfg(feature = "_core")]
pub(crate) const MAX_CONTENT_LENGTH: usize = 4_000_000 * 4 / 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not make sense to expose this as pub const for use in the implementation? Or to make it `pub(crate) for _core and NOT(v1) but pub for v1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right, it does. Updated to expose this in lib and use in the implementation

@shinghim shinghim force-pushed the 483-response-size branch 2 times, most recently from 1784002 to 77a95bf Compare March 30, 2025 21:26
DanGould added a commit that referenced this pull request Mar 31, 2025
Pull out the test constant commit from #586 into its own PR
shinghim added 2 commits May 21, 2025 13:42
BIP 78 doesn't enforce a size on the response, only that the content
length must be included. However, if there is not a limit, then some of
the send logic could make unbounded allocations.
Copy link
Collaborator

@spacebear21 spacebear21 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK - I took the liberty to rebase onto latest master.

@spacebear21 spacebear21 merged commit 7c9e7ee into payjoin:master May 21, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v1 sender should limit response size

5 participants