@@ -27,23 +27,25 @@ class ImageTransformerController extends \Illuminate\Routing\Controller
2727{
2828 use ResolvesOptions;
2929
30- public function __invoke (Request $ request , string $ options , string $ path )
30+ public function transformWithPrefix (Request $ request, string $ pathPrefix , string $ options , string $ path )
3131 {
32- $ pathPrefix = config ()->string ('image-transform-url.public_path ' );
33-
34- $ publicPath = realpath (public_path ($ pathPrefix .'/ ' .$ path ));
35-
36- abort_unless ($ publicPath , 404 );
32+ return $ this ->handleTransform ($ request , $ pathPrefix , $ options , $ path );
33+ }
3734
38- abort_unless (Str::startsWith ($ publicPath , public_path ($ pathPrefix )), 404 );
35+ public function transformDefault (Request $ request , string $ options , string $ path )
36+ {
37+ return $ this ->handleTransform ($ request , null , $ options , $ path );
38+ }
3939
40- abort_unless (in_array (File::mimeType ($ publicPath ), AllowedMimeTypes::all (), true ), 404 );
40+ protected function handleTransform (Request $ request , ?string $ pathPrefix , string $ options , ?string $ path = null )
41+ {
42+ $ realPath = $ this ->handlePath ($ pathPrefix , $ path );
4143
4244 $ options = $ this ->parseOptions ($ options );
4345
4446 // Check cache
4547 if (config ()->boolean ('image-transform-url.cache.enabled ' )) {
46- $ cachePath = $ this ->getCachePath ($ path , $ options );
48+ $ cachePath = $ this ->getCachePath ($ pathPrefix , $ path , $ options );
4749
4850 if (File::exists ($ cachePath )) {
4951 if (Cache::has ('image-transform-url: ' .$ cachePath )) {
@@ -66,7 +68,7 @@ public function __invoke(Request $request, string $options, string $path)
6668 $ this ->rateLimit ($ request , $ path );
6769 }
6870
69- $ image = Image::read ($ publicPath );
71+ $ image = Image::read ($ realPath );
7072
7173 if (Arr::hasAny ($ options , ['width ' , 'height ' ])) {
7274 $ image ->scale (
@@ -108,7 +110,7 @@ public function __invoke(Request $request, string $options, string $path)
108110 }
109111
110112 // We use the mime type instead of the extension to determine the format, because this is more reliable.
111- $ originalMimetype = File::mimeType ($ publicPath );
113+ $ originalMimetype = File::mimeType ($ realPath );
112114
113115 $ format = $ this ->getStringOptionValue ($ options , 'format ' , $ originalMimetype );
114116 $ quality = $ this ->getPositiveIntOptionValue ($ options , 'quality ' , 100 , 100 );
@@ -124,9 +126,9 @@ public function __invoke(Request $request, string $options, string $path)
124126 $ encoded = $ image ->encode ($ encoder );
125127
126128 if (config ()->boolean ('image-transform-url.cache.enabled ' )) {
127- defer (function () use ($ path , $ options , $ encoded ) {
129+ defer (function () use ($ pathPrefix , $ path , $ options , $ encoded ) {
128130
129- $ cachePath = $ this ->getCachePath ($ path , $ options );
131+ $ cachePath = $ this ->getCachePath ($ pathPrefix , $ path , $ options );
130132
131133 $ cacheDir = dirname ($ cachePath );
132134
@@ -149,6 +151,40 @@ public function __invoke(Request $request, string $options, string $path)
149151
150152 }
151153
154+ /**
155+ * Handle the path and ensure it is valid.
156+ */
157+ protected function handlePath (?string &$ pathPrefix , ?string &$ path ): string
158+ {
159+ if ($ path === null ) {
160+ $ path = $ pathPrefix ;
161+ $ pathPrefix = null ;
162+ }
163+
164+ $ allowedSourceDirectories = config ('image-transform-url.source_directories ' , []);
165+
166+ if (! $ pathPrefix ) {
167+ $ pathPrefix = config ('image-transform-url.default_source_directory ' ) ?? array_key_first ($ allowedSourceDirectories );
168+ }
169+
170+ abort_unless (array_key_exists ($ pathPrefix , $ allowedSourceDirectories ), 404 );
171+
172+ $ basePath = $ allowedSourceDirectories [$ pathPrefix ];
173+ $ requestedPath = $ basePath .'/ ' .$ path ;
174+ $ realPath = realpath ($ requestedPath );
175+
176+ abort_unless ($ realPath , 404 );
177+
178+ $ allowedBasePath = realpath ($ basePath );
179+ abort_unless ($ allowedBasePath , 404 );
180+
181+ abort_unless (Str::startsWith ($ realPath , $ allowedBasePath ), 404 );
182+
183+ abort_unless (in_array (File::mimeType ($ realPath ), AllowedMimeTypes::all (), true ), 404 );
184+
185+ return $ realPath ;
186+ }
187+
152188 /**
153189 * Rate limit the request.
154190 */
@@ -202,10 +238,8 @@ protected static function parseOptions(string $options): array
202238 /**
203239 * Get the cache path for the given path and options.
204240 */
205- protected static function getCachePath (string $ path , array $ options ): string
241+ protected static function getCachePath (string $ pathPrefix , string $ path , array $ options ): string
206242 {
207- $ pathPrefix = config ()->string ('image-transform-url.public_path ' );
208-
209243 $ optionsHash = md5 (json_encode ($ options ));
210244
211245 return Storage::disk (config ()->string ('image-transform-url.cache.disk ' ))->path ('_cache/image-transform-url/ ' .$ pathPrefix .'/ ' .$ optionsHash .'_ ' .$ path );
0 commit comments