@@ -7,7 +7,6 @@ use iron::{
7
7
IronResult , Request , Response , Url ,
8
8
} ;
9
9
use mime_guess:: MimeGuess ;
10
- use router:: Router ;
11
10
use std:: { ffi:: OsStr , fs, path:: Path } ;
12
11
13
12
const VENDORED_CSS : & str = include_str ! ( concat!( env!( "OUT_DIR" ) , "/vendored.css" ) ) ;
@@ -16,10 +15,11 @@ const RUSTDOC_CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/rustdoc.css"))
16
15
const STATIC_SEARCH_PATHS : & [ & str ] = & [ "vendor/pure-css/css" , "static" ] ;
17
16
18
17
pub ( crate ) fn static_handler ( req : & mut Request ) -> IronResult < Response > {
19
- let router = extension ! ( req, Router ) ;
20
- let file = cexpect ! ( req, router. find( "file" ) ) ;
18
+ let mut file = req. url . path ( ) ;
19
+ file. drain ( ..2 ) . for_each ( std:: mem:: drop) ;
20
+ let file = file. join ( "/" ) ;
21
21
22
- Ok ( match file {
22
+ Ok ( match file. as_str ( ) {
23
23
"vendored.css" => serve_resource ( VENDORED_CSS , ContentType ( "text/css" . parse ( ) . unwrap ( ) ) ) ?,
24
24
"style.css" => serve_resource ( STYLE_CSS , ContentType ( "text/css" . parse ( ) . unwrap ( ) ) ) ?,
25
25
"rustdoc.css" => serve_resource ( RUSTDOC_CSS , ContentType ( "text/css" . parse ( ) . unwrap ( ) ) ) ?,
@@ -28,15 +28,21 @@ pub(crate) fn static_handler(req: &mut Request) -> IronResult<Response> {
28
28
}
29
29
30
30
fn serve_file ( req : & Request , file : & str ) -> IronResult < Response > {
31
- // Filter out files that attempt to traverse directories
32
- if file. contains ( ".." ) || file. contains ( '/' ) || file. contains ( '\\' ) {
33
- return Err ( Nope :: ResourceNotFound . into ( ) ) ;
34
- }
35
-
36
31
// Find the first path that actually exists
37
32
let path = STATIC_SEARCH_PATHS
38
33
. iter ( )
39
- . map ( |p| Path :: new ( p) . join ( file) )
34
+ . filter_map ( |root| {
35
+ let path = Path :: new ( root) . join ( file) ;
36
+
37
+ // Prevent accessing static files outside the root. This could happen if the path
38
+ // contains `/` or `..`. The check doesn't outright prevent those strings to be present
39
+ // to allow accessing files in subdirectories.
40
+ if path. starts_with ( root) {
41
+ Some ( path)
42
+ } else {
43
+ None
44
+ }
45
+ } )
40
46
. find ( |p| p. exists ( ) )
41
47
. ok_or ( Nope :: ResourceNotFound ) ?;
42
48
let contents = ctry ! ( req, fs:: read( & path) ) ;
@@ -199,11 +205,15 @@ mod tests {
199
205
wrapper ( |env| {
200
206
let web = env. frontend ( ) ;
201
207
202
- for path in STATIC_SEARCH_PATHS {
203
- for ( file, path) in fs:: read_dir ( path) ?
204
- . map ( |e| e. unwrap ( ) )
205
- . map ( |e| ( e. file_name ( ) , e. path ( ) ) )
206
- {
208
+ for root in STATIC_SEARCH_PATHS {
209
+ for entry in walkdir:: WalkDir :: new ( root) {
210
+ let entry = entry?;
211
+ if !entry. file_type ( ) . is_file ( ) {
212
+ continue ;
213
+ }
214
+ let file = entry. path ( ) . strip_prefix ( root) . unwrap ( ) ;
215
+ let path = entry. path ( ) ;
216
+
207
217
let url = format ! ( "/-/static/{}" , file. to_str( ) . unwrap( ) ) ;
208
218
let resp = web. get ( & url) . send ( ) ?;
209
219
0 commit comments