HEX
Server: nginx/1.18.0
System: Linux test-ipsremont 5.4.0-214-generic #234-Ubuntu SMP Fri Mar 14 23:50:27 UTC 2025 x86_64
User: ips (1000)
PHP: 8.0.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //tmp/php_security_advisories/security-advisories-master/validator.php
<?php

// validates that all security advisories are valid

if (!is_file($autoloader = __DIR__.'/vendor/autoload.php')) {
    echo "Dependencies are not installed, please run 'composer install' first!\n";
    exit(1);
}
require $autoloader;

use Composer\Config;
use Composer\IO\NullIO;
use Composer\Repository\ComposerRepository;
use Composer\Repository\RepositoryInterface;
use Composer\Util\HttpDownloader;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser;

final class Validate extends Command
{
    private Parser $parser;
    private array $composerRepositories = array();
    private Config $composerConfig;
    private HttpDownloader $httpDownloader;

    public function __construct()
    {
        parent::__construct('validate');

        $this->parser = new Parser();
        $this->composerConfig = new Config(false);
        $this->composerConfig->merge(array('config' => array('cache-dir' => sys_get_temp_dir().'/php-security-advisories')));
        $this->httpDownloader = new HttpDownloader(new NullIO(), $this->composerConfig);
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        $advisoryFilter = function (SplFileInfo $file) {
            if ($file->isFile() && __DIR__ === $file->getPath()) {
                return false; // We want to skip root files
            }

            if ($file->isDir()) {
                if (__DIR__.DIRECTORY_SEPARATOR.'vendor' === $file->getPathname()) {
                    return false; // We want to skip the vendor dir
                }

                $dirName = $file->getFilename();
                if ('.' === $dirName[0]) {
                    return false; // Exclude hidden folders (.git and IDE folders at the root)
                }
            }

            return true; // any other file gets checks and any other folder gets iterated
        };

        $isAcceptableVersionConstraint = function ($versionString) {
            return (bool) preg_match('/^
                [<>]=?
                (([1-9]\d*)|0)
                (\.(([1-9]\d*)|0))*
                (-(alpha|beta|rc|p(atch)?)[1-9]\d*)?
            $/x', $versionString);
        };

        $messages = array();

        /* @var $dir \Traversable<\SplFileInfo> */
        $dir = new \RecursiveIteratorIterator(new RecursiveCallbackFilterIterator(new \RecursiveDirectoryIterator(__DIR__), $advisoryFilter));

        $progress = new ProgressBar($io, count(iterator_to_array($dir)));
        $progress->start();

        foreach ($dir as $file) {
            if (!$file->isFile()) {
                $progress->advance();

                continue;
            }

            $path = str_replace(__DIR__.DIRECTORY_SEPARATOR, '', $file->getPathname());

            if ('yaml' !== $file->getExtension()) {
                $messages[$path][] = 'The file extension should be ".yaml".';
                continue;
            }

            try {
                $data = $this->parser->parse(file_get_contents($file));

                // validate first level keys
                if ($keys = array_diff(array_keys($data), array('reference', 'branches', 'title', 'link', 'cve', 'composer-repository'))) {
                    foreach ($keys as $key) {
                        $messages[$path][] = sprintf('Key "%s" is not supported.', $key);
                    }
                }

                // required keys
                foreach (array('reference', 'title', 'link', 'branches') as $key) {
                    if (!isset($data[$key])) {
                        $messages[$path][] = sprintf('Key "%s" is required.', $key);
                    }
                }

                if (isset($data['reference'])) {
                    if (0 !== strpos($data['reference'], 'composer://')) {
                        $messages[$path][] = 'Reference must start with "composer://"';
                    } else {
                        $composerPackage = substr($data['reference'], 11);

                        if (str_replace(DIRECTORY_SEPARATOR, '/', dirname($path)) !== $composerPackage) {
                            $messages[$path][] = 'Reference composer package must match the folder name';
                        }

                        if (!isset($data['composer-repository'])) {
                            $data['composer-repository'] = 'https://repo.packagist.org';
                        }

                        if (!empty($data['composer-repository'])) {
                            $composerRepository = $this->getComposerRepository($data['composer-repository']);

                            $found = false;
                            foreach ($composerRepository->search($composerPackage, RepositoryInterface::SEARCH_NAME) as $package) {
                                if ($package['name'] === $composerPackage) {
                                    $found = true;
                                    break;
                                }
                            }
                            if (!$found) {
                                $messages[$path][] = sprintf('Invalid composer package (not found in repository %s)', $data['composer-repository']);
                            }
                        }
                    }
                }

                if (isset($data['cve']) && !str_starts_with($data['cve'], 'CVE-')) {
                    $messages[$path][] = '"cve" must be a valid CVE number when provided.';
                }

                if (!isset($data['branches'])) {
                    $progress->advance();

                    continue; // Don't validate branches when not set to avoid notices
                }

                if (!is_array($data['branches'])) {
                    $messages[$path][] = '"branches" must be an array.';
                    $progress->advance();

                    continue;  // Don't validate branches when not set to avoid notices
                }

                $upperBoundWithoutLowerBound = null;

                foreach ($data['branches'] as $name => $branch) {
                    if (!preg_match('/^([\d.\-]+(\.x)?(-dev)?|master|main)$/', $name)) {
                        $messages[$path][] = sprintf('Invalid branch name "%s".', $name);
                    }

                    if ($keys = array_diff(array_keys($branch), array('time', 'versions'))) {
                        foreach ($keys as $key) {
                            $messages[$path][] = sprintf('Key "%s" is not supported for branch "%s".', $key, $name);
                        }
                    }

                    if (!array_key_exists('time', $branch)) {
                        $messages[$path][] = sprintf('Key "time" is required for branch "%s".', $name);
                    } elseif (isset($branch['time'])) {
                        $timestamp = is_int($branch['time']) ? $branch['time'] : strtotime($branch['time']);

                        if ($timestamp === false) {
                            $messages[$path][] = sprintf('"time" is invalid for branch "%s", given "%s".', $name, $branch['time']);
                        } elseif ($timestamp > time()) {
                            $messages[$path][] = sprintf('"time" cannot be in the future for branch "%s", given "%s".', $name, $branch['time']);
                        }
                    }

                    if (!isset($branch['versions'])) {
                        $messages[$path][] = sprintf('Key "versions" is required for branch "%s".', $name);
                    } elseif (!is_array($branch['versions'])) {
                        $messages[$path][] = sprintf('"versions" must be an array for branch "%s".', $name);
                    } else {
                        $upperBound = null;
                        $hasMin = false;
                        foreach ($branch['versions'] as $version) {
                            if (!$isAcceptableVersionConstraint($version)) {
                                $messages[$path][] = sprintf('Version constraint "%s" is not in an acceptable format.', $version);
                            }

                            if ('<' === substr($version, 0, 1)) {
                                if (null !== $upperBound) {
                                    $messages[$path][] = sprintf('"versions" cannot have multiple upper bounds for branch "%s".', $name);
                                    continue;
                                }

                                $upperBound = $version;
                                continue;
                            }
                            if ('>' === substr($version, 0, 1)) {
                                if ($hasMin) {
                                    $messages[$path][] = sprintf('"versions" cannot have multiple lower bounds for branch "%s".', $name);
                                    continue;
                                }

                                $hasMin = true;
                            }
                        }

                        if (null === $upperBound) {
                            $messages[$path][] = sprintf('"versions" must have an upper bound for branch "%s".', $name);
                        }

                        if (!$hasMin && null === $upperBoundWithoutLowerBound) {
                            $upperBoundWithoutLowerBound = $upperBound;
                        }

                        // Branches can omit the lower bound only if their upper bound is the same than for other branches without lower bound.
                        if (!$hasMin && $upperBoundWithoutLowerBound !== $upperBound) {
                            $messages[$path][] = sprintf('"versions" must have a lower bound for branch "%s" to avoid overlapping lower branches.', $name);
                        }
                    }
                }

                if (isset($data['cve']) && $data['cve'] !== $file->getBasename('.yaml')) {
                    $messages[$path][] = sprintf('The filename should be %s.yaml.', $data['cve']);
                }

            } catch (ParseException $e) {
                $messages[$path][] = sprintf('YAML is not valid (%s).', $e->getMessage());
            }

            $progress->advance();
        }

        $progress->finish();

        $io->newLine();

        if ($messages) {
            $io->error(sprintf('Found %s issue%s in %s file%s.',
                $issues = array_sum(array_map('count', $messages)),
                1 === $issues ? '' : 's', $files = count($messages),
                1 === $files ? '' : 's'
            ));
            $table = new Table($io);

            $table->setHeaders(array('File', 'Issues'));

            $files = array_keys($messages);
            $lastFile = array_pop($files);

            foreach ($messages as $file => $issues) {
                $table->addRow(array(
                    new TableCell($file, array('rowspan' => count($issues))),
                    array_shift($issues)
                ));

                foreach ($issues as $issue) {
                    $table->addRow(array($issue));
                }

                if ($file !== $lastFile) {
                    $table->addRow(new TableSeparator());
                }
            }

            $table->render();
        } else {
            $io->success('No issues found.');
        }

        return count($messages);
    }

    private function getComposerRepository($uri): ComposerRepository
    {
        if (!isset($this->composerRepositories[$uri])) {
            $repository = new ComposerRepository(
                array(
                    'url' => $uri,
                ),
                new NullIO(),
                $this->composerConfig,
                $this->httpDownloader
            );

            $this->composerRepositories[$uri] = $repository;
        }

        return $this->composerRepositories[$uri];
    }
}

final class Validator extends Application
{
    protected function getCommandName(InputInterface $input): ?string
    {
        return 'validate';
    }

    protected function getDefaultCommands(): array
    {
        $defaultCommands = parent::getDefaultCommands();

        $defaultCommands[] = new Validate();

        return $defaultCommands;
    }

    public function getDefinition(): InputDefinition
    {
        $inputDefinition = parent::getDefinition();

        $inputDefinition->setArguments();

        return $inputDefinition;
    }
}

$application = new Validator();

$application->run();