PHPUnit - testing function which uses realpath() - php

I have a function that checks if user supplied filename is under the /opt/ directory.
It uses realpath so users can't pass in something like '../etc/passwd' for security reasons.
function check_file_path($relative_filename) {
$abspath = realpath('/opt/' . $relative_filename);
return ($abspath && strpos($abspath , '/opt/') === 0);
}
Now realpath will return false if the file doesn't exist on the fs.
When PHPUnit is running (on dev machine, not on the server), the path does not exist thus we cannot do a positive test.
I've looked into vfsstream but it doesn't play nice with realpath()
org\bovigo\vfs\vfsStream::setup('home');
$file = org\bovigo\vfs\vfsStream::url('home/test.txt');
file_put_contents($file, "The new contents of the file");
$abspath = realpath('vfs://home/test.txt');
// $abspath will be false
Any suggestion to mock a filesystem so that realpath will work?

You can use runkit. It's extension of php.
But if your function calling inside any namespace you can define your own function in that namespace.

Related

In Laravel 5.6, is it necessary to check if a directory exists before creation? (Situational)

In a function used in Image Storage, I realized I MAY have been over-complicating the code. I have two examples that both return the same results.
First Example:
public function image(Request $request, User $user)
{
$file = $request->file('image');
$path = "user_" . $user->id . "_images";
$filename = $path . "/test.jpg";
Storage::disk('public')->put($filename, File::get($file));
return back();
}
This example was tested in two scenarios. Once with NO directory inside public, and it returned by creating public/user_1_images/test.jpg ...
The second scenario, we already had public/user_1_images/ as an empty directory. Meaning it already exists. So instead, it just put the file inside of there without creating the folder.
Second Example:
public function image(Request $request, User $user)
{
$file = $request->file('image');
$path = "user_" . $user->id . "_images";
if (!Storage::disk('public')->exists($path))
{
Storage::makeDirectory($path, 0755, true, true);
}
$filename = $path . "/test.jpg";
Storage::disk('public')->put($filename, File::get($file));
return back();
}
In this example, we're checking to see if public/user_1_images/ exists, and if it does not, we're creating the folder, and then putting the image inside.
Would I need to do this, if I can just have it automatically check/create/not-create by titling the file "user_1_images/test.jpg"?
It all depends on the driver you use. All drivers must implement AdapterInterface interface and that's the only requirement they must meet.
I had a brief look at some of the popular drivers:
Local driver always calls ensureDirectory() before writing to a path, hence there is no need to create directory manually.
FTP driver always calls ensureDirectory() before writing to a path, hence there is no need to create directory manually.
S3 driver doesn't ensure a directory exist, but all necessary directories are created in S3 when it's asked to write to a path that contains directories. So again, no need to create directory manually.
So it seems that with most (if not all) drivers you can simply write to a path and not worry whether directory exists or not.

PHP fopen() error in testing

I have the following file structure
src/functions.php
src/sample.csv
tests/functionsTest.php
In the functions.php, I have this function to read a csv file
<?php
function readCSV($csvFile){
$file_handle = fopen($csvFile, 'r');
while (!feof($file_handle) ) {
$line_of_text[] = fgetcsv($file_handle, 1024);
}
fclose($file_handle);
return $line_of_text;
}
?>
In the functionsTest.php file, I have
<?php
require_once('src/functions.php');
use phpunit\framework\TestCase;
class test extends TestCase {
public function testReadDataFromFile() {
$filename = "./sample.csv";
$data = readCSV($filename);
$this->assertEquals("test data", $data);
}
}
?>
When I run the test code using >phpunit tests/functionsTest.php, I get error saying
fopen(./sample.csv): failed to open stream: No such file or directory
But if I call the function from the same src/functions.php, I get the data
print_r(readCSV("sample.csv"));
So I tried to move the sample.csv file to the test folder -
tests/sample.csv
but still I get the same error
I think I am missing something simple.
Can anyone help me with this?
That's because the single dot . represents the current directory. Calling your script from tests/ and specifying . will look for sample.csv in your tests directory, when it's actually in src/.
You want to specify the directory using $filename = "../src/sample.csv";
Also note that you may want to use magic constants instead, or specify an absolute path to the file (e.g. /YourName/YourProject/src/sample.csv), instead of a relative one.
P.S. You can print out those magic constants like __FILE__ and __DIR__ when running your script to troubleshoot issues like this.

Get filesystem path from a PHP streamwrapper?

If I have a file in a php streamwrapper, such as "media://icon.png" how can I get it to tell me the filesystem path of that file, assuming it's on a filesystem?
I looked on the documentation page but didn't see a method to return the streamwrapper path.
The StreamWrapper class represents generic streams. Because not all streams are backed by the notion of a filesystem, there isn't a generic method for this.
If the uri property from stream_get_meta_data isn't working for your implementation, you can record the information during open time then access it via stream_get_meta_data. Example:
class MediaStream {
public $localpath = null;
public function stream_open($path, $mode, $options, &$opened_path)
{
$this->localpath = realpath(preg_replace('+^media://+', '/', $path));
return fopen($this->localpath, $mode, $options);
}
}
stream_wrapper_register('media', 'MediaStream');
$fp = fopen('media://tmp/icon.png', 'r');
$data = stream_get_meta_data($fp);
var_dump(
$data['wrapper_data']->localpath
);
Of course, there's always a brute force approach: after creating your resource, you can call fstat, which includes the device and inode. You can then open that device, walk its directory structure, and find that inode. See here for an example.

Check is SFTP directory exists before trying to create it using phpseclib

I have the following function in my model:
public function createfolder($location, $name){
define('NET_SFTP_LOGGING', NET_SFTP_LOG_COMPLEX);
$sftp = new Net_SFTP('xx.xxx.xx.xx');
if (!$sftp->login('admin', '********')) {
exit('Login Failed');
}
//moves to a location (Job folder for example)
$sftp->chdir($location);
//makes the folder
$sftp->mkdir($name);
}
This will work but I would like to add some sort of error prevention validation, how can I check if a folder exists using SFTP?
Think I came up with a solution:
chdir() changes directories, mkdir() creates directories, and rmdir() removes directories. In the event of failure, they all return false. chdir(), mkdir(), and rmdir() return true on successful completion of the operation.
So I can use an if statement to check if chdir() === true or false to see if the directory is there.
You can use $sftp->is_dir($location) to check if it's a directory.
I think your solution is a good one.
In theory you could also do something like $sftp->stat('filename')['permissions'] & 0170000 == 0040000 I think. That's a hack borrowed from phpseclib itself:
https://github.com/phpseclib/phpseclib/blob/7a2c7a414c08d28f0700c7f6f8686a9e0e246a44/phpseclib/Net/SFTP.php#L1969

Laravel - Possible to detect if package is running from workbench or vendor?

I am sure with some hacking I could find a way to do this but I am hoping there is an elegant solution. I need to be able to detect when my package is running from the workbench of if the package is installed and running as a 3rd party.
You can create a method in your main package class to do the check, like this:
MyClass {
protected function getPackageLocation()
{
// Check if the path to this file contains 'workbench'
if (strpos(realpath(__FILE__), 'workbench') !== false) {
return 'workbench';
}
// If not, it's in vendor folder
return 'vendor';
}
}
If you need to check it from outside your package, you can always make the function public.
To make it more reliable, you can check for workbench/your_vendor/your_package/in the conditional, or even make it dynamic with something like:
// untested: translate namespace to path format
$needle = 'workspace/' . strtolower(str_replace("_", "/", __NAMESPACE__));
if (strpos(realpath(__FILE__), $needle) !== false) {
...

Categories