Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions src/Context/GivenStepDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,28 @@ public function given_a_specific_directory( $empty_or_nonexistent, $dir ): void
// Mac OS X can prefix the `/var` folder to turn it into `/private/var`.
$dir = preg_replace( '|^/private/var/|', '/var/', $dir );

$temp_dir = realpath( sys_get_temp_dir() );
$temp_dir = $temp_dir ? Path::normalize( $temp_dir ) : Path::normalize( sys_get_temp_dir() );
$dir = Path::normalize( $dir );
$temp_dir_original = Path::normalize( sys_get_temp_dir() );
$temp_dir_real = realpath( sys_get_temp_dir() );
$temp_dir_real = $temp_dir_real ? Path::normalize( $temp_dir_real ) : $temp_dir_original;
$dir = Path::normalize( $dir );

// Also normalize temp dir for Mac OS X.
$temp_dir = preg_replace( '|^/private/var/|', '/var/', $temp_dir );
$temp_dir_original = preg_replace( '|^/private/var/|', '/var/', $temp_dir_original );
$temp_dir_real = preg_replace( '|^/private/var/|', '/var/', $temp_dir_real );

// Also check for temp dir prefixed with `/private` for Mac OS X.
if ( 0 !== strpos( $dir, $temp_dir ) && 0 !== strpos( $dir, "/private{$temp_dir}" ) ) {
$is_windows = DIRECTORY_SEPARATOR === '\\';
Comment thread
swissspidy marked this conversation as resolved.
Outdated

$in_temp = 0 === ( $is_windows ? stripos( $dir, $temp_dir_original ) : strpos( $dir, $temp_dir_original ) )
|| 0 === ( $is_windows ? stripos( $dir, $temp_dir_real ) : strpos( $dir, $temp_dir_real ) )
|| 0 === ( $is_windows ? stripos( $dir, "/private{$temp_dir_original}" ) : strpos( $dir, "/private{$temp_dir_original}" ) )
|| 0 === ( $is_windows ? stripos( $dir, "/private{$temp_dir_real}" ) : strpos( $dir, "/private{$temp_dir_real}" ) );
Comment on lines +67 to +70
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for checking if the directory is within the temporary directory is quite repetitive and difficult to read. Since you are checking multiple candidates against the same directory using a conditional comparison function (stripos vs strpos), you can simplify this by using a loop. This improves maintainability and reduces the risk of errors when adding new candidates.

		$compare = $is_windows ? 'stripos' : 'strpos';
		$in_temp = false;
		foreach ( [ $temp_dir_original, $temp_dir_real ] as $temp_path ) {
			if ( 0 === $compare( $dir, $temp_path ) || 0 === $compare( $dir, "/private{$temp_path}" ) ) {
				$in_temp = true;
				break;
			}
		}

Comment on lines +67 to +70
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The temp-dir guard uses a raw prefix check (strpos/stripos === 0). This can be bypassed by paths that merely share the same prefix (e.g. temp dir /tmp vs directory /tmpfoo), which could allow deleting directories outside the temp tree. Consider enforcing a path-boundary check by normalizing/rtrimming both sides and comparing with an added trailing separator (or otherwise ensuring the next character is a separator/end-of-string) before calling remove_dir().

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is Windows-specific (case-insensitive matching + sys_get_temp_dir vs realpath handling), but there’s no corresponding functional test ensuring Given an empty/non-existent <absolute-path> directory works on Windows when the temp path casing differs. Adding a @require-windows Behat scenario that builds an absolute temp path with different casing (e.g. via php -r + save STDOUT as {VAR}) would help prevent regressions.

Copilot uses AI. Check for mistakes.

if ( ! $in_temp ) {
throw new RuntimeException(
sprintf(
"Attempted to delete directory '%s' that is not in the temp directory '%s'. " . __FILE__ . ':' . __LINE__,
$dir,
$temp_dir
$temp_dir_real
)
);
}
Expand Down