image_to_media_swapper-2.x-dev/tests/src/Kernel/ContentVerificationServiceTest.php
tests/src/Kernel/ContentVerificationServiceTest.php
<?php
namespace Drupal\Tests\image_to_media_swapper\Kernel;
use Drupal\Core\File\FileSystemInterface;
use Drupal\KernelTests\KernelTestBase;
use Drupal\file\Entity\File;
/**
* Tests the content verification service.
*
* @group image_to_media_swapper
*/
class ContentVerificationServiceTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'user',
'file',
'field',
'image_to_media_swapper',
'media',
'filter',
];
/**
* The content verification service.
*
* @var \Drupal\image_to_media_swapper\ContentVerificationService
*/
protected $contentVerificationService;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installConfig(['system']);
$this->installEntitySchema('file');
$this->installSchema('file', ['file_usage']);
// Get the service.
$this->contentVerificationService = $this->container->get('image_to_media_swapper.content_verification');
$this->fileSystem = $this->container->get('file_system');
$directory = 'public://';
// Ensure the files directory exists.
$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
}
/**
* Creates a test file with the given MIME type and content.
*
* @param string $filename
* The filename.
* @param string $mime_type
* The MIME type.
* @param string $content
* The file content.
*
* @return string
* The file URI.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function createTestFile(string $filename, string $mime_type, string $content): string {
$uri = 'public://' . $filename;
file_put_contents($uri, $content);
// Create a file entity.
$file = File::create([
'uri' => $uri,
'filename' => $filename,
'filemime' => $mime_type,
'status' => 1,
]);
$file->save();
return $uri;
}
/**
* Tests verification of valid image files.
*/
public function testValidImageVerification(): void {
// Create a simple PNG image with valid header.
$png_header = hex2bin('89504e470d0a1a0a') . str_repeat('x', 100);
$png_uri = $this->createTestFile('test.png', 'image/png', $png_header);
// Verify the PNG file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($png_uri), 'image/png');
$this->assertTrue($result['verified'], 'PNG file should be verified');
$this->assertEmpty($result['errors'], 'No errors should be reported for valid PNG');
}
/**
* Tests verification of invalid image files.
*/
public function testInvalidImageVerification(): void {
// Create a text file with fake image extension.
$fake_image = "This is not an image file";
$fake_uri = $this->createTestFile('fake.jpg', 'image/jpeg', $fake_image);
// Verify the fake image file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($fake_uri), 'image/jpeg');
$this->assertFalse($result['verified'], 'Fake image should not be verified');
$this->assertNotEmpty($result['errors'], 'Errors should be reported for fake image');
}
/**
* Tests verification of text files.
*/
public function testTextFileVerification(): void {
// Create a valid text file.
$text_content = "This is a plain text file.\nIt contains normal text content.";
$text_uri = $this->createTestFile('test.txt', 'text/plain', $text_content);
// Verify the text file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($text_uri), 'text/plain');
$this->assertTrue($result['verified'], 'Text file should be verified');
}
/**
* Tests verification of text files with binary content.
*/
public function testBinaryAsTextVerification(): void {
// Create a binary file with text extension.
$binary_content = file_get_contents(__DIR__ . '/../../fixtures/binary_sample.bin');
if (!$binary_content) {
// If fixture file doesn't exist, create some binary content.
$binary_content = '';
for ($i = 0; $i < 100; $i++) {
$binary_content .= chr(mt_rand(0, 31));
}
}
$binary_uri = $this->createTestFile('binary.txt', 'text/plain', $binary_content);
// Verify the binary file masquerading as text.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($binary_uri), 'text/plain');
$this->assertFalse($result['verified'], 'Binary file with text extension should not be verified as text');
}
/**
* Tests detection of malicious content in files.
*/
public function testMaliciousContentDetection(): void {
// Create an image file with embedded PHP code.
$malicious_content = hex2bin('89504e470d0a1a0a') . "<?php system('ls -la'); ?>" . str_repeat('x', 50);
$malicious_uri = $this->createTestFile('malicious.png', 'image/png', $malicious_content);
// Verify the malicious file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($malicious_uri), 'image/png');
$this->assertFalse($result['verified'], 'Malicious file should not be verified');
$this->assertNotEmpty($result['errors'], 'Errors should be reported for malicious file');
// Create an SVG with embedded JavaScript (XSS vulnerability)
$svg_with_js = '<?xml version="1.0" standalone="no"?>'
. '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
. '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">'
. '<script>alert("XSS")</script>'
. '<circle cx="50" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />'
. '</svg>';
// Create the test file in the public directory.
$svg_uri = $this->createTestFile('xss.svg', 'image/svg+xml', $svg_with_js);
// Ensure the file was created successfully.
$svg_path = $this->fileSystem->realpath($svg_uri);
$this->assertFileExists($svg_path, 'SVG test file should exist');
// Verify the SVG with JavaScript.
$result = $this->contentVerificationService->verifyFileContent($svg_path, 'image/svg+xml');
// Assertions.
$this->assertFalse($result['verified'], 'SVG with JavaScript should not be verified');
// Check for error message about script content.
$error_string = implode(' ', $result['errors']);
$this->assertStringContainsString('script', $error_string, 'Error should mention script content');
}
/**
* Tests verification of mismatched MIME types.
*/
public function testMimeMismatchDetection(): void {
// Create a PNG with JPEG MIME type.
$png_header = hex2bin('89504e470d0a1a0a') . str_repeat('x', 100);
$mismatched_uri = $this->createTestFile('mismatch.jpg', 'image/jpeg', $png_header);
// Verify the mismatched file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($mismatched_uri), 'image/jpeg');
// This is a subtle case - may or may not be verified depending on how
// strict the check is. The test is checking that the actual_type is
// detected and differs from declared type.
$this->assertNotEmpty($result['actual_type'], 'Actual type should be detected');
if ($result['actual_type'] !== 'image/jpeg') {
$this->assertNotEquals('image/jpeg', $result['actual_type'], 'Actual type should differ from declared type');
}
}
/**
* Tests verification of PDF files.
*/
public function testPdfVerification(): void {
// Create a minimal valid PDF.
$pdf_content = "%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R >>\nendobj\n4 0 obj\n<< /Length 22 >>\nstream\nBT /F1 12 Tf 100 700 Td (Test PDF) Tj ET\nendstream\nendobj\nxref\n0 5\n0000000000 65535 f\n0000000010 00000 n\n0000000060 00000 n\n0000000115 00000 n\n0000000200 00000 n\ntrailer << /Root 1 0 R /Size 5 >>\nstartxref\n270\n%%EOF";
$pdf_uri = $this->createTestFile('test.pdf', 'application/pdf', $pdf_content);
// Verify the PDF file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($pdf_uri), 'application/pdf');
$this->assertTrue($result['verified'], 'Valid PDF should be verified');
}
/**
* Tests verification of XML files.
*/
public function testXmlVerification(): void {
// Create a valid XML file.
$xml_content = '<?xml version="1.0" encoding="UTF-8"?><root><element attribute="value">Text content</element></root>';
$xml_uri = $this->createTestFile('test.xml', 'application/xml', $xml_content);
// Verify the XML file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($xml_uri), 'application/xml');
$this->assertTrue($result['verified'], 'Valid XML should be verified');
// Create an XML with XXE vulnerability.
$xxe_xml = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]><root>&xxe;</root>';
$xxe_uri = $this->createTestFile('xxe.xml', 'application/xml', $xxe_xml);
// Verify the XXE file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($xxe_uri), 'application/xml');
$this->assertFalse($result['verified'], 'XML with XXE should not be verified');
$this->assertNotEmpty($result['errors'], 'Errors should be reported for XML with XXE');
}
/**
* Tests handling of non-existent files.
*/
public function testNonExistentFile(): void {
$result = $this->contentVerificationService->verifyFileContent('/non/existent/file.txt', 'text/plain');
$this->assertFalse($result['verified'], 'Non-existent file should not be verified');
$this->assertStringContainsString('File does not exist', $result['errors'][0], 'Error should mention file does not exist');
}
/**
* Tests the system handles empty files correctly.
*/
public function testEmptyFile(): void {
// Create an empty file.
$empty_uri = $this->createTestFile('empty.txt', 'text/plain', ' ');
// Verify the empty file.
$result = $this->contentVerificationService->verifyFileContent($this->fileSystem->realpath($empty_uri), 'text/plain');
// Empty files should still be validated as text files.
$this->assertTrue($result['verified'], 'Empty text file should be verified as text');
}
}
