Skip to content

Commit ab278f6

Browse files
Copilotswissspidy
andauthored
Fix is_child_path: resolve .. segments and use case-insensitive comparison on Windows
Agent-Logs-Url: https://github.com/wp-cli/package-command/sessions/c458f3cb-4cae-4b57-b796-a60a092eea37 Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
1 parent c4f7ef4 commit ab278f6

1 file changed

Lines changed: 28 additions & 4 deletions

File tree

src/Package_Command.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,19 +1201,43 @@ private static function get_package_name_and_version_from_dir_package( $dir_pack
12011201
/**
12021202
* Checks whether a path is inside (or equal to) a given parent directory.
12031203
*
1204-
* Uses string-based normalisation so it works even when the paths do not
1205-
* exist on the filesystem yet.
1204+
* Resolves '.' and '..' segments without touching the filesystem so it
1205+
* works even when the paths do not exist yet. Uses a case-insensitive
1206+
* comparison on Windows where the filesystem is case-insensitive.
12061207
*
12071208
* @param string $path Path to test.
12081209
* @param string $parent_dir Parent directory to test against.
12091210
* @return bool True when $path is inside $parent_dir.
12101211
*/
12111212
private static function is_child_path( $path, $parent_dir ) {
1212-
$normalized_path = rtrim( Path::normalize( $path ), '/' ) . '/';
1213-
$normalized_parent = rtrim( Path::normalize( $parent_dir ), '/' ) . '/';
1213+
$normalized_path = self::resolve_dot_segments( rtrim( str_replace( '\\', '/', $path ), '/' ) ) . '/';
1214+
$normalized_parent = self::resolve_dot_segments( rtrim( str_replace( '\\', '/', $parent_dir ), '/' ) ) . '/';
1215+
if ( DIRECTORY_SEPARATOR === '\\' ) {
1216+
return 0 === stripos( $normalized_path, $normalized_parent );
1217+
}
12141218
return 0 === strpos( $normalized_path, $normalized_parent );
12151219
}
12161220

1221+
/**
1222+
* Resolves '.' and '..' segments in a path without touching the filesystem.
1223+
*
1224+
* @param string $path Forward-slash path to resolve.
1225+
* @return string Resolved path.
1226+
*/
1227+
private static function resolve_dot_segments( $path ) {
1228+
$is_absolute = isset( $path[0] ) && '/' === $path[0];
1229+
$result = [];
1230+
foreach ( explode( '/', $path ) as $part ) {
1231+
if ( '..' === $part ) {
1232+
array_pop( $result );
1233+
} elseif ( '.' !== $part && '' !== $part ) {
1234+
$result[] = $part;
1235+
}
1236+
}
1237+
$resolved = implode( '/', $result );
1238+
return $is_absolute ? '/' . $resolved : $resolved;
1239+
}
1240+
12171241
/**
12181242
* Gets the WP-CLI packages composer.json object.
12191243
*/

0 commit comments

Comments
 (0)