293 lines
9.1 KiB
PHP
293 lines
9.1 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Security\IDOR;
|
|
|
|
use App\Models\User;
|
|
use App\Models\Organization;
|
|
use App\Models\Bank;
|
|
use App\Models\Admin;
|
|
use App\Models\Post;
|
|
use App\Models\Transaction;
|
|
use App\Models\Account;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Profile Access IDOR (Insecure Direct Object Reference) Tests
|
|
*
|
|
* Tests that users cannot access resources belonging to other users
|
|
* by manipulating IDs in requests.
|
|
*
|
|
* @group security
|
|
* @group idor
|
|
* @group critical
|
|
*/
|
|
class ProfileAccessIDORTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* Test user cannot view another user's private profile data via ID manipulation
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_cannot_view_another_users_private_profile_via_id()
|
|
{
|
|
// Arrange: Create two users
|
|
$user1 = User::factory()->create();
|
|
$user2 = User::factory()->create();
|
|
|
|
// Act: Login as user1, try to access user2's profile edit page
|
|
$response = $this->actingAs($user1)
|
|
->get(route('profile.edit'));
|
|
|
|
// Try to load user2's data by manipulating session
|
|
session(['activeProfileId' => $user2->id]);
|
|
|
|
$response2 = $this->actingAs($user1)
|
|
->get(route('profile.edit'));
|
|
|
|
// Assert: Should not be able to see user2's private data
|
|
// This is a basic check - deeper validation would require inspecting response content
|
|
$this->assertTrue(true); // Placeholder for more specific assertions
|
|
}
|
|
|
|
/**
|
|
* Test user cannot access another user's account balance via ID manipulation
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_cannot_access_another_users_account_balance()
|
|
{
|
|
// Arrange: Create two users with accounts
|
|
$user1 = User::factory()->create();
|
|
$user2 = User::factory()->create();
|
|
|
|
$account1 = Account::factory()->create([
|
|
'accountable_type' => User::class,
|
|
'accountable_id' => $user1->id,
|
|
]);
|
|
|
|
$account2 = Account::factory()->create([
|
|
'accountable_type' => User::class,
|
|
'accountable_id' => $user2->id,
|
|
]);
|
|
|
|
// Create a transaction for user2
|
|
Transaction::factory()->create([
|
|
'from_account_id' => $account2->id,
|
|
'to_account_id' => $account1->id,
|
|
'amount' => 100,
|
|
]);
|
|
|
|
// Act: Login as user1
|
|
$this->actingAs($user1);
|
|
|
|
// Try to access account page
|
|
$response = $this->get(route('accounts'));
|
|
|
|
// Assert: user1 should only see their own account balance
|
|
// Should NOT see user2's account details
|
|
$response->assertSuccessful();
|
|
|
|
// The response should not contain user2's account ID or specific balance
|
|
// (This would require more specific assertions based on actual response structure)
|
|
}
|
|
|
|
/**
|
|
* Test user cannot view another user's transaction history via ID manipulation
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_cannot_view_another_users_transaction_history()
|
|
{
|
|
// Arrange: Create two users with transactions
|
|
$user1 = User::factory()->create();
|
|
$user2 = User::factory()->create();
|
|
|
|
$account1 = Account::factory()->create([
|
|
'accountable_type' => User::class,
|
|
'accountable_id' => $user1->id,
|
|
]);
|
|
|
|
$account2 = Account::factory()->create([
|
|
'accountable_type' => User::class,
|
|
'accountable_id' => $user2->id,
|
|
]);
|
|
|
|
// Create transaction between two different users (not involving user1)
|
|
$user3 = User::factory()->create();
|
|
$account3 = Account::factory()->create([
|
|
'accountable_type' => User::class,
|
|
'accountable_id' => $user3->id,
|
|
]);
|
|
|
|
$privateTransaction = Transaction::factory()->create([
|
|
'from_account_id' => $account2->id,
|
|
'to_account_id' => $account3->id,
|
|
'amount' => 500,
|
|
'description' => 'Private transaction between user2 and user3',
|
|
]);
|
|
|
|
// Act: Login as user1 and try to view transaction history
|
|
$response = $this->actingAs($user1)
|
|
->get(route('transactions'));
|
|
|
|
// Assert: Should only see transactions involving user1
|
|
$response->assertSuccessful();
|
|
|
|
// Try to access specific transaction by ID manipulation
|
|
$response2 = $this->actingAs($user1)
|
|
->get(route('statement', ['transactionId' => $privateTransaction->id]));
|
|
|
|
// Assert: Should not be able to access transaction details
|
|
// (Implementation should check ownership)
|
|
// For now, verify the route is accessible but doesn't leak data
|
|
}
|
|
|
|
/**
|
|
* Test user cannot edit another user's post via ID manipulation
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_cannot_edit_another_users_post()
|
|
{
|
|
// Arrange: Create two users with posts
|
|
$user1 = User::factory()->create();
|
|
$user2 = User::factory()->create();
|
|
|
|
$user1Post = Post::factory()->create([
|
|
'profile_id' => $user1->id,
|
|
'profile_type' => User::class,
|
|
]);
|
|
|
|
$user2Post = Post::factory()->create([
|
|
'profile_id' => $user2->id,
|
|
'profile_type' => User::class,
|
|
]);
|
|
|
|
// Act: Login as user1 and try to edit user2's post
|
|
$this->actingAs($user1);
|
|
|
|
// Try to access edit page for user2's post
|
|
$response = $this->get("/posts/{$user2Post->id}/edit");
|
|
|
|
// Assert: Should be denied (403) or redirected
|
|
// Proper authorization should prevent accessing edit page
|
|
$this->assertTrue(
|
|
$response->status() === 403 || $response->status() === 302,
|
|
"User should not be able to edit another user's post"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test user cannot access organization dashboard they're not linked to
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_cannot_access_organization_dashboard_without_link()
|
|
{
|
|
// Arrange: Create user and two organizations
|
|
$user = User::factory()->create();
|
|
$org1 = Organization::factory()->create();
|
|
$org2 = Organization::factory()->create();
|
|
|
|
// Link user to org1 only
|
|
$user->organizations()->attach($org1->id);
|
|
|
|
// Act: Login as user
|
|
$this->actingAs($user);
|
|
|
|
// Try to switch to org2 (not linked)
|
|
session(['activeProfileType' => 'App\\Models\\Organization']);
|
|
session(['activeProfileId' => $org2->id]); // IDOR attempt
|
|
session(['active_guard' => 'organization']);
|
|
|
|
// Try to access dashboard
|
|
$response = $this->get(route('dashboard'));
|
|
|
|
// Assert: Should not have access to org2's dashboard
|
|
// The getActiveProfile() helper should validate the link
|
|
}
|
|
|
|
/**
|
|
* Test user cannot access bank dashboard they're not linked to
|
|
*
|
|
* @test
|
|
*/
|
|
public function user_cannot_access_bank_dashboard_without_link()
|
|
{
|
|
// Arrange: Create user and two banks
|
|
$user = User::factory()->create();
|
|
$bank1 = Bank::factory()->create();
|
|
$bank2 = Bank::factory()->create();
|
|
|
|
// Link user to bank1 only
|
|
$user->banks()->attach($bank1->id);
|
|
|
|
// Act: Login as user
|
|
$this->actingAs($user);
|
|
|
|
// Try to switch to bank2 (not linked)
|
|
session(['activeProfileType' => 'App\\Models\\Bank']);
|
|
session(['activeProfileId' => $bank2->id]); // IDOR attempt
|
|
session(['active_guard' => 'bank']);
|
|
|
|
// Try to access protected bank functionality
|
|
$response = $this->get(route('dashboard'));
|
|
|
|
// Assert: Should not have access to bank2
|
|
// Middleware or profile validation should prevent this
|
|
}
|
|
|
|
/**
|
|
* Test user cannot access admin panel by ID manipulation
|
|
*
|
|
* @test
|
|
*/
|
|
public function regular_user_cannot_access_admin_panel()
|
|
{
|
|
// Arrange: Create regular user and admin
|
|
$user = User::factory()->create();
|
|
$admin = Admin::factory()->create();
|
|
|
|
// Act: Login as regular user
|
|
$this->actingAs($user);
|
|
|
|
// Try to manipulate session to become admin
|
|
session(['activeProfileType' => 'App\\Models\\Admin']);
|
|
session(['activeProfileId' => $admin->id]); // IDOR attempt
|
|
session(['active_guard' => 'admin']);
|
|
|
|
// Try to access admin dashboard
|
|
$response = $this->get('/admin/dashboard');
|
|
|
|
// Assert: Should be denied
|
|
$this->assertTrue(
|
|
$response->status() === 403 || $response->status() === 302 || $response->status() === 404,
|
|
"Regular user should not access admin panel"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test API endpoints validate ownership (if applicable)
|
|
*
|
|
* @test
|
|
*/
|
|
public function api_endpoints_validate_resource_ownership()
|
|
{
|
|
// Arrange: Create two users
|
|
$user1 = User::factory()->create();
|
|
$user2 = User::factory()->create();
|
|
|
|
// Act: Login as user1
|
|
$this->actingAs($user1);
|
|
|
|
// Try to access user2's data via API (if API exists)
|
|
// This is a placeholder test - actual implementation depends on API structure
|
|
|
|
// Assert: Proper 403 or ownership validation
|
|
$this->assertTrue(true); // Placeholder
|
|
}
|
|
}
|