@@ -607,6 +607,33 @@ private static function isSafeSegment(string $name): bool
607607 $ len = mb_strlen ($ name );
608608 return $ len > 0 && $ len <= 255 ;
609609 }
610+
611+ private static function isValidFolderSegment (string $ name ): bool
612+ {
613+ return self ::isSafeSegment ($ name ) && preg_match (REGEX_FOLDER_NAME , $ name ) === 1 ;
614+ }
615+
616+ private static function splitFolderSegments (string $ folder , bool $ allowRoot = true ): ?array
617+ {
618+ $ normalized = ACL ::normalizeFolder ($ folder );
619+ if ($ normalized === 'root ' ) {
620+ return $ allowRoot ? [] : null ;
621+ }
622+
623+ $ parts = explode ('/ ' , $ normalized );
624+ if ($ parts === [] || in_array ('' , $ parts , true )) {
625+ return null ;
626+ }
627+
628+ foreach ($ parts as $ seg ) {
629+ if (!self ::isValidFolderSegment ($ seg )) {
630+ return null ;
631+ }
632+ }
633+
634+ return $ parts ;
635+ }
636+
610637 private static function safeReal (string $ baseReal , string $ p ): ?string
611638 {
612639 $ rp = realpath ($ p );
@@ -1094,16 +1121,10 @@ private static function resolveFolderPath(string $folder, bool $create = false):
10941121 if (strtolower ($ folder ) === 'root ' ) {
10951122 $ dir = $ base ;
10961123 } else {
1097- // validate each segment against REGEX_FOLDER_NAME
1098- $ parts = array_filter (explode ('/ ' , trim ($ folder , "/ \\ " )), fn ($ p ) => $ p !== '' );
1099- if (empty ($ parts )) {
1124+ $ parts = self ::splitFolderSegments ($ folder );
1125+ if ($ parts === null || $ parts === []) {
11001126 return [null , 'root ' , "Invalid folder name. " ];
11011127 }
1102- foreach ($ parts as $ seg ) {
1103- if (!preg_match (REGEX_FOLDER_NAME , $ seg )) {
1104- return [null , 'root ' , "Invalid folder name. " ];
1105- }
1106- }
11071128 $ relative = implode ('/ ' , $ parts );
11081129 $ dir = $ base . DIRECTORY_SEPARATOR . implode (DIRECTORY_SEPARATOR , $ parts );
11091130 }
@@ -1145,15 +1166,10 @@ private static function resolveFolderPathForAdapter(StorageAdapterInterface $sto
11451166 if (strtolower ($ folder ) === 'root ' ) {
11461167 $ dir = $ base ;
11471168 } else {
1148- $ parts = array_filter ( explode ( ' / ' , trim ( $ folder, " / \\ " )), fn ( $ p ) => $ p !== '' );
1149- if (empty ( $ parts) ) {
1169+ $ parts = self :: splitFolderSegments ( $ folder );
1170+ if ($ parts === null || $ parts === [] ) {
11501171 return [null , 'root ' , "Invalid folder name. " ];
11511172 }
1152- foreach ($ parts as $ seg ) {
1153- if (!preg_match (REGEX_FOLDER_NAME , $ seg )) {
1154- return [null , 'root ' , "Invalid folder name. " ];
1155- }
1156- }
11571173 $ relative = implode ('/ ' , $ parts );
11581174 $ dir = $ base . DIRECTORY_SEPARATOR . implode (DIRECTORY_SEPARATOR , $ parts );
11591175 }
@@ -1533,14 +1549,25 @@ public static function createFolder(string $folderName, string $parent, string $
15331549 return ['success ' => false , 'error ' => 'Folder name required ' ];
15341550 }
15351551
1552+ $ parentParts = self ::splitFolderSegments ($ parent );
1553+ if ($ parentParts === null ) {
1554+ return ['success ' => false , 'error ' => 'Invalid parent folder name ' ];
1555+ }
1556+ $ folderParts = self ::splitFolderSegments ($ folderName , false );
1557+ if ($ folderParts === null || count ($ folderParts ) !== 1 ) {
1558+ return ['success ' => false , 'error ' => 'Invalid folder name ' ];
1559+ }
1560+
1561+ $ parent = $ parentParts === [] ? 'root ' : implode ('/ ' , $ parentParts );
1562+ $ folderName = $ folderParts [0 ];
1563+
15361564 // ACL key for new folder
15371565 $ newKey = ($ parent === 'root ' ) ? $ folderName : ($ parent . '/ ' . $ folderName );
15381566
15391567 // -------- Compose filesystem paths --------
15401568 $ base = rtrim (self ::uploadRoot (), "/ \\" );
15411569 $ parentRel = ($ parent === 'root ' ) ? '' : str_replace ('/ ' , DIRECTORY_SEPARATOR , $ parent );
15421570 $ parentAbs = $ parentRel ? ($ base . DIRECTORY_SEPARATOR . $ parentRel ) : $ base ;
1543- $ newAbs = $ parentAbs . DIRECTORY_SEPARATOR . $ folderName ;
15441571 $ storage = self ::storage ();
15451572 $ isLocal = $ storage ->isLocal ();
15461573
@@ -1552,6 +1579,21 @@ public static function createFolder(string $folderName, string $parent, string $
15521579 if (!$ parentExists ) {
15531580 return ['success ' => false , 'error ' => 'Parent folder does not exist ' ];
15541581 }
1582+
1583+ if ($ isLocal ) {
1584+ $ baseReal = realpath (self ::uploadRoot ());
1585+ $ parentReal = realpath ($ parentAbs );
1586+ if (
1587+ $ baseReal === false ||
1588+ $ parentReal === false ||
1589+ !self ::isPathInsideOrSame ($ baseReal , $ parentReal )
1590+ ) {
1591+ return ['success ' => false , 'error ' => 'Invalid folder path ' ];
1592+ }
1593+ $ parentAbs = $ parentReal ;
1594+ }
1595+
1596+ $ newAbs = $ parentAbs . DIRECTORY_SEPARATOR . $ folderName ;
15551597 if ($ storage ->stat ($ newAbs ) !== null ) {
15561598 return ['success ' => false , 'error ' => 'Folder already exists ' ];
15571599 }
0 commit comments