fetch.rs 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. //! SSRF-guarded fetch for pre-registered `request_uri` values.
  2. //!
  3. //! Delegates to the shared [`crate::protocol::ssrf`] guard (`https`-only,
  4. //! no redirects, DNS-validated and connection-pinned targets, success
  5. //! statuses only) and then enforces the request-object body cap of
  6. //! [`MAX_REQUEST_OBJECT_BYTES`] with a UTF-8 requirement.
  7. use super::MAX_REQUEST_OBJECT_BYTES;
  8. use crate::{
  9. error::AppError,
  10. protocol::ssrf::{self, SsrfError},
  11. };
  12. /// Fetch a pre-registered request object behind the SSRF guard.
  13. ///
  14. /// # Errors
  15. ///
  16. /// Returns `invalid_request_object` for any policy violation or fetch
  17. /// failure; never surfaces transport detail.
  18. pub(crate) async fn fetch_request_object(uri: &str) -> Result<String, AppError> {
  19. let bytes = ssrf::fetch_guarded_bytes(uri)
  20. .await
  21. .map_err(map_ssrf_error)?;
  22. decode_request_object(&bytes)
  23. }
  24. /// Map a shared-guard failure to `invalid_request_object` without leaking
  25. /// transport detail; an over-limit body keeps the too-large message.
  26. fn map_ssrf_error(error: SsrfError) -> AppError {
  27. match error {
  28. SsrfError::TooLarge => AppError::invalid_request_object("request_uri response is too large"),
  29. _ => AppError::invalid_request_object("request_uri fetch failed"),
  30. }
  31. }
  32. /// Enforce the 64 KiB request-object cap and the UTF-8 requirement on an
  33. /// already-guarded response body.
  34. fn decode_request_object(bytes: &[u8]) -> Result<String, AppError> {
  35. if bytes.len() > MAX_REQUEST_OBJECT_BYTES {
  36. return Err(AppError::invalid_request_object(
  37. "request_uri response is too large",
  38. ));
  39. }
  40. String::from_utf8(bytes.to_vec())
  41. .map_err(|_| AppError::invalid_request_object("request_uri body is not UTF-8"))
  42. }
  43. #[cfg(test)]
  44. mod tests {
  45. use super::decode_request_object;
  46. use crate::jose::jar::MAX_REQUEST_OBJECT_BYTES;
  47. #[test]
  48. fn jar_fetch_decode_rejects_body_over_64kib() {
  49. let oversize = vec![b'a'; MAX_REQUEST_OBJECT_BYTES + 1];
  50. assert!(decode_request_object(&oversize).is_err());
  51. }
  52. #[test]
  53. fn jar_fetch_decode_rejects_non_utf8_body() {
  54. assert!(decode_request_object(&[0xff, 0xfe, 0x00]).is_err());
  55. }
  56. #[test]
  57. fn jar_fetch_decode_accepts_valid_body() {
  58. let body = "header.payload.signature";
  59. assert!(decode_request_object(body.as_bytes()).is_ok());
  60. }
  61. }