diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4be1fa2..ccdd4b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: matrix: include: - mediawiki_version: '1.39' - smw_version: dev-master + smw_version: '5.1.0' php_version: 8.1 database_type: mysql database_image: "mariadb:10" diff --git a/Makefile b/Makefile index d824fc2..d2e046a 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,9 @@ DB_IMAGE?="" SMW_VERSION?=4.1.3 MM_VERSION ?= 3.1.0 +# PHP extensions +PHP_EXTENSIONS?=gd,zlib + # composer # Enables "composer update" inside of extension COMPOSER_EXT?=true diff --git a/extension.json b/extension.json index e87b215..12afbf4 100644 --- a/extension.json +++ b/extension.json @@ -6,7 +6,7 @@ "Robin van der Wiel", "Yvar Nanlohij" ], - "version": "2.1.1", + "version": "2.1.2", "url": "https://www.archixl.nl", "descriptionmsg": "sic-desc", "license-name": "GPL-2.0+", diff --git a/i18n/nl.json b/i18n/nl.json index 88fb684..339ece3 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -124,6 +124,7 @@ "sic-selection-generic-error": "Er is een fout opgetreden tijdens tekst selectie.", "sic-show-screenshot-full-size": "Klik om de volledige grootte te bekijken", "sic-broken-comments-below": "Er zijn opmerkingen die niet meer gekoppeld zijn aan een tekstselectie op deze pagina", - "sc-status-open": "Open", - "sc-status-completed": "Afgesloten" -} \ No newline at end of file + "sc-status-open": "open", + "sc-status-completed": "afgehandeld" +} + diff --git a/src/DBHandler.php b/src/DBHandler.php index 6606757..4a5628f 100644 --- a/src/DBHandler.php +++ b/src/DBHandler.php @@ -385,7 +385,7 @@ public static function selectAllPages() : array { foreach ( $res as $page ) { $title = Title::newFromID( $page ); if ( $title ) { - $pages[] = $title->getText(); + $pages[] = $title->getFullText(); } } diff --git a/src/Store/ImageSaver.php b/src/Store/ImageSaver.php index 5f1915e..ce47757 100644 --- a/src/Store/ImageSaver.php +++ b/src/Store/ImageSaver.php @@ -43,6 +43,16 @@ public function save( $data ): ?string { $data = str_replace( ' ', '+', $data ); $data = base64_decode($data); + try { + $image = \imagecreatefromstring( $data ); + } catch ( \Exception $e ) { + return null; + } + + if ( $image === false ) { + return null; + } + if ( $data === false ) { return null; } diff --git a/tests/phpunit/Integration/Store/ImageSaverTest.php b/tests/phpunit/Integration/Store/ImageSaverTest.php new file mode 100644 index 0000000..e67c1fb --- /dev/null +++ b/tests/phpunit/Integration/Store/ImageSaverTest.php @@ -0,0 +1,88 @@ +titleMock = $this->createMock( \Title::class ); + $this->titleMock->method( 'getId' )->willReturn( 123 ); + + Hooks::$imageSaveDirectory = sys_get_temp_dir(); + } + + /** + * Test that ImageSaver returns null for invalid XSS payload + */ + public function testInvalidXssPayload(): void { + $imageSaver = new ImageSaver( $this->titleMock ); + + $invalidPayload = 'data:image/png;base64,PHNjcmlwdD5hbGVydCgneHNzJyk7PC9zY3JpcHQ+'; + + $result = $imageSaver->save( $invalidPayload ); + + $this->assertNull( $result, 'ImageSaver should return null for XSS payload' ); + } + + /** + * Test that ImageSaver returns a filename for valid payload + */ + public function testValidPayload(): void { + $imageSaver = new ImageSaver( $this->titleMock ); + + $validPayload = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; + + $result = $imageSaver->save( $validPayload ); + + $this->assertNotNull( $result, 'ImageSaver should return a filename for valid payload' ); + + $filePath = Hooks::$imageSaveDirectory . '/' . $result; + $this->assertFileExists( $filePath, 'Image file should be created' ); + + if ( file_exists( $filePath ) ) { + unlink( $filePath ); + } + } + + /** + * Test that ImageSaver returns null for non-image data + */ + public function testNonImageData(): void { + $imageSaver = new ImageSaver( $this->titleMock ); + + // Base64 encoded text that's not an image + $nonImagePayload = 'data:image/png;base64,SGVsbG8gV29ybGQ='; + + $result = $imageSaver->save( $nonImagePayload ); + + // Assert that the result is null for non-image data + $this->assertNull( $result, 'ImageSaver should return null for non-image data' ); + } + + /** + * Test that ImageSaver returns null for unsupported image type + */ + public function testUnsupportedImageType(): void { + $imageSaver = new ImageSaver( $this->titleMock ); + + $unsupportedTypePayload = 'data:image/webp;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; + + $result = $imageSaver->save( $unsupportedTypePayload ); + + $this->assertNull( $result, 'ImageSaver should return null for unsupported image type' ); + } +} \ No newline at end of file