cimd_document.rs 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. //! Fuzz client-ID metadata document (CIMD) validation.
  2. //!
  3. //! [`validate_document`] is pure: it enforces the `client_id` echo, the
  4. //! registration-free auth methods, inline-only JWKS, the wildcard-free
  5. //! redirect set, the enabled-scope subset, and the grant allowlist without a
  6. //! fetch, Postgres, or Redis. The input is a first selector byte
  7. //! (`allow_localhost`), one `client_id` line, then a JSON document. An accepted
  8. //! document must echo its identifier, contain no wildcard redirect, and grant
  9. //! only the two registration-free grants.
  10. #![no_main]
  11. use std::collections::HashSet;
  12. use d_oauth::protocol::cimd::{CimdDocument, CimdValidation, validate_document};
  13. use libfuzzer_sys::fuzz_target;
  14. fuzz_target!(|data: &[u8]| {
  15. let Some((flag, rest)) = data.split_first() else {
  16. return;
  17. };
  18. let Ok(text) = core::str::from_utf8(rest) else {
  19. return;
  20. };
  21. let Some((client_id, json)) = text.split_once('\n') else {
  22. return;
  23. };
  24. let Ok(document) = serde_json::from_str::<CimdDocument>(json) else {
  25. return;
  26. };
  27. let scopes: HashSet<String> = ["read", "write"]
  28. .iter()
  29. .map(|name| (*name).to_owned())
  30. .collect();
  31. let validation = CimdValidation {
  32. enabled_scopes: &scopes,
  33. allow_localhost: *flag % 2 == 1,
  34. };
  35. let Ok(validated) = validate_document(client_id, &document, &validation) else {
  36. return;
  37. };
  38. // Accepted documents echo the URL they were validated under.
  39. assert_eq!(validated.client_id, client_id);
  40. // No wildcard ever survives the redirect allowlist.
  41. assert!(
  42. validated.redirect_uris.iter().all(|uri| !uri.contains('*')),
  43. "accepted document kept a wildcard redirect"
  44. );
  45. // Only the registration-free grants survive.
  46. assert!(
  47. validated
  48. .grant_types
  49. .iter()
  50. .all(|grant| grant == "authorization_code" || grant == "refresh_token"),
  51. "accepted document kept a non-CIMD grant"
  52. );
  53. });