mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-24 03:57:32 +09:00
150 lines
6.6 KiB
PHP
150 lines
6.6 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
| |
|
|
| This program is free software; you can redistribute it and/or |
|
|
| modify it under the terms of the GNU General Public License |
|
|
| as published by the Free Software Foundation; either version 2 |
|
|
| of the License, or (at your option) any later version. |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
require_once CACTI_PATH_INCLUDE . '/vendor/autoload.php';
|
|
require_once CACTI_PATH_LIBRARY . '/CactiFilesystem.php';
|
|
|
|
test('CactiFilesystem wraps Symfony filesystem operations', function () {
|
|
$filesystem = new CactiFilesystem();
|
|
$dir = sys_get_temp_dir() . '/cacti-filesystem-' . bin2hex(random_bytes(4));
|
|
$file = CactiFilesystem::join($dir, 'nested', 'settings.txt');
|
|
$copy = CactiFilesystem::join($dir, 'nested', 'settings.copy');
|
|
|
|
try {
|
|
$filesystem->ensureDirectory(dirname($file));
|
|
$filesystem->writeFile($file, 'alpha');
|
|
$filesystem->appendFile($file, "\nbeta");
|
|
$filesystem->copyFile($file, $copy);
|
|
|
|
expect($filesystem->has([$file, $copy]))->toBeTrue();
|
|
expect($filesystem->read($file))->toBe("alpha\nbeta");
|
|
expect(CactiFilesystem::canonicalize(CactiFilesystem::join($dir, 'nested', '..', 'nested', 'settings.txt')))->toBe($file);
|
|
} finally {
|
|
$filesystem->delete($dir);
|
|
}
|
|
});
|
|
|
|
test('CactiFilesystem read throws when the file is missing', function () {
|
|
$filesystem = new CactiFilesystem();
|
|
$missing = sys_get_temp_dir() . '/cacti-filesystem-missing-' . bin2hex(random_bytes(4));
|
|
|
|
expect(fn () => $filesystem->read($missing))
|
|
->toThrow(RuntimeException::class);
|
|
});
|
|
|
|
test('CactiFilesystem read rejects a path containing a null byte', function () {
|
|
$filesystem = new CactiFilesystem();
|
|
|
|
expect(fn () => $filesystem->read("/tmp/foo\0bar"))
|
|
->toThrow(RuntimeException::class, 'null byte');
|
|
});
|
|
|
|
test('CactiFilesystem rejects a null byte in has, ensureDirectory and delete', function () {
|
|
$filesystem = new CactiFilesystem();
|
|
$tainted = "/tmp/foo\0bar";
|
|
|
|
expect(fn () => $filesystem->has($tainted))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->has([$tainted]))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->ensureDirectory($tainted))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->ensureDirectory([$tainted]))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->delete($tainted))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->delete([$tainted]))->toThrow(RuntimeException::class, 'null byte');
|
|
});
|
|
|
|
test('CactiFilesystem rejects null bytes across write, copy, move and temporary paths', function () {
|
|
$filesystem = new CactiFilesystem();
|
|
$clean = sys_get_temp_dir() . '/clean';
|
|
$tainted = "/tmp/foo\0bar";
|
|
|
|
expect(fn () => $filesystem->writeFile($tainted, 'data'))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->appendFile($tainted, 'data'))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->copyFile($tainted, $clean))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->copyFile($clean, $tainted))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->move($tainted, $clean))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->move($clean, $tainted))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->temporaryName($tainted, 'tmp'))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->temporaryName($clean, "tmp\0"))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->temporaryName($clean, 'tmp', ".tmp\0"))->toThrow(RuntimeException::class, 'null byte')
|
|
->and(fn () => $filesystem->isAbsolute($tainted))->toThrow(RuntimeException::class, 'null byte');
|
|
});
|
|
|
|
test('CactiFilesystem accepts an injected Symfony filesystem', function () {
|
|
$injected = new Symfony\Component\Filesystem\Filesystem();
|
|
$filesystem = new CactiFilesystem($injected);
|
|
$dir = sys_get_temp_dir() . '/cacti-fs-inject-' . bin2hex(random_bytes(4));
|
|
|
|
try {
|
|
$filesystem->ensureDirectory($dir);
|
|
|
|
expect($filesystem->has($dir))->toBeTrue();
|
|
} finally {
|
|
$filesystem->delete($dir);
|
|
}
|
|
});
|
|
|
|
test('CactiFilesystem instance moves, renames and reports absolute paths', function () {
|
|
$filesystem = new CactiFilesystem();
|
|
$dir = sys_get_temp_dir() . '/cacti-fs-move-' . bin2hex(random_bytes(4));
|
|
$origin = CactiFilesystem::join($dir, 'origin.txt');
|
|
$target = CactiFilesystem::join($dir, 'target.txt');
|
|
|
|
try {
|
|
$filesystem->ensureDirectory($dir);
|
|
$filesystem->writeFile($origin, 'payload');
|
|
$filesystem->move($origin, $target);
|
|
|
|
expect($filesystem->has($origin))->toBeFalse()
|
|
->and($filesystem->read($target))->toBe('payload');
|
|
|
|
$temp = $filesystem->temporaryName($dir, 'tmp');
|
|
expect($filesystem->has($temp))->toBeTrue();
|
|
|
|
expect($filesystem->isAbsolute($target))->toBeTrue()
|
|
->and($filesystem->isAbsolute('relative/path'))->toBeFalse();
|
|
} finally {
|
|
$filesystem->delete($dir);
|
|
}
|
|
});
|
|
|
|
test('CactiFilesystem static facade renames, creates temp files with a suffix, and reports absolute paths', function () {
|
|
$dir = sys_get_temp_dir() . '/cacti-fs-static-' . bin2hex(random_bytes(4));
|
|
$origin = CactiFilesystem::join($dir, 'origin.txt');
|
|
$target = CactiFilesystem::join($dir, 'target.txt');
|
|
|
|
try {
|
|
CactiFilesystem::mkdir($dir);
|
|
CactiFilesystem::dumpFile($origin, 'payload');
|
|
CactiFilesystem::rename($origin, $target);
|
|
|
|
expect(CactiFilesystem::exists($origin))->toBeFalse()
|
|
->and(CactiFilesystem::exists($target))->toBeTrue();
|
|
|
|
// The suffix must land at the end of the generated filename: this is
|
|
// the Symfony Filesystem::tempnam() argument position, not a file mode.
|
|
$temp = CactiFilesystem::tempnam($dir, 'tmp', '.suffix');
|
|
expect(CactiFilesystem::exists($temp))->toBeTrue()
|
|
->and($temp)->toEndWith('.suffix');
|
|
|
|
expect(CactiFilesystem::isAbsolutePath($target))->toBeTrue()
|
|
->and(CactiFilesystem::isAbsolutePath('relative/path'))->toBeFalse();
|
|
} finally {
|
|
CactiFilesystem::remove($dir);
|
|
}
|
|
});
|
|
|
|
test('CactiFilesystem is a readonly value wrapper', function () {
|
|
$property = (new ReflectionClass(CactiFilesystem::class))->getProperty('filesystem');
|
|
expect($property->isReadOnly())->toBeTrue();
|
|
});
|