Files
coolify/tests/Unit/DatabaseBackupSecurityTest.php
T
Andras BacsaiandClaude Opus 5.5 a70e856882 test: remove tests that do not test behavior and strengthen weak ones
- Remove tests that only check markup, CSS, static text, wire:key strings,
  framework behavior, copied app logic, or expect(true)
- Replace source-grep and Reflection checks of security fixes with tests
  that run the real code (shell quoting, locked properties, 403 checks,
  webhook fork trust, log redaction, Sentinel token cleanup)
- Assert response bodies and unchanged data on API error paths
- Run the Traefik version job through handle() instead of private methods
- Update stale tests for scheduled task props, the default Postgres image,
  and the disabled transfer link of unvalidated servers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:47:56 +01:00

124 lines
4.7 KiB
PHP

<?php
/**
* Database Backup Security Tests
*
* Tests to ensure database backup functionality is protected against
* command injection attacks via malicious database names.
*
* Related Issues: #2 in security_issues.md
* Related Files: app/Jobs/DatabaseBackupJob.php, app/Livewire/Project/Database/BackupEdit.php
*/
test('database backup rejects command injection in database name with command substitution', function () {
expect(fn () => validateShellSafePath('test$(whoami)', 'database name'))
->toThrow(Exception::class);
});
test('database backup rejects command injection with semicolon separator', function () {
expect(fn () => validateShellSafePath('test; rm -rf /', 'database name'))
->toThrow(Exception::class);
});
test('database backup rejects command injection with pipe operator', function () {
expect(fn () => validateShellSafePath('test | cat /etc/passwd', 'database name'))
->toThrow(Exception::class);
});
test('database backup rejects command injection with backticks', function () {
expect(fn () => validateShellSafePath('test`whoami`', 'database name'))
->toThrow(Exception::class);
});
test('database backup rejects command injection with ampersand', function () {
expect(fn () => validateShellSafePath('test & whoami', 'database name'))
->toThrow(Exception::class);
});
test('database backup rejects command injection with redirect operators', function () {
expect(fn () => validateShellSafePath('test > /tmp/pwned', 'database name'))
->toThrow(Exception::class);
expect(fn () => validateShellSafePath('test < /etc/passwd', 'database name'))
->toThrow(Exception::class);
});
test('database backup rejects command injection with newlines', function () {
expect(fn () => validateShellSafePath("test\nrm -rf /", 'database name'))
->toThrow(Exception::class);
});
test('database backup accepts legitimate database names', function () {
expect(fn () => validateShellSafePath('postgres', 'database name'))
->not->toThrow(Exception::class);
expect(fn () => validateShellSafePath('my_database', 'database name'))
->not->toThrow(Exception::class);
expect(fn () => validateShellSafePath('db-prod', 'database name'))
->not->toThrow(Exception::class);
expect(fn () => validateShellSafePath('test123', 'database name'))
->not->toThrow(Exception::class);
});
// --- MongoDB collection name validation tests ---
test('mongodb collection name rejects command substitution injection', function () {
expect(fn () => validateShellSafePath('$(touch /tmp/pwned)', 'collection name'))
->toThrow(Exception::class);
});
test('mongodb collection name rejects backtick injection', function () {
expect(fn () => validateShellSafePath('`id > /tmp/pwned`', 'collection name'))
->toThrow(Exception::class);
});
test('mongodb collection name rejects semicolon injection', function () {
expect(fn () => validateShellSafePath('col1; rm -rf /', 'collection name'))
->toThrow(Exception::class);
});
test('mongodb collection name rejects ampersand injection', function () {
expect(fn () => validateShellSafePath('col1 & whoami', 'collection name'))
->toThrow(Exception::class);
});
test('mongodb collection name rejects redirect injection', function () {
expect(fn () => validateShellSafePath('col1 > /tmp/pwned', 'collection name'))
->toThrow(Exception::class);
});
test('validateDatabasesBackupInput validates mongodb format with collection names', function () {
// Valid MongoDB formats should pass
expect(fn () => validateDatabasesBackupInput('mydb'))
->not->toThrow(Exception::class);
expect(fn () => validateDatabasesBackupInput('mydb:col1,col2'))
->not->toThrow(Exception::class);
expect(fn () => validateDatabasesBackupInput('db1:col1,col2|db2:col3'))
->not->toThrow(Exception::class);
expect(fn () => validateDatabasesBackupInput('all'))
->not->toThrow(Exception::class);
});
test('validateDatabasesBackupInput rejects injection in collection names', function () {
// Command substitution in collection name
expect(fn () => validateDatabasesBackupInput('mydb:$(touch /tmp/pwned)'))
->toThrow(Exception::class);
// Backtick injection in collection name
expect(fn () => validateDatabasesBackupInput('mydb:`id`'))
->toThrow(Exception::class);
// Semicolon in collection name
expect(fn () => validateDatabasesBackupInput('mydb:col1;rm -rf /'))
->toThrow(Exception::class);
});
test('validateDatabasesBackupInput rejects injection in database name within mongo format', function () {
expect(fn () => validateDatabasesBackupInput('$(whoami):col1,col2'))
->toThrow(Exception::class);
});