Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ services:

App\Service\FilterListWorker:
$filterLists:
'aikido': '@App\FilterList\List\AikidoMalwareFilterList'
'aikido-malware': '@App\FilterList\List\AikidoMalwareFilterList'

Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
arguments: ['@snc_redis.cache', {prefix: 'sess:', ttl: 3600}]
Expand Down
4 changes: 2 additions & 2 deletions src/Audit/Display/AuditLogDisplayFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ public function buildSingle(AuditRecord $record): AuditLogDisplayInterface
$record->attributes['entry']['package_name'],
$record->attributes['entry']['version'],
FilterLists::from($record->attributes['entry']['list']),
$record->attributes['entry']['category'],
$record->attributes['entry']['reason'] ?? $record->attributes['entry']['category'],
$this->buildActor($record->attributes['actor'] ?? null),
$record->ip
),
Expand All @@ -229,7 +229,7 @@ public function buildSingle(AuditRecord $record): AuditLogDisplayInterface
$record->attributes['entry']['package_name'],
$record->attributes['entry']['version'],
FilterLists::from($record->attributes['entry']['list']),
$record->attributes['entry']['category'],
$record->attributes['entry']['reason'] ?? $record->attributes['entry']['category'],
$this->buildActor($record->attributes['actor'] ?? null),
$record->ip
),
Expand Down
2 changes: 1 addition & 1 deletion src/Audit/Display/FilterListEntryAddedDisplay.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function __construct(
public string $packageName,
public string $version,
public FilterLists $list,
public string $category,
public string $reason,
ActorDisplay $actor,
?string $ip,
) {
Expand Down
2 changes: 1 addition & 1 deletion src/Audit/Display/FilterListEntryDeletedDisplay.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function __construct(
public string $packageName,
public string $version,
public FilterLists $list,
public string $category,
public string $reason,
ActorDisplay $actor,
?string $ip,
) {
Expand Down
5 changes: 3 additions & 2 deletions src/Command/UpdateFilterListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ protected function configure(): void

protected function execute(InputInterface $input, OutputInterface $output): int
{
$list = FilterLists::tryFrom($input->getArgument('list'));
if (!$list) {
try {
$list = FilterLists::from($input->getArgument('list'));
} catch (\ValueError) {
$output->writeln('list must be one of ' . implode(', ', array_map(fn (FilterLists $list) => $list->value, FilterLists::cases())));

return self::INVALID;
Expand Down
8 changes: 3 additions & 5 deletions src/Controller/PackageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
use App\Entity\Version;
use App\Event\PackageAbandonedEvent;
use App\Event\PackageUnabandonedEvent;
use App\FilterList\FilterListCategories;
use App\Form\Model\MaintainerRequest;
use App\Form\Model\TransferPackageRequest;
use App\Form\Type\AbandonedType;
Expand Down Expand Up @@ -1659,12 +1658,12 @@ public function securityAdvisoryAction(Request $request, string $id): Response
return $this->render('package/security_advisory.html.twig', ['securityAdvisories' => $securityAdvisories, 'id' => $id]);
}

#[Route(path: '/packages/{name}/filter-lists/{category}', name: 'view_package_filter_lists', requirements: ['name' => Package::PACKAGE_NAME_OR_EXT_REGEX, 'category' => new EnumRequirement(FilterListCategories::class)])]
public function filterListsAction(Request $request, string $name, FilterListCategories $category): Response
#[Route(path: '/packages/{name}/filter-lists/', name: 'view_package_filter_lists', requirements: ['name' => Package::PACKAGE_NAME_OR_EXT_REGEX])]
public function filterListsAction(Request $request, string $name): Response
{
/** @var FilterListEntryRepository $repo */
$repo = $this->getEM()->getRepository(FilterListEntry::class);
$entries = $repo->getPackageEntriesForCategory($name, $category);
$entries = $repo->getPackageEntries($name);

$data = [];
$data['name'] = $name;
Expand Down Expand Up @@ -1696,7 +1695,6 @@ public function filterListsAction(Request $request, string $name, FilterListCate

$data['entries'] = $entries;
$data['count'] = \count($entries);
$data['category'] = $category;

return $this->render('package/filter_list_entries.html.twig', $data);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Entity/AuditRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,15 +321,15 @@ private static function getUserData(?User $user, string $fallbackString = 'unkno
}

/**
* @return array{package_name: string, version: string, list: string, category: string}
* @return array{package_name: string, version: string, list: string}
*/
private static function getFilterListEntryData(FilterListEntry $entry): array
{
return [
'package_name' => $entry->getPackageName(),
'version' => $entry->getVersion(),
'list' => $entry->getList()->value,
'category' => $entry->getCategory()->value,
'reason' => $entry->getReason(),
];
}
}
30 changes: 21 additions & 9 deletions src/Entity/FilterListEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@

namespace App\Entity;

use App\FilterList\FilterListCategories;
use App\FilterList\FilterLists;
use App\FilterList\RemoteFilterListEntry;
use App\Service\IdGenerator;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: FilterListEntryRepository::class)]
#[ORM\Table(name: 'filter_list_entry')]
#[ORM\UniqueConstraint(name: 'list_package_version_idx', columns: ['list', 'packageName', 'version'])]
#[ORM\Index(name: 'category_list_idx', columns: ['category', 'list'])]
#[ORM\Index(name: 'updated_at_idx', columns: ['updatedAt'])]
class FilterListEntry
{
Expand All @@ -35,27 +34,31 @@ class FilterListEntry
#[ORM\Column]
private string $version;

#[ORM\Column(type: 'text', nullable: true)]
private string|null $reason;

#[ORM\Column(nullable: true)]
private string|null $link = null;
private string|null $link;

#[ORM\Column]
private FilterLists $list;

#[ORM\Column]
private FilterListCategories $category;

#[ORM\Column]
private \DateTimeImmutable $createdAt;
#[ORM\Column]
private \DateTimeImmutable $updatedAt;

#[ORM\Column(nullable: true)]
private string|null $publicId;

public function __construct(RemoteFilterListEntry $remote)
{
$this->assignPublicId();
$this->packageName = $remote->packageName;
$this->version = $remote->version;
$this->link = $remote->link;
$this->list = $remote->list;
$this->category = $remote->category;
$this->reason = $remote->reason;

$this->createdAt = $this->updatedAt = new \DateTimeImmutable();
}
Expand All @@ -80,8 +83,17 @@ public function getList(): FilterLists
return $this->list;
}

public function getCategory(): FilterListCategories
public function getReason(): ?string
{
return $this->reason;
}
public function getPublicId(): ?string
{
return $this->publicId;
}

public function assignPublicId(): void
{
return $this->category;
$this->publicId = IdGenerator::generateFilterListEntry();
}
}
28 changes: 11 additions & 17 deletions src/Entity/FilterListEntryRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

namespace App\Entity;

use App\FilterList\FilterListCategories;
use App\FilterList\FilterLists;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\DBAL\ArrayParameterType;
Expand All @@ -32,13 +31,11 @@ public function __construct(
/**
* @return list<FilterListEntry>
*/
public function getPackageVersionsFlaggedAsMalwareInList(FilterLists $list): array
public function getEntriesInList(FilterLists $list): array
{
return $this->createQueryBuilder('fl')
->where('fl.list = :list')
->andWhere('fl.category = :category')
->setParameter('list', $list)
->setParameter('category', FilterListCategories::MALWARE)
->getQuery()
->getResult();
}
Expand All @@ -50,50 +47,47 @@ public function getPackageVersionsFlaggedAsMalwareForPackage(Package $package):
{
return $this->createQueryBuilder('fl')
->where('fl.packageName = :packageName')
->andWhere('fl.category = :category')
->andWhere('fl.list IN (:lists)')
->setParameter('packageName', $package->getName())
->setParameter('category', 'malware')
->setParameter('lists', FilterLists::malwareListsValues(), ArrayParameterType::STRING)
->getQuery()
->getResult();
}

/**
* @return list<FilterListEntry>
*/
public function getPackageEntriesForCategory(string $packageName, FilterListCategories $category): array
public function getPackageEntries(string $packageName): array
{
return $this->createQueryBuilder('fl')
->where('fl.packageName = :packageName')
->andWhere('fl.category = :category')
->setParameter('packageName', $packageName)
->setParameter('category', $category)
->getQuery()
->getResult();
}

/**
* @param array<string> $packageNames
* @return array<string, non-empty-list<array{version: string, list: string, category: string}>>
* @return array<string, non-empty-list<array{version: string, list: string, reason: string|null, publicId: string|null}>>
*/
public function getPackageVersionsFlaggedAsMalwareForPackageNames(array $packageNames): array
public function getAllPackageEntriesMap(array $packageNames): array
{
$entries = $this->createQueryBuilder('fl')
->where('fl.packageName IN (:packageNames)')
->setParameter('packageNames', $packageNames, ArrayParameterType::STRING)
->andWhere('fl.category = :category')
->setParameter('category', FilterListCategories::MALWARE)
->getQuery()
->getResult();

$malwarePackageVersions = [];
$mappedData = [];
foreach ($entries as $entry) {
$malwarePackageVersions[$entry->getPackageName()][] = [
$mappedData[$entry->getPackageName()][] = [
'version' => $entry->getVersion(),
'list' => $entry->getList()->value,
'category' => $entry->getCategory()->value,
'reason' => $entry->getReason(),
'publicId' => $entry->getPublicId(),
];
}

return $malwarePackageVersions;
return $mappedData;
}
}
3 changes: 2 additions & 1 deletion src/Entity/SecurityAdvisory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use App\SecurityAdvisory\FriendsOfPhpSecurityAdvisoriesSource;
use App\SecurityAdvisory\RemoteSecurityAdvisory;
use App\SecurityAdvisory\Severity;
use App\Service\IdGenerator;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Selectable;
Expand Down Expand Up @@ -246,7 +247,7 @@ public function calculateDifferenceScore(RemoteSecurityAdvisory $advisory): int

private function assignPackagistAdvisoryId(): void
{
$this->packagistAdvisoryId = AdvisoryIdGenerator::generate();
$this->packagistAdvisoryId = IdGenerator::generateSecurityAdvisoryId();
}

public function getSeverity(): ?Severity
Expand Down
2 changes: 1 addition & 1 deletion src/FilterList/Dump/DumpableFilterList.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public function __construct(
public string $constraint,
public string $url,
public string $category,
public ?string $reason,
public ?string $id,
) {}
}
25 changes: 9 additions & 16 deletions src/FilterList/Dump/FilterListDumperProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,19 @@ public function __construct(
* @param string[] $packageNames
* @return array<string, array<string, list<DumpableFilterList>>>
*/
public function getMalwareDataForDump(array $packageNames): array
public function getEntriesForDump(array $packageNames): array
{
$malwarePackageVersions = $this->getEM()->getRepository(FilterListEntry::class)->getPackageVersionsFlaggedAsMalwareForPackageNames($packageNames);
$allPackageEntries = $this->getEM()->getRepository(FilterListEntry::class)->getAllPackageEntriesMap($packageNames);

$groupedEntries = [];
foreach ($malwarePackageVersions as $packageName => $entries) {
$packageGroup = [];
foreach ($allPackageEntries as $packageName => $entries) {
foreach ($entries as $entry) {
$packageGroup[$entry['list']][$entry['category']][] = $entry['version'];
}

foreach ($packageGroup as $list => $categories) {
foreach ($categories as $category => $versions) {
$groupedEntries[$packageName][$list][] = new DumpableFilterList(
implode(' || ', $versions),
$this->urlGenerator->generate('view_package_filter_lists', ['name' => $packageName, 'category' => $category], UrlGeneratorInterface::ABSOLUTE_URL),
$category,
null,
);
}
$groupedEntries[$packageName][$entry['list']][] = new DumpableFilterList(
$entry['version'],
$this->urlGenerator->generate('view_package_filter_lists', ['name' => $packageName], UrlGeneratorInterface::ABSOLUTE_URL),
$entry['reason'],
$entry['publicId'],
);
}
}

Expand Down
18 changes: 0 additions & 18 deletions src/FilterList/FilterListCategories.php

This file was deleted.

9 changes: 8 additions & 1 deletion src/FilterList/FilterListResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ class FilterListResolver
/**
* @param array<FilterListEntry> $existingEntries
* @param array<RemoteFilterListEntry> $remoteEntries
* @return array<list<FilterListEntry>, list<FilterListEntry>>
* @return array{list<FilterListEntry>, list<FilterListEntry>, bool}
*/
public function resolve(array $existingEntries, array $remoteEntries): array
{
$modifiedExisting = false;
$existingMap = [];
foreach ($existingEntries as $existing) {
if (!$existing->getPublicId()) {
$existing->assignPublicId();
$modifiedExisting = true;
}

$existingMap[$existing->getPackageName()][$existing->getVersion()] = $existing;
}

Expand All @@ -51,6 +57,7 @@ public function resolve(array $existingEntries, array $remoteEntries): array
return [
$new,
$unmatched,
$modifiedExisting,
];
}
}
Loading