Skip to content

Commit b792151

Browse files
committed
Merge branch 'proptest' for CVE-2024-39697
2 parents c0fdd69 + d2bef6d commit b792151

File tree

6 files changed

+79
-12
lines changed

6 files changed

+79
-12
lines changed

.github/workflows/build.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ jobs:
2525
toolchain: ["stable", "beta"]
2626
coverage: [false]
2727
tests: [true]
28+
proptest_max: [false]
2829
include:
30+
# We run the proptests with the stable toolchain on more iterations
31+
- toolchain: "stable"
32+
coverage: false
33+
tests: true
34+
proptest_max: true
2935
- toolchain: "nightly"
3036
coverage: true
3137
tests: true
@@ -60,11 +66,20 @@ jobs:
6066

6167
- name: Run tests
6268
uses: actions-rs/cargo@v1
63-
if: ${{ !matrix.coverage && matrix.tests }}
69+
if: ${{ !matrix.coverage && matrix.tests && !matrix.proptest_max }}
6470
with:
6571
command: test
6672
args: --all-targets --no-fail-fast
6773

74+
- name: Run tests
75+
uses: actions-rs/cargo@v1
76+
if: ${{ !matrix.coverage && matrix.tests && matrix.proptest_max }}
77+
env:
78+
PROPTEST_CASES: 65536
79+
with:
80+
command: test
81+
args: --all-targets --release --no-fail-fast
82+
6883
- name: Run tests
6984
uses: actions-rs/cargo@v1
7085
if: ${{ matrix.coverage && matrix.tests }}

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ criterion = ">=0.4, <=0.5"
4141
doc-comment = "0.3"
4242
rstest = ">= 0.13, <=0.19"
4343
rstest_reuse = "0.6"
44+
proptest = "1.0.0"
4445

4546
[[bench]]
4647
name = "parsing"

src/national_number.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@ pub struct NationalNumber {
2222
}
2323

2424
impl NationalNumber {
25-
pub fn new(value: u64, zeros: u8) -> Self {
25+
pub fn new(value: u64, zeros: u8) -> Result<Self, crate::error::Parse> {
2626
// E.164 specifies a maximum of 15 decimals, which corresponds to slightly over 48.9 bits.
2727
// 56 bits ought to cut it here.
28-
assert!(value < (1 << 56), "number too long");
29-
Self {
30-
value: ((zeros as u64) << 56) | value,
28+
if value >= (1 << 56) {
29+
return Err(crate::error::Parse::TooLong);
3130
}
31+
32+
Ok(Self {
33+
value: ((zeros as u64) << 56) | value,
34+
})
3235
}
3336

3437
/// The number without any leading zeroes.

src/parser/mod.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub fn parse_with<S: AsRef<str>>(
8686
national: NationalNumber::new(
8787
number.national.parse()?,
8888
number.national.chars().take_while(|&c| c == '0').count() as u8,
89-
),
89+
)?,
9090

9191
extension: number.extension.map(|s| Extension(s.into_owned())),
9292
carrier: number.carrier.map(|s| Carrier(s.into_owned())),
@@ -108,7 +108,7 @@ mod test {
108108
source: country::Source::Default,
109109
},
110110

111-
national: NationalNumber::new(33316005, 0),
111+
national: NationalNumber::new(33316005, 0).unwrap(),
112112

113113
extension: None,
114114
carrier: None,
@@ -196,7 +196,7 @@ mod test {
196196
source: country::Source::Number,
197197
},
198198

199-
national: NationalNumber::new(64123456, 0),
199+
national: NationalNumber::new(64123456, 0).unwrap(),
200200

201201
extension: None,
202202
carrier: None,
@@ -214,7 +214,7 @@ mod test {
214214
source: country::Source::Default,
215215
},
216216

217-
national: NationalNumber::new(30123456, 0),
217+
national: NationalNumber::new(30123456, 0).unwrap(),
218218

219219
extension: None,
220220
carrier: None,
@@ -229,7 +229,7 @@ mod test {
229229
source: country::Source::Plus,
230230
},
231231

232-
national: NationalNumber::new(2345, 0,),
232+
national: NationalNumber::new(2345, 0,).unwrap(),
233233

234234
extension: None,
235235
carrier: None,
@@ -244,7 +244,7 @@ mod test {
244244
source: country::Source::Default,
245245
},
246246

247-
national: NationalNumber::new(12, 0,),
247+
national: NationalNumber::new(12, 0,).unwrap(),
248248

249249
extension: None,
250250
carrier: None,
@@ -259,7 +259,7 @@ mod test {
259259
source: country::Source::Default,
260260
},
261261

262-
national: NationalNumber::new(3121286979, 0),
262+
national: NationalNumber::new(3121286979, 0).unwrap(),
263263

264264
extension: None,
265265
carrier: Some("12".into()),
@@ -279,4 +279,10 @@ mod test {
279279
let res = parser::parse(None, ".;phone-context=");
280280
assert!(res.is_err(), "{res:?}");
281281
}
282+
283+
#[test]
284+
fn advisory_2() {
285+
let res = parser::parse(None, "+dwPAA;phone-context=AA");
286+
assert!(res.is_err(), "{res:?}");
287+
}
282288
}

tests/prop.proptest-regressions

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Seeds for failure cases proptest has generated in the past. It is
2+
# automatically read and these particular cases re-run before any
3+
# novel cases are generated.
4+
#
5+
# It is recommended to check this file in to source control so that
6+
# everyone who runs the test benefits from these saved cases.
7+
cc f4f1a19cf143c767508ab557480843e4f4093898768fbbea1034d19d4308257d # shrinks to tel_prefix = false, use_plus = true, s = "da", phone_context = Some("A0A0a")
8+
cc 4ea103e574793bd24b0267cc8a80962299ee50746d69332fbd0b85532fb707e2 # shrinks to tel_prefix = false, use_plus = false, s = "0", phone_context = Some("প")

tests/prop.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use phonenumber::parse;
2+
use proptest::prelude::*;
3+
4+
proptest! {
5+
#[test]
6+
fn rfc3966_crash_test(
7+
tel_prefix: bool,
8+
use_plus: bool,
9+
s: String,
10+
phone_context: Option<String>,
11+
) {
12+
let context = if let Some(phone_context) = &phone_context { format!(";phone-context={phone_context}") } else { "".to_string() };
13+
let tel_prefix = if tel_prefix { "tel:" } else { "" };
14+
let plus = if use_plus { "+" } else { "" };
15+
let s = format!("{}{}{}{}", tel_prefix, plus, s, context);
16+
let _ = parse(None, &s);
17+
}
18+
19+
#[test]
20+
fn doesnt_crash(s in "\\PC*") {
21+
let _ = parse(None, &s);
22+
}
23+
24+
#[test]
25+
fn doesnt_crash_2(s in "\\+\\PC*") {
26+
let _ = parse(None, &s);
27+
}
28+
29+
#[test]
30+
fn parse_belgian_phonenumbers(s in "\\+32[0-9]{8,9}") {
31+
let parsed = parse(None, &s).expect("valid Belgian number");
32+
prop_assert_eq!(parsed.country().id(), phonenumber::country::BE.into());
33+
}
34+
}

0 commit comments

Comments
 (0)