61 lines
2.2 KiB
PHP
61 lines
2.2 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
// Stubs for framework classes used by petition-form.php
|
||
|
|
class Context {}
|
||
|
|
class Hook { const TEMPLATE_VARS = 'template_vars'; }
|
||
|
|
class Hooks { public static function add(string $hook, callable $fn): void {} }
|
||
|
|
|
||
|
|
// petition-form.php calls session_start() at load time — stub $_SESSION and $_SERVER first
|
||
|
|
$_SESSION = [];
|
||
|
|
$_SERVER['REMOTE_ADDR'] ??= '127.0.0.1';
|
||
|
|
$_SERVER['HTTP_HOST'] ??= 'localhost';
|
||
|
|
|
||
|
|
require_once CUSTOM_DIR . '/plugins/page/petition-form.php';
|
||
|
|
|
||
|
|
// --- petitionSanitizeCSV ---
|
||
|
|
|
||
|
|
it('leaves safe values unchanged', function () {
|
||
|
|
expect(petitionSanitizeCSV('John Doe'))->toBe('John Doe');
|
||
|
|
expect(petitionSanitizeCSV('john@example.com'))->toBe('john@example.com');
|
||
|
|
expect(petitionSanitizeCSV(''))->toBe('');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('prefixes formula-starting characters to prevent CSV injection', function () {
|
||
|
|
expect(petitionSanitizeCSV('=SUM(A1)'))->toBe("'=SUM(A1)");
|
||
|
|
expect(petitionSanitizeCSV('+evil'))->toBe("'+evil");
|
||
|
|
expect(petitionSanitizeCSV('-evil'))->toBe("'-evil");
|
||
|
|
expect(petitionSanitizeCSV('@evil'))->toBe("'@evil");
|
||
|
|
});
|
||
|
|
|
||
|
|
it('prefixes tab and newline characters', function () {
|
||
|
|
expect(petitionSanitizeCSV("\t data"))->toBe("'\t data");
|
||
|
|
expect(petitionSanitizeCSV("\r data"))->toBe("'\r data");
|
||
|
|
expect(petitionSanitizeCSV("\n data"))->toBe("'\n data");
|
||
|
|
});
|
||
|
|
|
||
|
|
it('only checks the first character', function () {
|
||
|
|
expect(petitionSanitizeCSV('safe=still safe'))->toBe('safe=still safe');
|
||
|
|
});
|
||
|
|
|
||
|
|
// --- petitionGetCsvPath ---
|
||
|
|
|
||
|
|
it('builds the correct CSV path for a valid petition ID', function () {
|
||
|
|
$path = petitionGetCsvPath('my-petition');
|
||
|
|
expect($path)->toEndWith('/data/petitions/my-petition.csv');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('strips non-alphanumeric characters from petition ID', function () {
|
||
|
|
$path = petitionGetCsvPath('my petition!');
|
||
|
|
expect($path)->toEndWith('/data/petitions/mypetition.csv');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('throws for an empty petition ID', function () {
|
||
|
|
petitionGetCsvPath('');
|
||
|
|
})->throws(Exception::class);
|
||
|
|
|
||
|
|
it('strips path traversal characters rather than throwing', function () {
|
||
|
|
// Dots and slashes are stripped by the regex, leaving a safe ID
|
||
|
|
$path = petitionGetCsvPath('../evil');
|
||
|
|
expect($path)->toEndWith('/data/petitions/evil.csv');
|
||
|
|
});
|