Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
espresso
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Container Registry
Model registry
Analyze
Contributor analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Eduardo Trujillo
espresso
Commits
80e94329
Verified
Commit
80e94329
authored
2 years ago
by
Eduardo Trujillo
Browse files
Options
Downloads
Patches
Plain Diff
feat(files): Add support for percent-encoding in paths
parent
88509d4f
No related branches found
Branches containing commit
No related tags found
1 merge request
!3
v2.0.0
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/files/pathbuf.rs
+75
-41
75 additions, 41 deletions
src/files/pathbuf.rs
with
75 additions
and
41 deletions
src/files/pathbuf.rs
+
75
−
41
View file @
80e94329
...
...
@@ -4,11 +4,13 @@ use actix_web::{
FromRequest
,
HttpRequest
,
ResponseError
,
};
use
futures_util
::
future
::{
ready
,
Ready
};
use
percent_encoding
::
percent_decode_str
;
use
snafu
::
Snafu
;
use
std
::{
convert
::{
TryFrom
,
TryInto
},
ops
::
Deref
,
path
::{
Path
,
PathBuf
},
path
::{
Component
,
Path
,
PathBuf
},
str
::
FromStr
,
};
#[derive(Snafu,
Debug,
PartialEq)]
...
...
@@ -22,6 +24,12 @@ pub enum Error {
/// The segment ended with the wrapped invalid character.
#[snafu(display(
"The segment ended with the wrapped invalid character."
))]
BadEnd
{
char
:
char
},
/// The path is not a valid UTF-8 string after percent-decoding
#[snafu(display(
"The path is not a valid UTF-8 string after percent-decoding."
))]
NotValidUtf8
,
/// The path has invalid or unexpected components.
#[snafu(display(
"The path has invalid or unexpected components."
))]
InvalidComponents
,
}
/// Return `BadRequest` for `Error`.
...
...
@@ -37,12 +45,23 @@ type Result<T, E = Error> = std::result::Result<T, E>;
pub
struct
UriPathBuf
(
PathBuf
);
impl
UriPathBuf
{
pub
fn
new
(
path
:
&
str
)
->
Result
<
Self
>
{
let
mut
buf
=
PathBuf
::
new
();
pub
fn
new
(
raw_
path
:
&
str
)
->
Result
<
Self
>
{
let
mut
path
=
PathBuf
::
new
();
for
segment
in
path
.split
(
'/'
)
{
let
mut
segment_count
=
raw_path
.matches
(
'/'
)
.count
()
+
1
;
let
decoded_path
=
percent_decode_str
(
raw_path
)
.decode_utf8
()
.map_err
(|
_
|
Error
::
NotValidUtf8
)
?
;
if
segment_count
!=
decoded_path
.matches
(
'/'
)
.count
()
+
1
{
return
Err
(
Error
::
BadChar
{
char
:
'/'
});
}
for
segment
in
decoded_path
.split
(
'/'
)
{
if
segment
==
".."
{
buf
.pop
();
segment_count
-=
1
;
path
.pop
();
}
else
if
segment
.starts_with
(
'.'
)
{
return
Err
(
Error
::
BadStart
{
char
:
'.'
});
}
else
if
segment
.starts_with
(
'*'
)
{
...
...
@@ -54,15 +73,32 @@ impl UriPathBuf {
}
else
if
segment
.ends_with
(
'<'
)
{
return
Err
(
Error
::
BadEnd
{
char
:
'<'
});
}
else
if
segment
.is_empty
()
{
segment_count
-=
1
;
continue
;
}
else
if
cfg!
(
windows
)
&&
segment
.contains
(
'\\'
)
{
return
Err
(
Error
::
BadChar
{
char
:
'\\'
});
}
else
if
cfg!
(
windows
)
&&
segment
.contains
(
':'
)
{
return
Err
(
Error
::
BadChar
{
char
:
':'
});
}
else
{
buf
.push
(
segment
)
path
.push
(
segment
)
}
}
for
(
i
,
component
)
in
path
.components
()
.enumerate
()
{
if
!
matches!
(
component
,
Component
::
Normal
(
_
))
||
i
>=
segment_count
{
return
Err
(
Error
::
InvalidComponents
);
}
}
Ok
(
UriPathBuf
(
buf
))
Ok
(
UriPathBuf
(
path
))
}
}
impl
FromStr
for
UriPathBuf
{
type
Err
=
Error
;
fn
from_str
(
raw_path
:
&
str
)
->
Result
<
Self
,
Self
::
Err
>
{
Self
::
new
(
raw_path
)
}
}
...
...
@@ -70,8 +106,8 @@ impl FromRequest for UriPathBuf {
type
Error
=
Error
;
type
Future
=
Ready
<
Result
<
Self
,
Self
::
Error
>>
;
fn
from_request
(
req
:
&
HttpRequest
,
_
:
&
mut
Payload
)
->
Self
::
Future
{
ready
(
req
.try_into
())
fn
from_request
(
req
uest
:
&
HttpRequest
,
_
:
&
mut
Payload
)
->
Self
::
Future
{
ready
(
req
uest
.try_into
())
}
}
...
...
@@ -79,7 +115,7 @@ impl TryFrom<&HttpRequest> for UriPathBuf {
type
Error
=
Error
;
fn
try_from
(
request
:
&
HttpRequest
)
->
Result
<
Self
,
Error
>
{
UriPathBuf
::
new
(
request
.match_info
()
.
path
()
)
request
.match_info
()
.
unprocessed
()
.parse
(
)
}
}
...
...
@@ -87,13 +123,13 @@ impl TryFrom<&ServiceRequest> for UriPathBuf {
type
Error
=
Error
;
fn
try_from
(
request
:
&
ServiceRequest
)
->
Result
<
Self
,
Error
>
{
UriPathBuf
::
new
(
request
.match_info
()
.
path
()
)
request
.match_info
()
.
unprocessed
()
.parse
(
)
}
}
impl
From
<
UriPathBuf
>
for
PathBuf
{
fn
from
(
buf
:
UriPathBuf
)
->
PathBuf
{
buf
.0
fn
from
(
path
:
UriPathBuf
)
->
PathBuf
{
path
.0
}
}
...
...
@@ -110,33 +146,31 @@ mod tests {
#[actix_rt::test]
async
fn
test_path_buf
()
{
assert_eq!
(
UriPathBuf
::
new
(
"/test/.tt"
)
.map
(|
t
|
t
.0
),
Err
(
Error
::
BadStart
{
char
:
'.'
})
);
assert_eq!
(
UriPathBuf
::
new
(
"/test/*tt"
)
.map
(|
t
|
t
.0
),
Err
(
Error
::
BadStart
{
char
:
'*'
})
);
assert_eq!
(
UriPathBuf
::
new
(
"/test/tt:"
)
.map
(|
t
|
t
.0
),
Err
(
Error
::
BadEnd
{
char
:
':'
})
);
assert_eq!
(
UriPathBuf
::
new
(
"/test/tt<"
)
.map
(|
t
|
t
.0
),
Err
(
Error
::
BadEnd
{
char
:
'<'
})
);
assert_eq!
(
UriPathBuf
::
new
(
"/test/tt>"
)
.map
(|
t
|
t
.0
),
Err
(
Error
::
BadEnd
{
char
:
'>'
})
);
assert_eq!
(
UriPathBuf
::
new
(
"/seg1/seg2/"
)
.unwrap
()
.0
,
PathBuf
::
from_iter
(
vec!
[
"seg1"
,
"seg2"
])
);
assert_eq!
(
UriPathBuf
::
new
(
"/seg1/../seg2/"
)
.unwrap
()
.0
,
PathBuf
::
from_iter
(
vec!
[
"seg2"
])
);
let
cases
:
&
[(
&
str
,
Result
<
PathBuf
,
Error
>
)]
=
&
[
(
"/test/.tt"
,
Err
(
Error
::
BadStart
{
char
:
'.'
})),
(
"/test/*tt"
,
Err
(
Error
::
BadStart
{
char
:
'*'
})),
(
"/test/tt:"
,
Err
(
Error
::
BadEnd
{
char
:
':'
})),
(
"/test/tt<"
,
Err
(
Error
::
BadEnd
{
char
:
'<'
})),
(
"/test/tt>"
,
Err
(
Error
::
BadEnd
{
char
:
'>'
})),
(
"hello%20world"
,
Ok
(
PathBuf
::
from_iter
(
vec!
[
"hello world"
]))),
(
"/testing%21/hello%20world"
,
Ok
(
PathBuf
::
from_iter
(
vec!
[
"testing!"
,
"hello world"
])),
),
(
"/seg1/seg2/"
,
Ok
(
PathBuf
::
from_iter
(
vec!
[
"seg1"
,
"seg2"
]))),
(
"/seg1/../seg2/"
,
Ok
(
PathBuf
::
from_iter
(
vec!
[
"seg2"
]))),
(
"/../../../../../dev/null"
,
Ok
(
PathBuf
::
from_iter
(
vec!
[
"dev/null"
])),
),
(
"/../../../..%2F../dev/null"
,
Err
(
Error
::
BadChar
{
char
:
'/'
}),
),
];
for
(
input
,
expected
)
in
cases
{
assert_eq!
(
&
UriPathBuf
::
new
(
input
)
.map
(|
t
|
t
.0
),
expected
)
}
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment