/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/AssetMapper/Command/ImportMapAuditCommand.php
204 строки
7 KB
Dariusz Ruminski
PHP CS Fixer: enable static_lambda
19 дек 2025, 11:33
19 дек 2025, 11:33
3274fbc
Код
Авторство
О чём код?
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\AssetMapper\Command; use Symfony\Component\AssetMapper\ImportMap\ImportMapAuditor; use Symfony\Component\AssetMapper\ImportMap\ImportMapPackageAuditVulnerability; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand(name: 'importmap:audit', description: 'Check for security vulnerability advisories for dependencies')] class ImportMapAuditCommand extends Command { private const SEVERITY_COLORS = [ 'critical' => 'red', 'high' => 'red', 'medium' => 'yellow', 'low' => 'default', 'unknown' => 'default', ]; private SymfonyStyle $io; public function __construct( private readonly ImportMapAuditor $importMapAuditor, ) { parent::__construct(); } protected function configure(): void { $this ->addOption( name: 'format', mode: InputOption::VALUE_REQUIRED, description: \sprintf('The output format ("%s")', implode(', ', $this->getAvailableFormatOptions())), default: 'txt', ) ->setHelp(<<<'EOT' The <info>--format</info> option specifies the format of the command output: <info>php %command.full_name% --format=json</info> EOT ); } protected function initialize(InputInterface $input, OutputInterface $output): void { $this->io = new SymfonyStyle($input, $output); } protected function execute(InputInterface $input, OutputInterface $output): int { $format = $input->getOption('format'); $audit = $this->importMapAuditor->audit(); return match ($format) { 'txt' => $this->displayTxt($audit), 'json' => $this->displayJson($audit), default => throw new \InvalidArgumentException(\sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))), }; } private function displayTxt(array $audit): int { $rows = []; $packagesWithoutVersion = []; $vulnerabilitiesCount = array_map(static fn () => 0, self::SEVERITY_COLORS); foreach ($audit as $packageAudit) { if (!$packageAudit->version) { $packagesWithoutVersion[] = $packageAudit->package; } foreach ($packageAudit->vulnerabilities as $vulnerability) { $rows[] = [ \sprintf('<fg=%s>%s</>', self::SEVERITY_COLORS[$vulnerability->severity] ?? 'default', ucfirst($vulnerability->severity)), $vulnerability->summary, $packageAudit->package, $packageAudit->version ?? 'n/a', $vulnerability->firstPatchedVersion ?? 'n/a', $vulnerability->url, ]; ++$vulnerabilitiesCount[$vulnerability->severity]; } } $packagesCount = \count($audit); $packagesWithoutVersionCount = \count($packagesWithoutVersion); if (!$rows && !$packagesWithoutVersionCount) { $this->io->info('No vulnerabilities found.'); return self::SUCCESS; } if ($rows) { $table = $this->io->createTable(); $table->setHeaders([ 'Severity', 'Title', 'Package', 'Version', 'Patched in', 'More info', ]); $table->addRows($rows); $table->render(); $this->io->newLine(); } $this->io->text(\sprintf('%d package%s found: %d audited / %d skipped', $packagesCount, 1 === $packagesCount ? '' : 's', $packagesCount - $packagesWithoutVersionCount, $packagesWithoutVersionCount, )); if (0 < $packagesWithoutVersionCount) { $this->io->warning(\sprintf('Unable to retrieve versions for package%s: %s', 1 === $packagesWithoutVersionCount ? '' : 's', implode(', ', $packagesWithoutVersion) )); } if ([] !== $rows) { $vulnerabilityCount = 0; $vulnerabilitySummary = []; foreach ($vulnerabilitiesCount as $severity => $count) { if (!$count) { continue; } $vulnerabilitySummary[] = \sprintf('%d %s', $count, ucfirst($severity)); $vulnerabilityCount += $count; } $this->io->text(\sprintf('%d vulnerabilit%s found: %s', $vulnerabilityCount, 1 === $vulnerabilityCount ? 'y' : 'ies', implode(' / ', $vulnerabilitySummary), )); } return self::FAILURE; } private function displayJson(array $audit): int { $vulnerabilitiesCount = array_map(static fn () => 0, self::SEVERITY_COLORS); $json = [ 'packages' => [], 'summary' => $vulnerabilitiesCount, ]; foreach ($audit as $packageAudit) { $json['packages'][] = [ 'package' => $packageAudit->package, 'version' => $packageAudit->version, 'vulnerabilities' => array_map(static fn (ImportMapPackageAuditVulnerability $v) => [ 'ghsa_id' => $v->ghsaId, 'cve_id' => $v->cveId, 'url' => $v->url, 'summary' => $v->summary, 'severity' => $v->severity, 'vulnerable_version_range' => $v->vulnerableVersionRange, 'first_patched_version' => $v->firstPatchedVersion, ], $packageAudit->vulnerabilities), ]; foreach ($packageAudit->vulnerabilities as $vulnerability) { ++$json['summary'][$vulnerability->severity]; } } $this->io->write(json_encode($json)); return 0 < array_sum($json['summary']) ? self::FAILURE : self::SUCCESS; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestOptionValuesFor('format')) { $suggestions->suggestValues($this->getAvailableFormatOptions()); } } /** @return string[] */ private function getAvailableFormatOptions(): array { return ['txt', 'json']; } }