6b9a7636ae
F-CSV: ячейки экспорта писались «как есть» — значение вроде =HYPERLINK(...) или @SUM(...) в комментарии/контакте/городе при открытии файла в Excel/ LibreOffice исполнялось как формула (OWASP Formula Injection). Новый App\Support\CsvFormulaGuard::neutralize() префиксует апострофом ячейку, начинающуюся с = + - @ TAB CR. Применён к свободному тексту в: - DealExportController (телефон/источник/город/статус/комментарий) - DealsExportProvider (телефон/контакт/статус/проект/менеджер) Числовые колонки (id/суммы/даты) не трогаются — ведущий «-» там легитимен. TenantChargesController НЕ уязвим: колонки enum(CHECK)/число/дата, свободного текста нет. TDD: 13 unit-тестов хелпера + feature-тест экспорта сделок + provider-тест. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
36 lines
1.6 KiB
PHP
36 lines
1.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Support\CsvFormulaGuard;
|
|
|
|
// CSV/formula-инъекция: значение, начинающееся с = + - @ (или TAB/CR), при
|
|
// открытии в Excel/LibreOffice исполняется как формула. Нейтрализация —
|
|
// префикс апострофом (ячейка показывается как текст).
|
|
|
|
dataset('formula_triggers', ['=', '+', '-', '@', "\t", "\r"]);
|
|
|
|
it('нейтрализует значение, начинающееся с формульного символа', function (string $trigger) {
|
|
expect(CsvFormulaGuard::neutralize($trigger.'HYPERLINK("http://evil")'))
|
|
->toBe("'".$trigger.'HYPERLINK("http://evil")');
|
|
})->with('formula_triggers');
|
|
|
|
it('не трогает безопасные значения', function (string $safe) {
|
|
expect(CsvFormulaGuard::neutralize($safe))->toBe($safe);
|
|
})->with([
|
|
'+7 999 111-11-11 как обычный текст после цифр' => '79991234567',
|
|
'кириллица' => 'Окна Москва',
|
|
'латиница' => 'comment text',
|
|
'число' => '1500.00',
|
|
'email' => 'user@example.ru как текст',
|
|
]);
|
|
|
|
it('пропускает null и пустую строку без изменений', function () {
|
|
expect(CsvFormulaGuard::neutralize(null))->toBeNull();
|
|
expect(CsvFormulaGuard::neutralize(''))->toBe('');
|
|
});
|
|
|
|
it('не ломает многобайтовую кириллицу в начале', function () {
|
|
expect(CsvFormulaGuard::neutralize('Москва'))->toBe('Москва');
|
|
});
|