Your IP : 216.73.216.220


Current Path : /home/rtorresani/www/vendor/magento/composer-dependency-version-audit-plugin/src/
Upload File :
Current File : //home/rtorresani/www/vendor/magento/composer-dependency-version-audit-plugin/src/Plugin.php

<?php
/**
 * Copyright © 2021 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);

namespace Magento\ComposerDependencyVersionAuditPlugin;

use Composer\Composer;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Request;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer;
use Composer\Installer\PackageEvent;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginEvents;
use Composer\Plugin\PluginInterface;
use Composer\Plugin\PrePoolCreateEvent;
use Composer\Repository\ComposerRepository;
use Composer\Repository\FilterRepository;
use Composer\Repository\RepositoryInterface;
use Composer\Package\PackageInterface;
use Exception;
use Magento\ComposerDependencyVersionAuditPlugin\Utils\Version;

/**
 * Composer's entry point for the plugin
 */
class Plugin implements PluginInterface, EventSubscriberInterface
{

    /**#@+
     * URL For Public Packagist Repo
     */
    const URL_REPO_PACKAGIST = 'https://repo.packagist.org';

    /**
     * @var Composer
     */
    private $composer;

    /**
     * @var Version
     */
    private $versionSelector;

    /**
     * @var array
     */
    private $nonFixedPackages;

    /**#@+
     * Constant for VBE ALLOW LIST
     */
    private const VBE_ALLOW_LIST = [
        'vertexinc',
        'yotpo',
        'klarna',
        'amzn',
        'dotmailer',
        'braintree',
        'paypal',
        'gene'
    ];

    /**
     * Initialize dependencies
     * @param Version|null $version
     */
    public function __construct(Version $version = null)
    {
        if ($version) {
            $this->versionSelector = $version;
        } else {
            $this->versionSelector = new Version();
        }
    }

    /**
     * @inheritdoc
     */
    public function activate(Composer $composer, IOInterface $io)
    {
        // Declaration must exist
    }

    /**
     * @inheritdoc
     */
    public function deactivate(Composer $composer, IOInterface $io)
    {
        // Declaration must exist
    }

    /**
     * @inheritdoc
     */
    public function uninstall(Composer $composer, IOInterface $io)
    {
        // Declaration must exist
    }

    /**
     * Event subscriber
     *
     * @return array
     */
    public static function getSubscribedEvents(): array
    {
        $events = [
            Installer\PackageEvents::PRE_PACKAGE_INSTALL => 'packageUpdate',
            Installer\PackageEvents::PRE_PACKAGE_UPDATE => 'packageUpdate'
        ];

        if ((int)explode('.', Composer::VERSION)[0] === 2) {
            $events[PluginEvents::PRE_POOL_CREATE] = 'prePoolCreate';
        }

        return $events;
    }

    /**
     * Get all package installations that use non-fixed version constraints (IE: 2.4.*, ^2.4, etc.)
     * this needs to be done for Composer V1 installs since prePoolCreate event doesn't exist in V1
     *
     * @param Request $request
     * @return array
     */
    private function getNonFixedConstraintList(Request $request): array
    {
        if (!$this->nonFixedPackages) {
            $constraintList = [];
            foreach ($request->getJobs() as $job) {
                if ($job['cmd'] === 'install' &&
                    (strpbrk($job['constraint']->getPrettyString(), "*^-~") ||
                        preg_match('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $job['constraint']->getPrettyString()
                        )
                    )
                ) {
                    $constraintList[$job['packageName']] = true;
                }
            }
            $this->nonFixedPackages = $constraintList;
        }
        return $this->nonFixedPackages;
    }

    /**
     * Event listener for PrePoolCreate event that is used for composer V2
     *
     * @param PrePoolCreateEvent $event
     */
    public function prePoolCreate(PrePoolCreateEvent $event): void
    {
        if (!$this->nonFixedPackages) {
            $constraintList = [];

            /**
             * get all packages that are in the composer.json under require section, this will be the only time
             * we will be able to get constraints for packages in the require section as this request data isn't
             * shared in the installer event on composer v2
             */
            foreach ($event->getRequest()->getRequires() as $name => $constraint) {
                $prettyString = $constraint->getPrettyString();
                $multiConstraint = preg_match('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $prettyString);
                if (strpbrk($prettyString, "*^-~") || $multiConstraint){
                    $constraintList[$name] = true;
                }
            }

            /**
             * get all sub packages that are now requirements for new packages to install and store their constraints.
             */
            foreach ($event->getPackages() as $package) {
                foreach ($package->getRequires() as $name => $constraint) {
                    $prettyConstraint = $constraint->getPrettyConstraint();
                    $multiConstraint = preg_match('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $prettyConstraint);
                    if (strpbrk($prettyConstraint, "*^-~")|| $multiConstraint)
                        $constraintList[$name] = true;
                }
            }

            $this->nonFixedPackages = $constraintList;
        }
    }

    /**
     * Event listener for Package Install or Update
     *
     * @param PackageEvent $event
     * @return void
     * @throws Exception
     */
    public function packageUpdate(PackageEvent $event): void
    {
        /** @var  OperationInterface */
        $operation = $event->getOperation();
        $this->composer = $event->getComposer();

        /** @var PackageInterface $package  */
        $package = method_exists($operation, 'getPackage')
            ? $operation->getPackage()
            : $operation->getInitialPackage();

        $packageName = $package->getName();
        $privateRepoVersion = '';
        $publicRepoVersion = '';
        $privateRepoUrl = '';
        list($namespace, $project) = explode("/", $packageName);
        $isPackageVBE = in_array($namespace, self::VBE_ALLOW_LIST, true);

        if ((int)explode('.', Composer::VERSION)[0] === 1) {
            $this->getNonFixedConstraintList($event->getRequest());
        }

        if(!$isPackageVBE) {
            foreach ($this->composer->getRepositoryManager()->getRepositories() as $repository) {
                $found = $this->versionSelector->findBestCandidate($this->composer, $packageName, $repository);
                $repoUrl = "";
                /** @var RepositoryInterface $repository */
                if ($repository instanceof ComposerRepository) {
                    $repoUrl = $repository->getRepoConfig()['url'];

                } else if ($repository instanceof FilterRepository) {
                    $repoUrl = $repository->getRepository()->getRepoConfig()['url'];
                }
                if ($found) {
                    if ($repoUrl && strpos($repoUrl, self::URL_REPO_PACKAGIST) !== false) {
                        $publicRepoVersion = $found->getFullPrettyVersion();
                    } else {
                        $currentPrivateRepoVersion = $found->getFullPrettyVersion();
                        //private repo version should hold highest version of package
                        if (empty($privateRepoVersion) || version_compare($currentPrivateRepoVersion, $privateRepoVersion, '>')) {
                            $privateRepoVersion = $currentPrivateRepoVersion;
                            $privateRepoUrl = $repoUrl;
                        }
                    }
                }
            }

            if ($privateRepoVersion && $publicRepoVersion && version_compare($publicRepoVersion, $privateRepoVersion, '>')) {
                $exceptionMessage = "Higher matching version {$publicRepoVersion} of {$packageName} was found in public repository packagist.org 
                             than {$privateRepoVersion} in private {$privateRepoUrl}. Public package might've been taken over by a malicious entity, 
                             please investigate and update package requirement to match the version from the private repository";

                if ($this->nonFixedPackages && array_key_exists($packageName, $this->nonFixedPackages)) {
                    throw new Exception($exceptionMessage);
                } else {
                    $event->getIO()->writeError('<warning>' . $exceptionMessage . '</warning>');
                }
            }
        }
    }
}