mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
change tests to test model/plugin logic directly
Some checks are pending
tests / ci (push) Waiting to run
Some checks are pending
tests / ci (push) Waiting to run
Previously it was testing the rendered frontend, now it ensures no malicious xss is saved
This commit is contained in:
parent
3e670d37c0
commit
a3f792944c
1 changed files with 32 additions and 86 deletions
|
|
@ -4,12 +4,8 @@ use App\Models\Plugin;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Livewire\Volt\Volt;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
|
|
||||||
use Tests\TestCase;
|
uses(Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||||
|
|
||||||
uses(TestCase::class,RefreshDatabase::class);
|
|
||||||
|
|
||||||
test('plugin has required attributes', function (): void {
|
test('plugin has required attributes', function (): void {
|
||||||
$plugin = Plugin::factory()->create([
|
$plugin = Plugin::factory()->create([
|
||||||
|
|
@ -684,56 +680,25 @@ test('plugin render includes utc_offset and time_zone_iana in trmnl.user context
|
||||||
->and($rendered)->toMatch('/\|-?\d+/'); // Should contain a pipe followed by a number (offset in seconds)
|
->and($rendered)->toMatch('/\|-?\d+/'); // Should contain a pipe followed by a number (offset in seconds)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin security: XSS Payload Dataset
|
* Plugin security: XSS Payload Dataset
|
||||||
* [Input, Expected to See, Dangerous parts that must be Missing]
|
* [Input, Expected Result, Forbidden String]
|
||||||
*/
|
*/
|
||||||
dataset('xss_vectors', [
|
dataset('xss_vectors', [
|
||||||
'standard_script' => [
|
'standard_script' => ['Safe <script>alert(1)</script>', 'Safe ', '<script>'],
|
||||||
'Safe <script>alert(1)</script>',
|
'attribute_event_handlers' => ['<a onmouseover="alert(1)">Link</a>', '<a>Link</a>', 'onmouseover'],
|
||||||
'Safe',
|
'javascript_protocol' => ['<a href="javascript:alert(1)">Click</a>', '<a>Click</a>', 'javascript:'],
|
||||||
['<script>', 'alert(1)']
|
'iframe_injection' => ['Watch <iframe src="https://x.com"></iframe>', 'Watch ', '<iframe>'],
|
||||||
],
|
'img_onerror_fallback' => ['Photo <img src=x onerror=alert(1)>', 'Photo <img src="x" alt="x">', 'onerror'],
|
||||||
'attribute_event_handlers' => [
|
|
||||||
'<a href="https://trmnl.com" onmouseover="alert(1)" onclick="confirm()">Link</a>',
|
|
||||||
'<a href="https://trmnl.com">Link</a>',
|
|
||||||
['onmouseover', 'onclick', 'confirm()']
|
|
||||||
],
|
|
||||||
'javascript_protocol' => [
|
|
||||||
'<a href="javascript:alert(1)">Click Me</a>',
|
|
||||||
'<a>Click Me</a>',
|
|
||||||
['javascript:']
|
|
||||||
],
|
|
||||||
'broken_tags_layout_break' => [
|
|
||||||
'<b>Unclosed tag <script>alert(1)</script>',
|
|
||||||
'<b>Unclosed tag',
|
|
||||||
['<script>']
|
|
||||||
],
|
|
||||||
'iframe_injection' => [
|
|
||||||
'Watch <iframe src="https://malicious.com"></iframe>',
|
|
||||||
'Watch',
|
|
||||||
['<iframe>', 'https://malicious.com']
|
|
||||||
],
|
|
||||||
'encoded_entities' => [
|
|
||||||
'<a href="javascript:alert(1)">Link</a>',
|
|
||||||
'<a>Link</a>',
|
|
||||||
['javascript']
|
|
||||||
],
|
|
||||||
'img_onerror_fallback' => [
|
|
||||||
'Photo <img src=x onerror=alert(1)>',
|
|
||||||
'Photo',
|
|
||||||
['onerror', 'alert(1)']
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
test('plugin config descriptions are sanitized', function (string $input, string $expectedSee, array $forbidden) : void {
|
test('plugin model sanitizes template fields on save', function (string $input, string $expected, string $forbidden): void {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
// This triggers the static::saving hook in the Plugin model
|
// We test the Model logic directly. This triggers the static::saving hook.
|
||||||
$plugin = Plugin::create([
|
$plugin = Plugin::create([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'name' => 'Security Test Plugin',
|
'name' => 'Security Test',
|
||||||
'data_stale_minutes' => 15,
|
'data_stale_minutes' => 15,
|
||||||
'data_strategy' => 'static',
|
'data_strategy' => 'static',
|
||||||
'polling_verb' => 'get',
|
'polling_verb' => 'get',
|
||||||
|
|
@ -741,53 +706,34 @@ test('plugin config descriptions are sanitized', function (string $input, string
|
||||||
'custom_fields' => [
|
'custom_fields' => [
|
||||||
[
|
[
|
||||||
'keyname' => 'test_field',
|
'keyname' => 'test_field',
|
||||||
'field_type' => 'string',
|
|
||||||
'name' => 'Secure Field',
|
|
||||||
'description' => $input,
|
'description' => $input,
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'configuration' => [],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$test = Volt::test('plugins.recipe', ['plugin' => $plugin])
|
|
||||||
->assertSeeHtml($expectedSee);
|
|
||||||
|
|
||||||
foreach ($forbidden as $malice) {
|
|
||||||
$test->assertDontSeeHtml($malice);
|
|
||||||
}
|
|
||||||
})->with('xss_vectors');
|
|
||||||
|
|
||||||
test('plugin configuration help_text is sanitized', function (string $input, string $expectedSee, array $forbidden) : void {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$plugin = Plugin::create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'name' => 'Help Text Security Test',
|
|
||||||
'data_stale_minutes' => 15,
|
|
||||||
'data_strategy' => 'static',
|
|
||||||
'polling_verb' => 'get',
|
|
||||||
'configuration_template' => [
|
|
||||||
'custom_fields' => [
|
|
||||||
[
|
|
||||||
'keyname' => 'test',
|
|
||||||
'field_type' => 'string',
|
|
||||||
'name' => 'Secure Field',
|
|
||||||
'help_text' => $input,
|
'help_text' => $input,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'configuration' => [],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user);
|
$field = $plugin->fresh()->configuration_template['custom_fields'][0];
|
||||||
|
|
||||||
$test = Volt::test('plugins.recipe', ['plugin' => $plugin])
|
// Assert the saved data is clean
|
||||||
->assertSeeHtml($expectedSee);
|
expect($field['description'])->toBe($expected)
|
||||||
|
->and($field['help_text'])->toBe($expected)
|
||||||
foreach ($forbidden as $malice) {
|
->and($field['description'])->not->toContain($forbidden);
|
||||||
$test->assertDontSeeHtml($malice);
|
|
||||||
}
|
|
||||||
})->with('xss_vectors');
|
})->with('xss_vectors');
|
||||||
|
|
||||||
|
test('plugin model preserves multi_string csv format', function (): void {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$plugin = Plugin::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'name' => 'Multi-string Test',
|
||||||
|
'data_stale_minutes' => 15,
|
||||||
|
'data_strategy' => 'static',
|
||||||
|
'polling_verb' => 'get',
|
||||||
|
'configuration' => [
|
||||||
|
'tags' => 'laravel,pest,security'
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($plugin->fresh()->configuration['tags'])->toBe('laravel,pest,security');
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue