Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exclude_re = [
"deserialize",
"Iterator",
".*Error",
"impl\\s+arbitrary::Arbitrary",

# ---------------------Crate-specific exclusions ---------------------
# Timeout loops
Expand Down
6 changes: 4 additions & 2 deletions Cargo-minimal.lock
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"

[[package]]
name = "arbitrary"
version = "1.0.0"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "698b65a961a9d730fb45b6b0327e20207810c9f61ee421b082b27ba003f49e2b"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"

[[package]]
name = "arc-swap"
Expand Down Expand Up @@ -2756,6 +2756,7 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
name = "payjoin"
version = "1.0.0-rc.3"
dependencies = [
"arbitrary",
"bhttp",
"bitcoin 0.32.9",
"bitcoin-hpke",
Expand Down Expand Up @@ -2835,6 +2836,7 @@ dependencies = [
name = "payjoin-fuzz"
version = "0.0.1"
dependencies = [
"arbitrary",
"bitcoin_uri",
"home",
"libfuzzer-sys",
Expand Down
2 changes: 2 additions & 0 deletions Cargo-recent.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2800,6 +2800,7 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
name = "payjoin"
version = "1.0.0-rc.3"
dependencies = [
"arbitrary",
"bhttp",
"bitcoin 0.32.100",
"bitcoin-hpke",
Expand Down Expand Up @@ -2879,6 +2880,7 @@ dependencies = [
name = "payjoin-fuzz"
version = "0.0.1"
dependencies = [
"arbitrary",
"bitcoin_uri",
"home",
"libfuzzer-sys",
Expand Down
8 changes: 8 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ cargo-fuzz = true
default = []

[dependencies]
arbitrary = { version = "1.4.2" }
bitcoin_uri = { version = "0.1.0" }
home = "=0.5.11"
libfuzzer-sys = { version = "0.4.10" }
payjoin = { path = "../payjoin", default-features = false, features = [
"arbitrary",
"_core",
"v1",
"v2",
Expand All @@ -35,3 +37,9 @@ name = "url_decode_url"
path = "fuzz_targets/url/decode_url.rs"
doc = false
bench = false

[[bin]]
name = "url_arbitrary_url"
path = "fuzz_targets/url/arbitrary_url.rs"
doc = false
bench = false
42 changes: 42 additions & 0 deletions fuzz/fuzz_targets/url/arbitrary_url.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#![no_main]

use arbitrary::{Arbitrary, Unstructured};
use libfuzzer_sys::fuzz_target;
use payjoin::Url;

fn do_test(data: &[u8]) {
let mut u = Unstructured::new(data);
if let Ok(mut url) = Url::arbitrary(&mut u) {
if let Ok(port) = u.arbitrary::<Option<u16>>() {
url.set_port(port);
}
if let Ok(fragment) = u.arbitrary::<Option<&str>>() {
url.set_fragment(fragment);
}
if let Ok((key, value)) = u.arbitrary::<(&str, &str)>() {
url.query_pairs_mut().append_pair(key, value);
url.clear_query();
}
if let Some(mut segs) = url.path_segments_mut() {
if let Ok(segment) = u.arbitrary::<&str>() {
segs.push(segment);
}
}
if let Ok(segment) = String::arbitrary(&mut u) {
let _ = url.join(&segment);
}
}
}

fuzz_target!(|data| {
do_test(data);
});

#[cfg(test)]
mod tests {
#[test]
fn duplicate_crash() {
let data = b"\x00";
super::do_test(&data[..]);
}
}
19 changes: 1 addition & 18 deletions fuzz/fuzz_targets/url/decode_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use payjoin::Url;
fn do_test(data: &[u8]) {
let Ok(s) = str::from_utf8(data) else { return };

let Ok(mut url) = Url::parse(s) else { return };
let Ok(url) = Url::parse(s) else { return };

let _ = url.scheme();
let _ = url.domain();
Expand Down Expand Up @@ -44,23 +44,6 @@ fn do_test(data: &[u8]) {
reparsed.as_str()
);
}

url.set_port(Some(8080));
url.set_port(None);
url.set_fragment(Some("fuzz"));
url.set_fragment(None);
url.query_pairs_mut().append_pair("k", "v");
url.clear_query();
url.query_pairs_mut().append_pair("fuzz_key", "fuzz_val");

if let Some(mut segs) = url.path_segments_mut() {
segs.push("fuzz_segment");
}

let _ = url.join("relative/path");
let _ = url.join("/absolute/path");
let _ = url.join("../dotdot");
let _ = url.join("https://other.example.com/new");
}

fuzz_target!(|data| {
Expand Down
1 change: 1 addition & 0 deletions payjoin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ io = ["v2", "reqwest/rustls-tls"]
_manual-tls = ["rustls"]

[dependencies]
arbitrary = { version = "1.4.2", optional = true }
bhttp = { version = "0.6.1", optional = true }
bitcoin = { version = "0.32.9", features = ["base64"] }
bitcoin-units = "0.1.3"
Expand Down
26 changes: 26 additions & 0 deletions payjoin/src/core/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,32 @@ impl<'de> serde::Deserialize<'de> for Url {
}
}

#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Host {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
match u.int_in_range(0..=2)? {
0 => Ok(Self::Domain(u.arbitrary()?)),
1 => Ok(Self::Ipv4(u.arbitrary()?)),
_ => Ok(Self::Ipv6(u.arbitrary()?)),
}
}
}

#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Url {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Url {
raw: u.arbitrary()?,
scheme: u.arbitrary()?,
host: u.arbitrary()?,
port: u.arbitrary()?,
path: u.arbitrary()?,
query: u.arbitrary()?,
fragment: u.arbitrary()?,
})
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading