| 123456789101112131415161718192021222324252627282930313233343536373839404142 |
- //! Fuzz `redirect_uri` matching (`protocol::redirect_repo`, RFC 3986 §6.2.1
- //! plus the RFC 8252 §7.3 loopback exception).
- //!
- //! Both the requested URI (first input line) and the registered catalog set
- //! (following lines, capped) are hostile input: the matcher must never panic,
- //! never fall back to prefix/wildcard comparison, and only report `Exact`
- //! for a character-for-character member of the registered set.
- #![no_main]
- use d_oauth::protocol::redirect_repo::{RedirectDecision, match_redirect_uri};
- use libfuzzer_sys::fuzz_target;
- /// Maximum registered URIs taken from one input; matching is linear, so the
- /// cap keeps a single execution bounded.
- const MAX_REGISTERED: usize = 8;
- fuzz_target!(|data: &[u8]| {
- let Ok(text) = core::str::from_utf8(data) else {
- return;
- };
- let mut lines = text.split('\n');
- let Some(requested) = lines.next() else {
- return;
- };
- let registered: Vec<&str> = lines.take(MAX_REGISTERED).collect();
- // Flag off is the default policy; flag on additionally admits the exact
- // host `localhost` as a loopback host. Both must hold the same invariants.
- let decision = match_redirect_uri(®istered, requested, false);
- let localhost_decision = match_redirect_uri(®istered, requested, true);
- // An empty catalog can never match.
- if registered.is_empty() {
- debug_assert_eq!(decision, RedirectDecision::Reject);
- debug_assert_eq!(localhost_decision, RedirectDecision::Reject);
- }
- // `Exact` fires only for a character-for-character registered member.
- for decision in [decision, localhost_decision] {
- if decision == RedirectDecision::Exact {
- debug_assert!(registered.contains(&requested));
- }
- }
- });
|