function ComposerPluginsValidator::validate

Validates the allowed Composer plugins, both in active and stage.

File

core/modules/package_manager/src/Validator/ComposerPluginsValidator.php, line 130

Class

ComposerPluginsValidator
Validates the allowed Composer plugins, both in active and stage.

Namespace

Drupal\package_manager\Validator

Code

public function validate(SandboxValidationEvent $event) : void {
  $sandbox_manager = $event->sandboxManager;
  // When about to copy the changes from the stage directory to the active
  // directory, use the stage directory's composer instead of the active.
  // Because composer plugins may be added or removed; the only thing that
  // matters is the set of composer plugins that *will* apply — if a composer
  // plugin is being removed, that's fine.
  $dir = $event instanceof PreApplyEvent ? $sandbox_manager->getSandboxDirectory() : $this->pathLocator
    ->getProjectRoot();
  try {
    $allowed_plugins = $this->inspector
      ->getAllowPluginsConfig($dir);
  } catch (RuntimeException $exception) {
    $event->addErrorFromThrowable($exception, $this->t('Unable to determine Composer <code>allow-plugins</code> setting.'));
    return;
  }
  if ($allowed_plugins === TRUE) {
    $event->addError([
      $this->t('All composer plugins are allowed because <code>config.allow-plugins</code> is configured to <code>true</code>. This is an unacceptable security risk.'),
    ]);
    return;
  }
  // TRICKY: additional trusted Composer plugins is listed first, to allow
  // site owners who know what they're doing to use unsupported versions of
  // supported Composer plugins.
  $trusted_plugins = $this->additionalTrustedComposerPlugins + self::SUPPORTED_PLUGINS_THAT_DO_MODIFY + self::SUPPORTED_PLUGINS_THAT_DO_NOT_MODIFY;
  assert(is_array($allowed_plugins));
  // Only packages with `true` as a value are actually executed by Composer.
  $allowed_plugins = array_keys(array_filter($allowed_plugins));
  // The keys are normalized package names, and the values are the original,
  // non-normalized package names.
  $allowed_plugins = array_combine(array_map([
    __CLASS__,
    'normalizePackageName',
  ], $allowed_plugins), $allowed_plugins);
  $installed_packages = $this->inspector
    ->getInstalledPackagesList($dir);
  // Determine which plugins are both trusted by us, AND allowed by Composer's
  // configuration.
  $supported_plugins = array_intersect_key($allowed_plugins, $trusted_plugins);
  // Create an array whose keys are the names of those plugins, and the values
  // are their installed versions.
  $supported_plugins_installed_versions = array_combine($supported_plugins, array_map(fn(string $name): ?string => $installed_packages[$name]?->version, $supported_plugins));
  // Find the plugins whose installed versions aren't in the supported range.
  $unsupported_installed_versions = array_filter($supported_plugins_installed_versions, fn(?string $version, string $name): bool => $version && !Semver::satisfies($version, $trusted_plugins[$name]), ARRAY_FILTER_USE_BOTH);
  $untrusted_plugins = array_diff_key($allowed_plugins, $trusted_plugins);
  $messages = array_map(fn(string $raw_name) => $this->t('<code>@name</code>', [
    '@name' => $raw_name,
  ]), $untrusted_plugins);
  foreach ($unsupported_installed_versions as $name => $installed_version) {
    $messages[] = $this->t("<code>@name</code> is supported, but only version <code>@supported_version</code>, found <code>@installed_version</code>.", [
      '@name' => $name,
      '@supported_version' => $trusted_plugins[$name],
      '@installed_version' => $installed_version,
    ]);
  }
  if ($messages) {
    $summary = $this->formatPlural(count($messages), 'An unsupported Composer plugin was detected.', 'Unsupported Composer plugins were detected.');
    $event->addError($messages, $summary);
  }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.