Skip to content

Commit 72388d2

Browse files
committed
FilterLists: have one generic malware list with sources
1 parent 6c0c9d5 commit 72388d2

30 files changed

Lines changed: 145 additions & 94 deletions

phpstan.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ parameters:
5858
PackageUpdateJob: 'array{id: int, update_equal_refs: bool, delete_before: bool, force_dump: bool, source: string}'
5959
GitHubUserMigrateJob: 'array{id: int, old_scope: string, new_scope: string}'
6060
SecurityAdvisoryJob: 'array{source: string}'
61-
FilterListJob: 'array{list: string}'
61+
FilterListJob: 'array{list: string, source: string}'
6262

6363
JobResult: 'array{status: \App\Entity\Job::STATUS_*, message: string, vendor?: string, details?: string, exceptionMsg?: string, exceptionClass?: class-string<\Throwable>, results?: array{hooks_setup: int, hooks_failed: array<int, array{package: string, reason: mixed}>, hooks_ok_unchanged: int}}'
6464
ErroredResult: 'array{status: \App\Entity\Job::STATUS_ERRORED, message: string, exception: \Throwable}'

src/Audit/Display/AuditLogDisplayFactory.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use App\Entity\AuditRecord;
1818
use App\Entity\User;
1919
use App\FilterList\FilterLists;
20+
use App\FilterList\FilterSources;
2021
use Symfony\Bundle\SecurityBundle\Security;
2122

2223
class AuditLogDisplayFactory
@@ -222,6 +223,7 @@ public function buildSingle(AuditRecord $record): AuditLogDisplayInterface
222223
$record->attributes['entry']['package_name'],
223224
$record->attributes['entry']['version'],
224225
FilterLists::from($record->attributes['entry']['list']),
226+
FilterSources::from($record->attributes['entry']['source']),
225227
$record->attributes['entry']['reason'] ?? $record->attributes['entry']['category'],
226228
$this->buildActor($record->attributes['actor'] ?? null),
227229
$record->ip
@@ -231,6 +233,7 @@ public function buildSingle(AuditRecord $record): AuditLogDisplayInterface
231233
$record->attributes['entry']['package_name'],
232234
$record->attributes['entry']['version'],
233235
FilterLists::from($record->attributes['entry']['list']),
236+
FilterSources::from($record->attributes['entry']['source']),
234237
$record->attributes['entry']['reason'] ?? $record->attributes['entry']['category'],
235238
$this->buildActor($record->attributes['actor'] ?? null),
236239
$record->ip

src/Audit/Display/FilterListEntryAddedDisplay.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use App\Audit\AuditRecordType;
1616
use App\FilterList\FilterLists;
17+
use App\FilterList\FilterSources;
1718

1819
readonly class FilterListEntryAddedDisplay extends AbstractAuditLogDisplay
1920
{
@@ -22,6 +23,7 @@ public function __construct(
2223
public string $packageName,
2324
public string $version,
2425
public FilterLists $list,
26+
public FilterSources $source,
2527
public string $reason,
2628
ActorDisplay $actor,
2729
?string $ip,

src/Audit/Display/FilterListEntryDeletedDisplay.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use App\Audit\AuditRecordType;
1616
use App\FilterList\FilterLists;
17+
use App\FilterList\FilterSources;
1718

1819
readonly class FilterListEntryDeletedDisplay extends AbstractAuditLogDisplay
1920
{
@@ -22,6 +23,7 @@ public function __construct(
2223
public string $packageName,
2324
public string $version,
2425
public FilterLists $list,
26+
public FilterSources $source,
2527
public string $reason,
2628
ActorDisplay $actor,
2729
?string $ip,

src/Command/UpdateFilterListCommand.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace App\Command;
1414

1515
use App\FilterList\FilterLists;
16+
use App\FilterList\FilterSources;
1617
use App\Service\Locker;
1718
use App\Service\Scheduler;
1819
use Symfony\Component\Console\Command\Command;
@@ -35,8 +36,9 @@ protected function configure(): void
3536
->setName('packagist:filter-list')
3637
->setDefinition([
3738
new InputArgument('list', InputArgument::REQUIRED, 'The name of the filter list', null, FilterLists::cases()),
39+
new InputArgument('source', InputArgument::REQUIRED, 'The name of the filter source', null, FilterSources::cases()),
3840
])
39-
->setDescription('Updates all entries for a single filter list')
41+
->setDescription('Updates all entries for a single filter list source')
4042
;
4143
}
4244

@@ -50,12 +52,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5052
return self::INVALID;
5153
}
5254

55+
try {
56+
$source = FilterSources::from($input->getArgument('source'));
57+
} catch (\ValueError) {
58+
$output->writeln('source must be one of '.implode(', ', array_map(fn (FilterLists $list) => $list->value, FilterLists::cases())));
59+
60+
return self::INVALID;
61+
}
62+
5363
$lockAcquired = $this->locker->lockFilterList($list->value);
5464
if (!$lockAcquired) {
5565
return 0;
5666
}
5767

58-
$this->scheduler->scheduleFilterList($list, 0);
68+
$this->scheduler->scheduleFilterList($list, $source, 0);
5969
sleep(2); // sleep to prevent running the same command on multiple machines at around the same time via cron
6070

6171
$this->locker->unlockFilterList($list->value);

src/Controller/PackageController.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use App\Entity\Version;
3232
use App\Event\PackageAbandonedEvent;
3333
use App\Event\PackageUnabandonedEvent;
34+
use App\FilterList\FilterLists;
3435
use App\Form\Model\MaintainerRequest;
3536
use App\Form\Model\TransferPackageRequest;
3637
use App\Form\Type\AbandonedType;
@@ -74,6 +75,7 @@
7475
use Symfony\Component\Mailer\MailerInterface;
7576
use Symfony\Component\Routing\Attribute\Route;
7677
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
78+
use Symfony\Component\Routing\Requirement\EnumRequirement;
7779
use Symfony\Component\Routing\RouterInterface;
7880
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
7981
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
@@ -681,7 +683,7 @@ public function viewPackageAction(Request $req, string $name, CsrfTokenManagerIn
681683
foreach ($versions as $version) {
682684
if ($version->getNormalizedVersion() === $normalizedVersion) {
683685
$data['hasVersionsFlaggedAsMalware'][$version->getId()] = true;
684-
$data['listsFlaggingVersionsAsMalware'][$packageVersionFlaggedAsMalware->getList()->value] = $packageVersionFlaggedAsMalware->getList();
686+
$data['listsFlaggingVersionsAsMalware'][$packageVersionFlaggedAsMalware->getSource()->value] = $packageVersionFlaggedAsMalware->getSource();
685687
}
686688
}
687689
}
@@ -1656,12 +1658,12 @@ public function securityAdvisoryAction(Request $request, string $id): Response
16561658
return $this->render('package/security_advisory.html.twig', ['securityAdvisories' => $securityAdvisories, 'id' => $id]);
16571659
}
16581660

1659-
#[Route(path: '/packages/{name}/filter-lists/', name: 'view_package_filter_lists', requirements: ['name' => Package::PACKAGE_NAME_OR_EXT_REGEX])]
1660-
public function filterListsAction(Request $request, string $name): Response
1661+
#[Route(path: '/packages/{name}/filter-lists/{list}/', name: 'view_package_filter_lists', requirements: ['name' => Package::PACKAGE_NAME_OR_EXT_REGEX, 'list' => new EnumRequirement(FilterLists::class)])]
1662+
public function filterListsAction(Request $request, string $name, FilterLists $list): Response
16611663
{
16621664
/** @var FilterListEntryRepository $repo */
16631665
$repo = $this->getEM()->getRepository(FilterListEntry::class);
1664-
$entries = $repo->getPackageEntries($name);
1666+
$entries = $repo->getPackageEntries($name, $list);
16651667

16661668
$data = [];
16671669
$data['name'] = $name;
@@ -1693,6 +1695,7 @@ public function filterListsAction(Request $request, string $name): Response
16931695

16941696
$data['entries'] = $entries;
16951697
$data['count'] = \count($entries);
1698+
$data['list'] = $list;
16961699

16971700
return $this->render('package/filter_list_entries.html.twig', $data);
16981701
}

src/Entity/FilterListEntry.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace App\Entity;
1414

1515
use App\FilterList\FilterLists;
16+
use App\FilterList\FilterSources;
1617
use App\FilterList\RemoteFilterListEntry;
1718
use App\Service\IdGenerator;
1819
use Doctrine\ORM\Mapping as ORM;
@@ -51,6 +52,9 @@ class FilterListEntry
5152
#[ORM\Column]
5253
private string $publicId;
5354

55+
#[ORM\Column]
56+
private FilterSources $source;
57+
5458
public function __construct(RemoteFilterListEntry $remote)
5559
{
5660
$this->assignPublicId();
@@ -59,6 +63,7 @@ public function __construct(RemoteFilterListEntry $remote)
5963
$this->link = $remote->link;
6064
$this->list = $remote->list;
6165
$this->reason = $remote->reason;
66+
$this->source = $remote->source;
6267

6368
$this->createdAt = $this->updatedAt = new \DateTimeImmutable();
6469
}
@@ -93,6 +98,11 @@ public function getPublicId(): ?string
9398
return $this->publicId;
9499
}
95100

101+
public function getSource(): FilterSources
102+
{
103+
return $this->source;
104+
}
105+
96106
private function assignPublicId(): void
97107
{
98108
$this->publicId = IdGenerator::generateFilterListEntry();

src/Entity/FilterListEntryRepository.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace App\Entity;
1414

1515
use App\FilterList\FilterLists;
16+
use App\FilterList\FilterSources;
1617
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
1718
use Doctrine\DBAL\ArrayParameterType;
1819
use Doctrine\Persistence\ManagerRegistry;
@@ -31,11 +32,13 @@ public function __construct(
3132
/**
3233
* @return list<FilterListEntry>
3334
*/
34-
public function getEntriesInList(FilterLists $list): array
35+
public function getEntriesInList(FilterLists $list, FilterSources $source): array
3536
{
3637
return $this->createQueryBuilder('fl')
3738
->where('fl.list = :list')
39+
->andWhere('fl.source = :source')
3840
->setParameter('list', $list)
41+
->setParameter('source', $source)
3942
->getQuery()
4043
->getResult();
4144
}
@@ -47,29 +50,31 @@ public function getPackageVersionsFlaggedAsMalwareForPackage(Package $package):
4750
{
4851
return $this->createQueryBuilder('fl')
4952
->where('fl.packageName = :packageName')
50-
->andWhere('fl.list IN (:lists)')
53+
->andWhere('fl.list = :malware')
5154
->setParameter('packageName', $package->getName())
52-
->setParameter('lists', FilterLists::malwareListsValues(), ArrayParameterType::STRING)
55+
->setParameter('malware', FilterLists::MALWARE)
5356
->getQuery()
5457
->getResult();
5558
}
5659

5760
/**
5861
* @return list<FilterListEntry>
5962
*/
60-
public function getPackageEntries(string $packageName): array
63+
public function getPackageEntries(string $packageName, FilterLists $list): array
6164
{
6265
return $this->createQueryBuilder('fl')
6366
->where('fl.packageName = :packageName')
67+
->andWhere('fl.list = :list')
6468
->setParameter('packageName', $packageName)
69+
->setParameter('list', $list)
6570
->getQuery()
6671
->getResult();
6772
}
6873

6974
/**
7075
* @param array<string> $packageNames
7176
*
72-
* @return array<string, non-empty-list<array{version: string, list: string, reason: string|null, publicId: string|null}>>
77+
* @return array<string, non-empty-list<array{version: string, list: string, reason: string|null, publicId: string|null, source: string}>>
7378
*/
7479
public function getAllPackageEntriesMap(array $packageNames): array
7580
{
@@ -86,6 +91,7 @@ public function getAllPackageEntriesMap(array $packageNames): array
8691
'list' => $entry->getList()->value,
8792
'reason' => $entry->getReason(),
8893
'publicId' => $entry->getPublicId(),
94+
'source' => $entry->getSource()->value,
8995
];
9096
}
9197

src/FilterList/Dump/DumpableFilterList.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212

1313
namespace App\FilterList\Dump;
1414

15+
use App\FilterList\FilterSources;
16+
1517
final readonly class DumpableFilterList
1618
{
1719
public function __construct(
1820
public string $constraint,
1921
public string $url,
2022
public ?string $reason,
2123
public ?string $id,
24+
public string $source,
2225
) {
2326
}
2427
}

src/FilterList/Dump/FilterListDumperProvider.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ public function getEntriesForDump(array $packageNames): array
4141
foreach ($entries as $entry) {
4242
$groupedEntries[$packageName][$entry['list']][] = new DumpableFilterList(
4343
$entry['version'],
44-
$this->urlGenerator->generate('view_package_filter_lists', ['name' => $packageName], UrlGeneratorInterface::ABSOLUTE_URL),
44+
$this->urlGenerator->generate('view_package_filter_lists', ['name' => $packageName, 'list' => $entry['list']], UrlGeneratorInterface::ABSOLUTE_URL),
4545
$entry['reason'],
4646
$entry['publicId'],
47+
$entry['source'],
4748
);
4849
}
4950
}

0 commit comments

Comments
 (0)