Skip to content
Snippets Groups Projects
Verified Commit a61ad676 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #1630568 by sokru, Laureatus, quietone, smustgrave, ravi.shankar:...

Issue #1630568 by sokru, Laureatus, quietone, smustgrave, ravi.shankar: Validate that uploaded .po files are UTF8
parent 5e0ff5a8
No related branches found
No related tags found
16 merge requests!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!8736Update the Documention As per the Function uses.,!8513Issue #3453786: DefaultSelection should document why values for target_bundles NULL and [] behave as they do,!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3133core/modules/system/css/components/hidden.module.css,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2062Issue #3246454: Add weekly granularity to views date sort,!877Issue #2708101: Default value for link text is not saved,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493
Pipeline #293370 canceled
<?php
declare(strict_types=1);
namespace Drupal\file\Plugin\Validation\Constraint;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Validation\Attribute\Constraint;
use Symfony\Component\Validator\Constraint as SymfonyConstraint;
/**
* Defines an encoding constraint for files.
*/
#[Constraint(
id: 'FileEncoding',
label: new TranslatableMarkup('File encoding', [], ['context' => 'Validation'])
)]
class FileEncodingConstraint extends SymfonyConstraint {
/**
* The error message.
*
* @var string
*/
public string $message = "The file is encoded with %detected. It must be encoded with %encoding";
/**
* The allowed file encodings.
*
* @var array
*/
public array $encodings;
}
<?php
declare(strict_types=1);
namespace Drupal\file\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates the file encoding constraint.
*/
class FileEncodingConstraintValidator extends BaseFileConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate(mixed $value, Constraint $constraint): void {
/** @var \Drupal\file\Entity\FileInterface $file */
$file = $this->assertValueIsFile($value);
if (!$constraint instanceof FileEncodingConstraint) {
throw new UnexpectedTypeException($constraint, FileEncodingConstraint::class);
}
$encodings = $constraint->encodings;
$data = file_get_contents($file->getFileUri());
foreach ($encodings as $encoding) {
$this->validateEncoding($data, $encoding, $constraint);
}
}
/**
* Validates the encoding of the file.
*
* @param string $data
* The file data.
* @param string $encoding
* The encoding to validate.
* @param \Drupal\file\Plugin\Validation\Constraint\FileEncodingConstraint $constraint
* The constraint.
*/
protected function validateEncoding(string $data, string $encoding, FileEncodingConstraint $constraint): void {
if (mb_check_encoding($data, $encoding)) {
return;
}
$this->context->addViolation($constraint->message, [
'%encoding' => $encoding,
'%detected' => mb_detect_encoding($data),
]);
}
}
<?php
declare(strict_types=1);
namespace Drupal\Tests\file\Kernel\Plugin\Validation\Constraint;
use Drupal\file\Entity\File;
use Drupal\Tests\file\Kernel\Validation\FileValidatorTestBase;
// cspell:ignore räme
/**
* Tests the FileEncodingConstraintValidator.
*
* @group file
* @coversDefaultClass \Drupal\file\Plugin\Validation\Constraint\FileEncodingConstraintValidator
*/
class FileEncodingConstraintValidatorTest extends FileValidatorTestBase {
/**
* Tests the FileEncodingConstraintValidator.
*
* @param array $file_properties
* The properties of the file being validated.
* @param string[] $encodings
* An array of the allowed file encodings.
* @param string[] $expected_errors
* The expected error messages as string.
*
* @dataProvider providerTestFileValidateEncodings
* @covers ::validate
*/
public function testFileEncodings(array $file_properties, array $encodings, array $expected_errors): void {
$data = 'Räme';
$data = mb_convert_encoding($data, $file_properties['encoding']);
file_put_contents($file_properties['uri'], $data);
$file = File::create($file_properties);
// Test for failure.
$validators = [
'FileEncoding' => [
'encodings' => $encodings,
],
];
$violations = $this->validator->validate($file, $validators);
$actual_errors = [];
foreach ($violations as $violation) {
$actual_errors[] = $violation->getMessage();
}
$this->assertEquals($expected_errors, $actual_errors);
}
/**
* Data provider for ::testFileEncoding.
*
* @return array[][]
* The test cases.
*/
public static function providerTestFileValidateEncodings(): array {
$utf8_encoded_txt_file_properties = [
'filename' => 'druplicon.txt',
'uri' => 'public://druplicon.txt',
'status' => 0,
'encoding' => 'UTF-8',
];
$windows1252_encoded_txt_file = [
'filename' => 'druplicon-win.txt',
'uri' => 'public://druplicon-win.txt',
'status' => 1,
'encoding' => 'windows-1252',
];
return [
'UTF-8 encoded file validated with "UTF-8" encoding' => [
'file_properties' => $utf8_encoded_txt_file_properties,
'encodings' => ['UTF-8'],
'expected_errors' => [],
],
'Windows-1252 encoded file validated with "UTF-8" encoding' => [
'file_properties' => $windows1252_encoded_txt_file,
'encodings' => ['UTF-8'],
'expected_errors' => [
'The file is encoded with ASCII. It must be encoded with UTF-8',
],
],
];
}
/**
* Helper function that returns a .po file with invalid encoding.
*/
public function getInvalidEncodedPoFile() {
return <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 8\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=Windows-1252\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "Swamp"
msgstr "Räme"
EOF;
}
}
......@@ -101,6 +101,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$validators = [
'FileExtension' => ['extensions' => 'po'],
'FileSizeLimit' => ['fileLimit' => Environment::getUploadMaxSize()],
'FileEncoding' => ['encodings' => ['UTF-8']],
];
$form['file'] = [
'#type' => 'file',
......
......@@ -11,7 +11,7 @@
use Drupal\Tests\BrowserTestBase;
// cspell:ignore chien chiens deutsch januari lundi montag moutons műveletek
// cspell:ignore svibanj
// cspell:ignore svibanj räme
/**
* Tests the import of locale files.
......@@ -250,6 +250,9 @@ public function testStandalonePoFile(): void {
$this->submitForm($search, 'Filter');
$this->assertSession()->pageTextNotContains('No strings available.');
// Try importing a .po file with invalid encoding.
$this->importPoFile($this->getInvalidEncodedPoFile(), [], ['Windows-1252']);
$this->assertSession()->pageTextContains('The file is encoded with ASCII. It must be encoded with UTF-8');
}
/**
......@@ -402,10 +405,15 @@ public function testCreatedLanguageTranslation(): void {
* Contents of the .po file to import.
* @param array $options
* (optional) Additional options to pass to the translation import form.
* @param array $encodings
* (optional) The encoding of the file.
*/
public function importPoFile($contents, array $options = []) {
public function importPoFile($contents, array $options = [], array $encodings = []) {
$file_system = \Drupal::service('file_system');
$name = $file_system->tempnam('temporary://', "po_") . '.po';
foreach ($encodings as $encoding) {
$contents = mb_convert_encoding($contents, $encoding);
}
file_put_contents($name, $contents);
$options['files[file]'] = $name;
$this->drupalGet('admin/config/regional/translate/import');
......@@ -672,6 +680,24 @@ public function getPoFileWithConfigDe() {
msgid "German"
msgstr "Deutsch"
EOF;
}
/**
* Helper function that returns a .po file with invalid encoding.
*/
public function getInvalidEncodedPoFile() {
return <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 8\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=Windows-1252\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "Swamp"
msgstr "Räme"
EOF;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment