You are here

class UploadExtensions in Security Review 8

Checks for unsafe extensions in the allowed extensions settings of fields.

Hierarchy

Expanded class hierarchy of UploadExtensions

1 file declares its use of UploadExtensions
security_review.module in ./security_review.module
Site security review and reporting Drupal module.

File

src/Checks/UploadExtensions.php, line 15

Namespace

Drupal\security_review\Checks
View source
class UploadExtensions extends Check {

  /**
   * {@inheritdoc}
   */
  public function getNamespace() {
    return 'Security Review';
  }

  /**
   * {@inheritdoc}
   */
  public function getTitle() {
    return 'Allowed upload extensions';
  }

  /**
   * {@inheritdoc}
   */
  public function getMachineTitle() {
    return 'upload_extensions';
  }

  /**
   * {@inheritdoc}
   */
  public function run() {

    // If field is not enabled return with INFO.
    if (!$this
      ->moduleHandler()
      ->moduleExists('field')) {
      return $this
        ->createResult(CheckResult::INFO);
    }
    $result = CheckResult::SUCCESS;
    $findings = [];

    // Check field configuration entities.
    foreach (FieldConfig::loadMultiple() as $entity) {

      /** @var FieldConfig $entity */
      $extensions = $entity
        ->getSetting('file_extensions');
      if ($extensions != NULL) {
        $extensions = explode(' ', $extensions);
        $intersect = array_intersect($extensions, $this
          ->security()
          ->unsafeExtensions());

        // $intersect holds the unsafe extensions this entity allows.
        foreach ($intersect as $unsafe_extension) {
          $findings[$entity
            ->id()][] = $unsafe_extension;
        }
      }
    }
    if (!empty($findings)) {
      $result = CheckResult::FAIL;
    }
    return $this
      ->createResult($result, $findings);
  }

  /**
   * {@inheritdoc}
   */
  public function help() {
    $paragraphs = [];
    $paragraphs[] = $this
      ->t('File and image fields allow for uploaded files. Some extensions are considered dangerous because the files can be evaluated and then executed in the browser. A malicious user could use this opening to gain control of your site. Review <a href=":url">all fields on your site</a>.', [
      ':url' => Url::fromRoute('entity.field_storage_config.collection')
        ->toString(),
    ]);
    return [
      '#theme' => 'check_help',
      '#title' => 'Allowed upload extensions',
      '#paragraphs' => $paragraphs,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function evaluate(CheckResult $result) {
    $findings = $result
      ->findings();
    if (empty($findings)) {
      return [];
    }
    $paragraphs = [];
    $paragraphs[] = $this
      ->t('The following extensions are considered unsafe and should be removed or limited from use. Or, be sure you are not granting untrusted users the ability to upload files.');
    $items = [];
    foreach ($findings as $entity_id => $unsafe_extensions) {
      $entity = FieldConfig::load($entity_id);

      /** @var FieldConfig $entity */
      foreach ($unsafe_extensions as $extension) {
        $item = $this
          ->t('Review @type in <em>@field</em> field on @bundle', [
          '@type' => $extension,
          '@field' => $entity
            ->label(),
          '@bundle' => $entity
            ->getTargetBundle(),
        ]);

        // Try to get an edit url.
        try {
          $url_params = [
            'field_config' => $entity
              ->id(),
          ];
          if ($entity
            ->getTargetEntityTypeId() == 'node') {
            $url_params['node_type'] = $entity
              ->getTargetBundle();
          }
          $items[] = Link::createFromRoute($item, sprintf('entity.field_config.%s_field_edit_form', $entity
            ->getTargetEntityTypeId()), $url_params);
        } catch (RouteNotFoundException $e) {
          $items[] = $item;
        }
      }
    }
    return [
      '#theme' => 'check_evaluation',
      '#paragraphs' => $paragraphs,
      '#items' => $items,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function evaluatePlain(CheckResult $result) {
    $findings = $result
      ->findings();
    if (empty($findings)) {
      return '';
    }
    $output = '';
    foreach ($findings as $entity_id => $unsafe_extensions) {
      $entity = FieldConfig::load($entity_id);

      /** @var FieldConfig $entity */
      $output .= $this
        ->t('@bundle: field @field', [
        '@bundle' => $entity
          ->getTargetBundle(),
        '@field' => $entity
          ->label(),
      ]);
      $output .= "\n\t" . implode(', ', $unsafe_extensions) . "\n";
    }
    return $output;
  }

  /**
   * {@inheritdoc}
   */
  public function getMessage($result_const) {
    switch ($result_const) {
      case CheckResult::SUCCESS:
        return $this
          ->t('Only safe extensions are allowed for uploaded files and images.');
      case CheckResult::FAIL:
        return $this
          ->t('Unsafe file extensions are allowed in uploads.');
      case CheckResult::INFO:
        return $this
          ->t('Module field is not enabled.');
      default:
        return $this
          ->t('Unexpected result.');
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Check::$config protected property The configuration storage for this check.
Check::$container protected property The service container.
Check::$settings protected property Settings handler for this check.
Check::$state protected property The State system.
Check::$statePrefix protected property The check's prefix in the State system.
Check::checklist protected function Returns the Security Review Checklist service.
Check::configFactory protected function Returns the Config factory.
Check::container protected function Returns the service container.
Check::createResult public function Creates a new CheckResult for this Check.
Check::currentUser protected function Returns the current Drupal user.
Check::database protected function Returns the database connection.
Check::enable public function Enables the check. Has no effect if the check was not skipped.
Check::entityTypeManager protected function Returns the entity type manager.
Check::getMachineNamespace public function Returns the namespace of the check.
Check::id final public function Returns the identifier constructed using the namespace and title values.
Check::isSkipped public function Returns whether the check is skipped. Checks are not skipped by default.
Check::kernel protected function Returns the Drupal Kernel.
Check::lastResult public function Returns the last stored result of the check.
Check::lastRun public function Returns the timestamp the check was last run.
Check::moduleHandler protected function Returns the module handler.
Check::runCli public function Same as run(), but used in CLI context such as Drush. 2
Check::security protected function Returns the Security Review Security service.
Check::securityReview protected function Returns the Security Review service.
Check::settings public function Returns the check-specific settings' handler.
Check::skip public function Marks the check as skipped.
Check::skippedBy public function Returns the user the check was skipped by.
Check::skippedOn public function Returns the timestamp the check was last skipped on.
Check::storeResult public function Stores a result in the state system.
Check::storesFindings public function Returns whether the findings should be stored or reproduced when needed. 2
Check::__construct public function Initializes the configuration storage and the settings handler. 2
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UploadExtensions::evaluate public function Returns the evaluation page of a result. Overrides Check::evaluate
UploadExtensions::evaluatePlain public function Evaluates a CheckResult and returns a plaintext output. Overrides Check::evaluatePlain
UploadExtensions::getMachineTitle public function Returns the machine name of the check. Overrides Check::getMachineTitle
UploadExtensions::getMessage public function Converts a result integer to a human-readable result message. Overrides Check::getMessage
UploadExtensions::getNamespace public function Returns the namespace of the check. Overrides Check::getNamespace
UploadExtensions::getTitle public function Returns the human-readable title of the check. Overrides Check::getTitle
UploadExtensions::help public function Returns the check-specific help page. Overrides Check::help
UploadExtensions::run public function The actual procedure of carrying out the check. Overrides Check::run