@@ -4,6 +4,7 @@ use crate::{Body, Endpoint, Request, Response, Result, StatusCode};
4
4
use async_std:: fs:: File ;
5
5
use async_std:: io:: BufReader ;
6
6
7
+ use std:: ffi:: OsStr ;
7
8
use std:: path:: { Path , PathBuf } ;
8
9
9
10
type BoxFuture < ' a , T > = std:: pin:: Pin < Box < dyn std:: future:: Future < Output = T > + ' a + Send > > ;
@@ -22,43 +23,38 @@ impl ServeDir {
22
23
impl < State > Endpoint < State > for ServeDir {
23
24
fn call < ' a > ( & ' a self , req : Request < State > ) -> BoxFuture < ' a , Result > {
24
25
let path = req. uri ( ) . path ( ) ;
25
- let path = path. replacen ( & self . prefix , "" , 1 ) ;
26
+ let path = path. trim_start_matches ( & self . prefix ) ;
26
27
let path = path. trim_start_matches ( '/' ) ;
27
- let mut dir = self . dir . clone ( ) ;
28
+ let mut file_path = self . dir . clone ( ) ;
28
29
for p in Path :: new ( path) {
29
- dir. push ( & p) ;
30
+ if p == OsStr :: new ( "." ) {
31
+ continue ;
32
+ } else if p == OsStr :: new ( ".." ) {
33
+ file_path. pop ( ) ;
34
+ } else {
35
+ file_path. push ( & p) ;
36
+ }
30
37
}
31
- log:: info!( "Requested file: {:?}" , dir) ;
38
+
39
+ log:: info!( "Requested file: {:?}" , file_path) ;
32
40
33
41
Box :: pin ( async move {
34
- let file = match async_std:: fs:: canonicalize ( & dir) . await {
35
- Err ( _) => {
36
- // This needs to return the same status code as the
37
- // unauthorized case below to ensure we don't leak
38
- // information of which files exist to adversaries.
39
- log:: warn!( "File not found: {:?}" , dir) ;
40
- return Ok ( Response :: new ( StatusCode :: NotFound ) ) ;
41
- }
42
- Ok ( mut file_path) => {
43
- // Verify this is a sub-path of the original dir.
44
- let mut file_iter = ( & mut file_path) . iter ( ) ;
45
- if !dir. iter ( ) . all ( |lhs| Some ( lhs) == file_iter. next ( ) ) {
46
- // This needs to return the same status code as the
47
- // 404 case above to ensure we don't leak
48
- // information about the local fs to adversaries.
49
- log:: warn!( "Unauthorized attempt to read: {:?}" , file_path) ;
50
- return Ok ( Response :: new ( StatusCode :: NotFound ) ) ;
51
- }
42
+ if !file_path. starts_with ( & self . dir ) {
43
+ log:: warn!( "Unauthorized attempt to read: {:?}" , file_path) ;
44
+ return Ok ( Response :: new ( StatusCode :: Forbidden ) ) ;
45
+ }
52
46
53
- // Open the file and send back the contents.
54
- match File :: open ( & file_path) . await {
55
- Ok ( file) => file,
56
- Err ( _) => {
57
- log:: warn!( "Could not open {:?}" , file_path) ;
58
- return Ok ( Response :: new ( StatusCode :: InternalServerError ) ) ;
59
- }
47
+ let file = match File :: open ( & file_path) . await {
48
+ Err ( error) => {
49
+ return if error. kind ( ) == async_std:: io:: ErrorKind :: NotFound {
50
+ log:: warn!( "File not found: {:?}" , file_path) ;
51
+ Ok ( Response :: new ( StatusCode :: NotFound ) )
52
+ } else {
53
+ log:: warn!( "Could not open {:?}" , file_path) ;
54
+ Ok ( Response :: new ( StatusCode :: InternalServerError ) )
60
55
}
61
56
}
57
+ Ok ( file) => file,
62
58
} ;
63
59
64
60
let len = match file. metadata ( ) . await {
0 commit comments