Verified Commit 3a661cf7 authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #3379102 by phenaproxima, Wim Leers, borisson_: Add validation...

Issue #3379102 by phenaproxima, Wim Leers, borisson_: Add validation constraint to type: label + type: text: disallow control characters
parent ded815fb
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -64,13 +64,11 @@ label:
  translatable: true
  constraints:
    Regex:
      # Forbid any kind of line ending:
      # - Windows: `\r\n`
      # - old macOS: `\r`
      # - *nix: `\n`
      pattern: '/(\r\n|\r|\n)/'
      # Forbid any kind of control character.
      # @see https://stackoverflow.com/a/66587087
      pattern: '/([^\PC])/u'
      match: false
      message: 'Labels are not allowed to span multiple lines.'
      message: 'Labels are not allowed to span multiple lines or contain control characters.'

required_label:
  type: label
@@ -93,6 +91,18 @@ text:
  type: string
  label: 'Text'
  translatable: true
  constraints:
    Regex:
      # Disallow all control characters except for tabs (ASCII 9, 0x09) as well
      # as carriage returns (ASCII 13, 0x0D) and line feeds (ASCII 10, 0x0A),
      # which are used for line endings:
      # - Windows: `\r\n`
      # - old macOS: `\r`
      # - *nix: `\n`
      # @see https://stackoverflow.com/a/66587087
      pattern: '/([^\PC\x09\x0a\x0d])/u'
      match: false
      message: 'Text is not allowed to contain control characters, only visible characters.'

# A UUID.
uuid:
+1 −1
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ public function testLabelValidation(): void {
    // key, it is impossible for the generic ::testLabelValidation()
    // implementation in the base class to know at which property to expect a
    // validation error. Hence it is hardcoded in this case.
    $this->assertValidationErrors(['settings.label' => "Labels are not allowed to span multiple lines."]);
    $this->assertValidationErrors(['settings.label' => "Labels are not allowed to span multiple lines or contain control characters."]);
  }

}
+9 −0
Original line number Diff line number Diff line
@@ -119,6 +119,15 @@ views.field.numeric:
    format_plural_string:
      type: plural_label
      label: 'Plural variants'
      constraints:
        Regex:
          # Normally, labels cannot contain invisible control characters. In this particular
          # case, an invisible character (ASCII 3, 0x03) is used to encode translation
          # information, so carve out an exception for that only.
          # @see \Drupal\views\Plugin\views\field\NumericField
          pattern: '/([^\PC\x03])/u'
          match: false
          message: 'Labels are not allowed to span multiple lines or contain control characters.'
    prefix:
      type: label
      label: 'Prefix'
+1 −1
Original line number Diff line number Diff line
@@ -318,7 +318,7 @@ public function testLabelValidation(): void {
    }

    static::setLabel($this->entity, "Multi\nLine");
    $this->assertValidationErrors([$this->entity->getEntityType()->getKey('label') => "Labels are not allowed to span multiple lines."]);
    $this->assertValidationErrors([$this->entity->getEntityType()->getKey('label') => "Labels are not allowed to span multiple lines or contain control characters."]);
  }

  /**
+77 −0
Original line number Diff line number Diff line
@@ -72,4 +72,81 @@ public function testDefaultConfigHashValidation(): void {
    $this->assertSame("'invalid_key' is not a supported key.", (string) $violations[0]->getMessage());
  }

  /**
   * Data provider for ::testSpecialCharacters().
   *
   * @return array[]
   *   The test cases.
   */
  public function providerSpecialCharacters(): array {
    $data = [];

    for ($code_point = 0; $code_point < 32; $code_point++) {
      $data["label $code_point"] = [
        'system.site',
        'name',
        mb_chr($code_point),
        'Labels are not allowed to span multiple lines or contain control characters.',
      ];
      $data["text $code_point"] = [
        'system.maintenance',
        'message',
        mb_chr($code_point),
        'Text is not allowed to contain control characters, only visible characters.',
      ];
    }
    // Line feeds (ASCII 10) and carriage returns (ASCII 13) are used to create
    // new lines, so they are allowed in text data, along with tabs (ASCII 9).
    $data['text 9'][3] = $data['text 10'][3] = $data['text 13'][3] = NULL;

    // Ensure emoji are allowed.
    $data['emoji in label'] = [
      'system.site',
      'name',
      '😎',
      NULL,
    ];
    $data['emoji in text'] = [
      'system.maintenance',
      'message',
      '🤓',
      NULL,
    ];

    return $data;
  }

  /**
   * Tests that special characters are not allowed in labels or text data.
   *
   * @param string $config_name
   *   The name of the simple config to test with.
   * @param string $property
   *   The config property in which to embed a control character.
   * @param string $character
   *   A special character to embed.
   * @param string|null $expected_error_message
   *   The expected validation error message, if any.
   *
   * @dataProvider providerSpecialCharacters
   */
  public function testSpecialCharacters(string $config_name, string $property, string $character, ?string $expected_error_message): void {
    $config = $this->config($config_name)
      ->set($property, "This has a special character: $character");

    $violations = $this->container->get('config.typed')
      ->createFromNameAndData($config->getName(), $config->get())
      ->validate();

    if ($expected_error_message === NULL) {
      $this->assertCount(0, $violations);
    }
    else {
      $code_point = mb_ord($character);
      $this->assertCount(1, $violations, "Character $code_point did not raise a constraint violation.");
      $this->assertSame($property, $violations[0]->getPropertyPath());
      $this->assertSame($expected_error_message, (string) $violations[0]->getMessage());
    }
  }

}