feedparser_rs/http/
validation.rs1use crate::error::Result;
2use crate::util::ssrf;
3use url::Url;
4
5pub fn validate_url(url_str: &str) -> Result<Url> {
46 ssrf::validate_url(url_str)
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
55 fn test_valid_http_url() {
56 assert!(validate_url("http://example.com/feed.xml").is_ok());
57 }
58
59 #[test]
60 fn test_valid_https_url() {
61 assert!(validate_url("https://blog.example.org/rss").is_ok());
62 }
63
64 #[test]
65 fn test_valid_with_port() {
66 assert!(validate_url("https://example.com:8443/feed").is_ok());
67 }
68
69 #[test]
70 fn test_valid_with_path() {
71 assert!(validate_url("https://example.com/path/to/feed.xml").is_ok());
72 }
73
74 #[test]
76 fn test_reject_file_scheme() {
77 assert!(validate_url("file:///etc/passwd").is_err());
78 }
79
80 #[test]
81 fn test_reject_ftp_scheme() {
82 assert!(validate_url("ftp://example.com/file").is_err());
83 }
84
85 #[test]
86 fn test_reject_javascript_scheme() {
87 assert!(validate_url("javascript:alert(1)").is_err());
88 }
89
90 #[test]
91 fn test_reject_data_scheme() {
92 assert!(validate_url("data:text/html,<script>alert(1)</script>").is_err());
93 }
94
95 #[test]
97 fn test_reject_ipv4_private_10() {
98 assert!(validate_url("http://10.0.0.1/").is_err());
99 assert!(validate_url("http://10.255.255.255/").is_err());
100 }
101
102 #[test]
103 fn test_reject_ipv4_private_172() {
104 assert!(validate_url("http://172.16.0.1/").is_err());
105 assert!(validate_url("http://172.31.255.255/").is_err());
106 }
107
108 #[test]
109 fn test_reject_ipv4_private_192() {
110 assert!(validate_url("http://192.168.0.1/").is_err());
111 assert!(validate_url("http://192.168.255.255/").is_err());
112 }
113
114 #[test]
115 fn test_reject_ipv4_localhost() {
116 assert!(validate_url("http://127.0.0.1/").is_err());
117 assert!(validate_url("http://127.0.0.2/").is_err());
118 }
119
120 #[test]
121 fn test_reject_ipv4_link_local() {
122 assert!(validate_url("http://169.254.169.254/").is_err());
123 assert!(validate_url("http://169.254.0.1/").is_err());
124 }
125
126 #[test]
127 fn test_reject_ipv4_zero() {
128 assert!(validate_url("http://0.0.0.0/").is_err());
129 }
130
131 #[test]
132 fn test_reject_ipv4_broadcast() {
133 assert!(validate_url("http://255.255.255.255/").is_err());
134 }
135
136 #[test]
138 fn test_reject_ipv6_loopback() {
139 assert!(validate_url("http://[::1]/").is_err());
140 }
141
142 #[test]
143 fn test_reject_ipv6_link_local() {
144 assert!(validate_url("http://[fe80::1]/").is_err());
145 }
146
147 #[test]
148 fn test_reject_ipv6_unique_local() {
149 assert!(validate_url("http://[fc00::1]/").is_err());
150 assert!(validate_url("http://[fd00::1]/").is_err());
151 }
152
153 #[test]
155 fn test_reject_localhost_domain() {
156 assert!(validate_url("http://localhost/").is_err());
157 }
158
159 #[test]
160 fn test_reject_local_tld() {
161 assert!(validate_url("http://myserver.local/").is_err());
162 }
163
164 #[test]
165 fn test_reject_internal_tld() {
166 assert!(validate_url("http://server.internal/").is_err());
167 }
168
169 #[test]
170 fn test_reject_cloud_metadata() {
171 assert!(validate_url("http://metadata.google.internal/").is_err());
172 assert!(validate_url("http://metadata.azure.com/").is_err());
173 }
174
175 #[test]
177 fn test_reject_no_host() {
178 assert!(validate_url("http://").is_err());
179 }
180
181 #[test]
182 fn test_reject_invalid_url() {
183 assert!(validate_url("not a url").is_err());
184 }
185
186 #[test]
187 fn test_public_ip_allowed() {
188 assert!(validate_url("http://8.8.8.8/").is_ok());
190 assert!(validate_url("http://1.1.1.1/").is_ok());
191 }
192
193 #[test]
194 fn test_carrier_grade_nat_blocked() {
195 assert!(validate_url("http://100.64.0.1/").is_err());
196 assert!(validate_url("http://100.127.255.255/").is_err());
197 }
198
199 #[test]
200 fn test_ipv6_multicast_blocked() {
201 assert!(validate_url("http://[ff00::1]/").is_err());
202 assert!(validate_url("http://[ff02::1]/").is_err());
203 }
204}