image_to_media_swapper-2.x-dev/tests/src/Kernel/SecurityAttackSimulationTest.php

tests/src/Kernel/SecurityAttackSimulationTest.php
<?php

declare(strict_types=1);

namespace Drupal\Tests\image_to_media_swapper\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\user\Entity\User;
use Drupal\user\Entity\Role;
use Drupal\image_to_media_swapper\Controller\SwapperController;
use Drupal\user\UserInterface;
use Symfony\Component\HttpFoundation\Request;

/**
 * Tests security vulnerabilities for the image_to_media_swapper module.
 *
 * @group image_to_media_swapper
 * @group security
 */
class SecurityAttackSimulationTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'system',
    'user',
    'file',
    'image',
    'media',
    'serialization',
    'image_to_media_swapper',
  ];

  /**
   * The controller to test.
   */
  protected SwapperController $controller;

  /**
   * Test user with permissions.
   */
  protected User $testUser;

  /**
   * Anonymous user without permissions.
   *
   * @var \Drupal\user\UserInterface
   */
  protected UserInterface $anonymousUser;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->installEntitySchema('user');
    $this->installEntitySchema('file');
    $this->installEntitySchema('media');
    $this->installSchema('system', ['sequences']);
    $this->installConfig(['image_to_media_swapper']);

    // Create authenticated user role with required permissions.
    $role = Role::create([
      'id' => 'media_user',
      'label' => 'Media User',
    ]);
    $role->grantPermission('create media');
    $role->grantPermission('update media');
    $role->save();

    // Create test user with permissions.
    $this->testUser = User::create([
      'name' => 'test_user',
      'uid' => 2,
      'roles' => ['media_user'],
    ]);
    $this->testUser->save();

    // Get anonymous user.
    $this->anonymousUser = User::getAnonymousUser();

    // Get controller instance.
    $this->controller = SwapperController::create($this->container);
  }

  /**
   * Tests anonymous user access to protected endpoints.
   */
  public function testAnonymousUserAccessDenied(): void {
    // Set anonymous user as current user.
    $this->container->get('current_user')->setAccount($this->anonymousUser);

    // Test token endpoint access.
    $request = Request::create('/media-api/security-tokens', 'GET');
    $request->headers->set('Referer', 'http://example.com/admin');

    $response = $this->controller->getSecurityTokens($request);
    $this->assertEquals(403, $response->getStatusCode());

    // Test main API endpoint access.
    $request = $this->createApiRequest([
      'uuid' => 'test-uuid',
      'csrf_token' => 'fake-token',
      'user_uuid' => 'fake-uuid',
    ]);

    $response = $this->controller->swapFileEntityWithMediaFromUuid($request);
    $this->assertEquals(403, $response->getStatusCode());
  }

  /**
   * Tests missing CSRF token handling.
   */
  public function testMissingCsrfTokenBlocked(): void {
    $this->container->get('current_user')->setAccount($this->testUser);

    // Test with missing CSRF token.
    $request = $this->createApiRequest([
      'uuid' => 'test-uuid',
      'user_uuid' => $this->testUser->uuid(),
    ]);

    $response = $this->controller->swapFileEntityWithMediaFromUuid($request);
    $this->assertEquals(403, $response->getStatusCode());
    $this->assertStringContainsString('Missing CSRF token', $response->getContent());
  }

  /**
   * Tests that security validation fails on the first missing requirement.
   */
  public function testSecurityValidationOrder(): void {
    $this->container->get('current_user')->setAccount($this->testUser);

    // Test 1: Missing CSRF token (should fail first).
    $request = $this->createApiRequest([
      'uuid' => 'test-uuid',
      'user_uuid' => $this->testUser->uuid(),
    ]);

    $response = $this->controller->swapFileEntityWithMediaFromUuid($request);
    $this->assertEquals(403, $response->getStatusCode());
    $this->assertStringContainsString('Missing CSRF token', $response->getContent());

    // Test 2: Invalid CSRF token (should fail on token validation).
    $request = $this->createApiRequest([
      'uuid' => 'test-uuid',
      'csrf_token' => 'fake-token',
      'user_uuid' => $this->testUser->uuid(),
    ]);

    $response = $this->controller->swapFileEntityWithMediaFromUuid($request);
    $this->assertEquals(403, $response->getStatusCode());
    $this->assertStringContainsString('Invalid CSRF token', $response->getContent());
  }

  /**
   * Tests that malicious payloads don't crash the system.
   */
  public function testMaliciousPayloadHandling(): void {
    $this->container->get('current_user')->setAccount($this->testUser);

    $malicious_payloads = [
      // XSS attempts.
      ['uuid' => '<script>alert("xss")</script>'],

      // Path traversal.
      ['uuid' => '../../../etc/passwd'],

      // Null bytes.
      ['uuid' => "test\0uuid"],
    ];

    foreach ($malicious_payloads as $payload) {
      $request = $this->createApiRequest($payload);

      try {
        $response = $this->controller->swapFileEntityWithMediaFromUuid($request);

        // Should handle gracefully. Will likely fail on missing CSRF.
        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
        $this->assertEquals(403, $response->getStatusCode());

      }
      catch (\Exception $e) {
        // Should not throw unhandled exceptions.
        $this->fail('Malicious payload caused unhandled exception: ' . $e->getMessage());
      }
    }
  }

  /**
   * Creates an API request with given payload and headers.
   */
  private function createApiRequest(array $payload, array $headers = []): Request {
    $request = Request::create(
      '/media-api/swap-file-to-media/file-uuid',
      'POST',
      [],
      [],
      [],
      ['HTTP_HOST' => 'example.com'],
      json_encode($payload)
    );

    $default_headers = [
      'Content-Type' => 'application/json',
      'Origin' => 'http://example.com',
    ];

    foreach (array_merge($default_headers, $headers) as $name => $value) {
      $request->headers->set($name, $value);
    }

    return $request;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc