@@ -88,7 +88,7 @@ maybe_file(Req, Prefix, Dir) ->
88
88
89
89
% % santize the path ensuring the request doesn't access any parent
90
90
% % directories ... and reattach the slash if deemed safe
91
- SafePath = case filename : safe_relative_path (RawPath ) of
91
+ SafePath = case safe_relative_path (RawPath ) of
92
92
unsafe ->
93
93
throw (? NOT_FOUND );
94
94
% % return type quirk work around
@@ -110,3 +110,77 @@ maybe_file(Req, Prefix, Dir) ->
110
110
_ ->
111
111
nothing
112
112
end .
113
+
114
+ % % @doc Backport of `filename:safe_relative_path/1' from 19.3. This code was
115
+ % % lifted from:
116
+ % % https://github.com/erlang/otp/blob/master/lib/stdlib/src/filename.erl#L811
117
+ -spec safe_relative_path (file :name_all ()) -> unsafe | file :name_all ().
118
+ safe_relative_path (Path ) ->
119
+ % % prefer the OTP version of `safe_relative_path/1' if it exists
120
+ case lists :member ({safe_relative_path , 1 },
121
+ filename :module_info (exports )) of
122
+ true ->
123
+ % % to suppress xref warnings on 19.2 and lower
124
+ Mod = filename ,
125
+ Mod :safe_relative_path (Path );
126
+ false ->
127
+ case filename :pathtype (Path ) of
128
+ relative ->
129
+ Cs0 = filename :split (Path ),
130
+ safe_relative_path_1 (Cs0 , []);
131
+ _ ->
132
+ unsafe
133
+ end
134
+ end .
135
+
136
+ safe_relative_path_1 ([" ." |T ], Acc ) ->
137
+ safe_relative_path_1 (T , Acc );
138
+ safe_relative_path_1 ([<<" ." >>|T ], Acc ) ->
139
+ safe_relative_path_1 (T , Acc );
140
+ safe_relative_path_1 ([" .." |T ], Acc ) ->
141
+ climb (T , Acc );
142
+ safe_relative_path_1 ([<<" .." >>|T ], Acc ) ->
143
+ climb (T , Acc );
144
+ safe_relative_path_1 ([H |T ], Acc ) ->
145
+ safe_relative_path_1 (T , [H |Acc ]);
146
+ safe_relative_path_1 ([], []) ->
147
+ [];
148
+ safe_relative_path_1 ([], Acc ) ->
149
+ filename :join (lists :reverse (Acc )).
150
+
151
+ climb (_ , []) ->
152
+ unsafe ;
153
+ climb (T , [_ |Acc ]) ->
154
+ safe_relative_path_1 (T , Acc ).
155
+
156
+
157
+ -ifdef (TEST ).
158
+ -include_lib (" eunit/include/eunit.hrl" ).
159
+
160
+ safe_relative_path_test () ->
161
+ % % binary args
162
+ ? assertMatch ([], safe_relative_path (<<>>)),
163
+ ? assertMatch (unsafe , safe_relative_path (<<" /" >>)),
164
+ ? assertMatch (unsafe , safe_relative_path (<<" /root" >>)),
165
+ ? assertMatch (unsafe , safe_relative_path (<<" .." >>)),
166
+ ? assertMatch (unsafe , safe_relative_path (<<" dir/../../dir" >>)),
167
+ ? assertMatch (<<" dir" >>, safe_relative_path (<<" dir" >>)),
168
+ ? assertMatch (<<" dir" >>, safe_relative_path (<<" dir/../dir" >>)),
169
+ ? assertMatch (<<" dir/sub" >>, safe_relative_path (<<" dir/sub/../sub" >>)),
170
+ ? assertMatch (<<" dir/sub" >>, safe_relative_path (<<" dir/./sub" >>)),
171
+ ? assertMatch (<<" dir/sub" >>, safe_relative_path (<<" dir/././../dir/././sub" >>)),
172
+
173
+ % % string args
174
+ % % (these are here for completeness since `filename:safe_relative_path'
175
+ % % supports string arguments
176
+ ? assertMatch ([], safe_relative_path (" " )),
177
+ ? assertMatch (unsafe , safe_relative_path (" /" )),
178
+ ? assertMatch (unsafe , safe_relative_path (" /root" )),
179
+ ? assertMatch (unsafe , safe_relative_path (" .." )),
180
+ ? assertMatch (unsafe , safe_relative_path (" dir/../../dir" )),
181
+ ? assertMatch (" dir" , safe_relative_path (" dir" )),
182
+ ? assertMatch (" dir" , safe_relative_path (" dir/../dir" )),
183
+ ? assertMatch (" dir/sub" , safe_relative_path (" dir/sub/../sub" )),
184
+ ? assertMatch (" dir/sub" , safe_relative_path (" dir/./sub" )),
185
+ ? assertMatch (" dir/sub" , safe_relative_path (" dir/././../dir/././sub" )).
186
+ -endif .
0 commit comments