1
+ use anyhow:: { anyhow, Context } ;
1
2
use ipnetwork:: IpNetwork ;
2
3
3
4
use crate :: publish_rate_limit:: PublishRateLimit ;
@@ -94,7 +95,11 @@ impl Default for Server {
94
95
match env_optional :: < String > ( "WEB_PAGE_OFFSET_CIDR_BLOCKLIST" ) {
95
96
None => vec ! [ ] ,
96
97
Some ( s) if s. is_empty ( ) => vec ! [ ] ,
97
- Some ( s) => s. split ( ',' ) . map ( String :: from) . collect ( ) ,
98
+ Some ( s) => s
99
+ . split ( ',' )
100
+ . map ( parse_cidr_block)
101
+ . collect :: < Result < _ , _ > > ( )
102
+ . unwrap ( ) ,
98
103
} ;
99
104
100
105
let base = Base :: from_environment ( ) ;
@@ -116,7 +121,7 @@ impl Default for Server {
116
121
blocked_traffic : blocked_traffic ( ) ,
117
122
max_allowed_page_offset : env_optional ( "WEB_MAX_ALLOWED_PAGE_OFFSET" ) . unwrap_or ( 200 ) ,
118
123
page_offset_ua_blocklist,
119
- page_offset_cidr_blocklist : parse_cidr_blocks ( & page_offset_cidr_blocklist ) ,
124
+ page_offset_cidr_blocklist,
120
125
excluded_crate_names,
121
126
domain_name : domain_name ( ) ,
122
127
allowed_origins,
@@ -158,7 +163,7 @@ pub(crate) fn domain_name() -> String {
158
163
dotenv:: var ( "DOMAIN_NAME" ) . unwrap_or_else ( |_| "crates.io" . into ( ) )
159
164
}
160
165
161
- /// Parses list of CIDR block strings to valid `IpNetwork` structs .
166
+ /// Parses a CIDR block string to a valid `IpNetwork` struct .
162
167
///
163
168
/// The purpose is to be able to block IP ranges that overload the API that uses pagination.
164
169
///
@@ -167,30 +172,21 @@ pub(crate) fn domain_name() -> String {
167
172
/// * at least 16 for IPv4 based CIDRs.
168
173
/// * at least 64 for IPv6 based CIDRs
169
174
///
170
- fn parse_cidr_blocks ( blocks : & [ String ] ) -> Vec < IpNetwork > {
171
- blocks
172
- . iter ( )
173
- . map ( |block| {
174
- let network = block. parse :: < IpNetwork > ( ) ;
175
- match network {
176
- Ok ( cidr) => {
177
- let host_prefix = match cidr {
178
- IpNetwork :: V4 ( _) => 16 ,
179
- IpNetwork :: V6 ( _) => 64 ,
180
- } ;
181
- if cidr. prefix ( ) < host_prefix {
182
- panic ! (
183
- "WEB_PAGE_OFFSET_CIDR_BLOCKLIST only allows CIDR blocks with a host prefix \
184
- of at least 16 bits (IPv4) or 64 bits (IPv6)."
185
- ) ;
186
- } else {
187
- cidr
188
- }
189
- } ,
190
- Err ( _) => panic ! ( "WEB_PAGE_OFFSET_CIDR_BLOCKLIST must contain IPv4 or IPv6 CIDR blocks." ) ,
191
- }
192
- } )
193
- . collect :: < Vec < _ > > ( )
175
+ fn parse_cidr_block ( block : & str ) -> anyhow:: Result < IpNetwork > {
176
+ let cidr = block
177
+ . parse ( )
178
+ . context ( "WEB_PAGE_OFFSET_CIDR_BLOCKLIST must contain IPv4 or IPv6 CIDR blocks." ) ?;
179
+
180
+ let host_prefix = match cidr {
181
+ IpNetwork :: V4 ( _) => 16 ,
182
+ IpNetwork :: V6 ( _) => 64 ,
183
+ } ;
184
+
185
+ if cidr. prefix ( ) < host_prefix {
186
+ return Err ( anyhow ! ( "WEB_PAGE_OFFSET_CIDR_BLOCKLIST only allows CIDR blocks with a host prefix of at least 16 bits (IPv4) or 64 bits (IPv6)." ) ) ;
187
+ }
188
+
189
+ Ok ( cidr)
194
190
}
195
191
196
192
fn blocked_traffic ( ) -> Vec < ( String , Vec < String > ) > {
@@ -235,35 +231,40 @@ fn parse_traffic_patterns_splits_on_comma_and_looks_for_equal_sign() {
235
231
236
232
#[ test]
237
233
fn parse_cidr_block_list_successfully ( ) {
238
- let cidr_blocks = vec ! [ "127.0.0.1/24" . to_string( ) , "192.168.0.1/31" . to_string( ) ] ;
239
-
240
- let blocks = parse_cidr_blocks ( & cidr_blocks) ;
241
- assert_eq ! (
242
- vec![
243
- "127.0.0.1/24" . parse:: <IpNetwork >( ) . unwrap( ) ,
244
- "192.168.0.1/31" . parse:: <IpNetwork >( ) . unwrap( ) ,
245
- ] ,
246
- blocks,
234
+ assert_ok_eq ! (
235
+ parse_cidr_block( "127.0.0.1/24" ) ,
236
+ "127.0.0.1/24" . parse:: <IpNetwork >( ) . unwrap( )
237
+ ) ;
238
+ assert_ok_eq ! (
239
+ parse_cidr_block( "192.168.0.1/31" ) ,
240
+ "192.168.0.1/31" . parse:: <IpNetwork >( ) . unwrap( )
247
241
) ;
248
242
}
249
243
250
244
#[ test]
251
- #[ should_panic]
252
245
fn parse_cidr_blocks_panics_when_host_ipv4_prefix_is_too_low ( ) {
253
- parse_cidr_blocks ( & [ "127.0.0.1/8" . to_string ( ) ] ) ;
246
+ assert_err ! ( parse_cidr_block ( "127.0.0.1/8" ) ) ;
254
247
}
255
248
256
249
#[ test]
257
- #[ should_panic]
258
250
fn parse_cidr_blocks_panics_when_host_ipv6_prefix_is_too_low ( ) {
259
- parse_cidr_blocks ( & [ "2001:0db8:0123:4567:89ab:cdef:1234:5678/56" . to_string ( ) ] ) ;
251
+ assert_err ! ( parse_cidr_block(
252
+ "2001:0db8:0123:4567:89ab:cdef:1234:5678/56"
253
+ ) ) ;
260
254
}
261
255
262
256
#[ test]
263
257
fn parse_ipv6_based_cidr_blocks ( ) {
264
- let input = vec ! [
265
- "2002::1234:abcd:ffff:c0a8:101/64" . to_string( ) ,
266
- "2001:0db8:0123:4567:89ab:cdef:1234:5678/92" . to_string( ) ,
267
- ] ;
268
- assert_eq ! ( 2 , parse_cidr_blocks( & input) . len( ) ) ;
258
+ assert_ok_eq ! (
259
+ parse_cidr_block( "2002::1234:abcd:ffff:c0a8:101/64" ) ,
260
+ "2002::1234:abcd:ffff:c0a8:101/64"
261
+ . parse:: <IpNetwork >( )
262
+ . unwrap( )
263
+ ) ;
264
+ assert_ok_eq ! (
265
+ parse_cidr_block( "2001:0db8:0123:4567:89ab:cdef:1234:5678/92" ) ,
266
+ "2001:0db8:0123:4567:89ab:cdef:1234:5678/92"
267
+ . parse:: <IpNetwork >( )
268
+ . unwrap( )
269
+ ) ;
269
270
}
0 commit comments