params_form.rs 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. //! Fuzz the wire-form helpers (`protocol::params`).
  2. //!
  3. //! [`parse_pairs`] is lossy but infallible on malformed percent escapes and
  4. //! non-UTF-8, so it must never panic on any input. Once accepted, the ordered
  5. //! pairs feed [`values_of`] (wire order) and [`single_valued_map`] (last-wins);
  6. //! [`has_duplicate_field`] reports a repeat only for a known field.
  7. #![no_main]
  8. use d_oauth::protocol::params::{has_duplicate_field, parse_pairs, single_valued_map, values_of};
  9. use libfuzzer_sys::fuzz_target;
  10. fuzz_target!(|data: &[u8]| {
  11. let Ok(raw) = core::str::from_utf8(data) else {
  12. return;
  13. };
  14. let Ok(pairs) = parse_pairs(raw) else {
  15. return;
  16. };
  17. // One pair per non-empty segment, in wire order.
  18. let segments = raw.split('&').filter(|segment| !segment.is_empty()).count();
  19. assert_eq!(pairs.len(), segments, "parse_pairs dropped or added a pair");
  20. // Distinct names in first-occurrence order.
  21. let mut names: Vec<&str> = Vec::new();
  22. for (name, _) in &pairs {
  23. if !names.contains(&name.as_str()) {
  24. names.push(name);
  25. }
  26. }
  27. let map = single_valued_map(&pairs);
  28. assert_eq!(map.len(), names.len(), "map lost a distinct name");
  29. for name in &names {
  30. let values = values_of(&pairs, name);
  31. assert!(!values.is_empty(), "known name produced no value");
  32. // Last-wins: the map carries the final occurrence.
  33. assert_eq!(
  34. map.get(*name),
  35. values.last(),
  36. "map did not keep the last value"
  37. );
  38. }
  39. // A repeat is reported exactly when a known name occurs twice or more.
  40. let expected = names.iter().any(|name| values_of(&pairs, name).len() > 1);
  41. assert_eq!(
  42. has_duplicate_field(&pairs, &names),
  43. expected,
  44. "duplicate detection disagreed with the wire pairs"
  45. );
  46. });