Verified Commit fce65ce3 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3392903 by borisson_, Wim Leers, phenaproxima: Validate inputs of...

Issue #3392903 by borisson_, Wim Leers, phenaproxima: Validate inputs of TypeResolver::resolveExpression(): only allow %parent, %type and %key

(cherry picked from commit 94e4d471)
parent 53c02df1
Loading
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -75,7 +75,8 @@ public static function resolveDynamicTypeName(string $name, mixed $data): string
   *   The value the expression resolves to, or the given expression if it
   *   cannot be resolved.
   *
   * @todo Validate the expression in https://www.drupal.org/project/drupal/issues/3392903
   * @throws \LogicException
   *    Exception thrown if $expression is not a valid dynamic type expression.
   */
  public static function resolveExpression(string $expression, array|TypedDataInterface $data): string {
    if ($data instanceof TypedDataInterface) {
@@ -87,8 +88,16 @@ public static function resolveExpression(string $expression, array|TypedDataInte
    }

    $parts = explode('.', $expression);
    $previous_name = NULL;
    // Process each value part, one at a time.
    while ($name = array_shift($parts)) {
      if (str_starts_with($name, '%') && !in_array($name, ['%parent', '%key', '%type'], TRUE)) {
        throw new \LogicException('`' . $expression . '` is not a valid dynamic type expression. Dynamic type expressions must contain at least `%parent`, `%key`, or `%type`.`');
      }
      if ($name === '%type' && $previous_name !== '%parent') {
        throw new \LogicException('`%type` can only used when immediately preceded by `%parent` in `' . $expression . '`');
      }
      $previous_name = $name;
      if (!is_array($data) || !isset($data[$name])) {
        // Key not found, return original value
        return $expression;
+3 −3
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ public function testEntityTypeIdIsStatic(): void {
  }

  /**
   * Tests getting the entity type ID from the parent property path.
   * Tests getting the entity type ID.
   *
   * @param string $constraint_value
   *   The entity type ID to supply to the validation constraint. Must be a
@@ -74,9 +74,9 @@ public function testEntityTypeIdIsStatic(): void {
   *   a bundle.
   *
   * @testWith ["%parent.entity_type_id", "entity_test_with_bundle"]
   *   ["%paren.entity_type_id", "%paren.entity_type_id"]
   *   ["%key", "bundle"]
   */
  public function testEntityTypeIdFromParent(string $constraint_value, string $resolved_entity_type_id): void {
  public function testDynamicEntityType(string $constraint_value, string $resolved_entity_type_id): void {
    /** @var \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager */
    $typed_data_manager = $this->container->get('typed_data_manager');

+27 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\Core\Config;

use Drupal\Core\Config\Schema\TypeResolver;
use Drupal\Tests\UnitTestCase;

/**
 * @covers \Drupal\Core\Config\Schema\TypeResolver
 * @group config
 */
class TypeResolverTest extends UnitTestCase {

  /**
   * @testWith ["[foo.%bar.qux]", "`foo.%bar.qux` is not a valid dynamic type expression. Dynamic type expressions must contain at least `%parent`, `%key`, or `%type`.`", {"foo": "foo"}]
   *           ["[%paren.field_type]", "`%paren.field_type` is not a valid dynamic type expression. Dynamic type expressions must contain at least `%parent`, `%key`, or `%type`."]
   *           ["[something.%type]", "`%type` can only used when immediately preceded by `%parent` in `something.%type`", {"something": "something"}]
   */
  public function testInvalidType(string $name, string $message, array $data = []): void {
    $this->expectException(\LogicException::class);
    $this->expectExceptionMessage($message);
    TypeResolver::resolveDynamicTypeName($name, $data);
  }

}