Browse Source

Merge pull request #1058 from glaubinix/t/security-advisory-date

Security Advisories: store reported date
Jordi Boggiano 5 years ago
parent
commit
cbcdbd9197

+ 18 - 2
src/Packagist/WebBundle/Entity/SecurityAdvisory.php

@@ -18,6 +18,8 @@ use Packagist\WebBundle\SecurityAdvisory\RemoteSecurityAdvisory;
  */
 class SecurityAdvisory
 {
+    public const PACKAGIST_ORG = 'https://packagist.org';
+
     /**
      * @ORM\Id
      * @ORM\Column(type="integer")
@@ -51,7 +53,7 @@ class SecurityAdvisory
     private $cve;
 
     /**
-     * @ORM\Column(type="string")
+     * @ORM\Column(type="text")
      */
     private $affectedVersions;
 
@@ -60,11 +62,21 @@ class SecurityAdvisory
      */
     private $source;
 
+    /**
+     * @ORM\Column(type="datetime")
+     */
+    private $reportedAt;
+
     /**
      * @ORM\Column(type="datetime")
      */
     private $updatedAt;
 
+    /**
+     * @ORM\Column(type="string", nullable=true)
+     */
+    private $composerRepository;
+
     public function __construct(RemoteSecurityAdvisory $advisory, string $source)
     {
         $this->source = $source;
@@ -79,9 +91,12 @@ class SecurityAdvisory
             $this->title !== $advisory->getTitle() ||
             $this->link !== $advisory->getLink() ||
             $this->cve !== $advisory->getCve() ||
-            $this->affectedVersions !== $advisory->getAffectedVersions()
+            $this->affectedVersions !== $advisory->getAffectedVersions() ||
+            $this->reportedAt != $advisory->getDate() ||
+            $this->composerRepository !== $advisory->getComposerRepository()
         ) {
             $this->updatedAt = new \DateTime();
+            $this->reportedAt = $advisory->getDate();
         }
 
         $this->remoteId = $advisory->getId();
@@ -90,6 +105,7 @@ class SecurityAdvisory
         $this->link = $advisory->getLink();
         $this->cve = $advisory->getCve();
         $this->affectedVersions = $advisory->getAffectedVersions();
+        $this->composerRepository = $advisory->getComposerRepository();
     }
 
     public function getRemoteId(): string

+ 2 - 2
src/Packagist/WebBundle/Entity/SecurityAdvisoryRepository.php

@@ -18,7 +18,7 @@ class SecurityAdvisoryRepository extends ServiceEntityRepository
         $sql = 'SELECT s.*
             FROM security_advisory s
             WHERE s.packageName = :name
-            ORDER BY s.id DESC';
+            ORDER BY s.reportedAt DESC, s.id DESC';
 
         return $this->getEntityManager()->getConnection()
             ->fetchAll($sql, ['name' => $name]);
@@ -26,7 +26,7 @@ class SecurityAdvisoryRepository extends ServiceEntityRepository
 
     public function searchSecurityAdvisories(array $packageNames, int $updatedSince): array
     {
-        $sql = 'SELECT s.packageName, s.remoteId, s.title, s.link, s.cve, s.affectedVersions, s.source
+        $sql = 'SELECT s.packageName, s.remoteId, s.title, s.link, s.cve, s.affectedVersions, s.source, s.reportedAt, s.composerRepository
             FROM security_advisory s
             WHERE s.updatedAt >= :updatedSince ' .
             (count($packageNames) > 0 ? ' AND s.packageName IN (:packageNames)' : '')

+ 57 - 6
src/Packagist/WebBundle/SecurityAdvisory/RemoteSecurityAdvisory.php

@@ -2,6 +2,8 @@
 
 namespace Packagist\WebBundle\SecurityAdvisory;
 
+use Packagist\WebBundle\Entity\SecurityAdvisory;
+
 class RemoteSecurityAdvisory
 {
     /** @var string */
@@ -16,8 +18,12 @@ class RemoteSecurityAdvisory
     private $link;
     /** @var ?string */
     private $cve;
+    /** @var \DateTime */
+    private $date;
+    /** @var string|null */
+    private $composerRepository;
 
-    public function __construct(string $id, string $title, string $packageName, string $affectedVersions, string $link, $cve)
+    public function __construct(string $id, string $title, string $packageName, string $affectedVersions, string $link, $cve, \DateTime $date, ?string $composerRepository)
     {
         $this->id = $id;
         $this->title = $title;
@@ -25,6 +31,8 @@ class RemoteSecurityAdvisory
         $this->affectedVersions = $affectedVersions;
         $this->link = $link;
         $this->cve = $cve;
+        $this->date = $date;
+        $this->composerRepository = $composerRepository;
     }
 
     public function getId(): string
@@ -57,19 +65,62 @@ class RemoteSecurityAdvisory
         return $this->cve;
     }
 
+    public function getDate(): \DateTime
+    {
+        return $this->date;
+    }
+
+    public function getComposerRepository(): ?string
+    {
+        return $this->composerRepository;
+    }
+
     public static function createFromFriendsOfPhp(string $fileNameWithPath, array $info): RemoteSecurityAdvisory
     {
-        $affectedVersion = implode('|', array_map(function (array $branchInfo) {
-            return implode(',', $branchInfo['versions']);
-        }, $info['branches']));
+        $date = null;
+        if (preg_match('#(\d{4}-\d{2}-\d{2})#', basename($fileNameWithPath), $matches)) {
+            $date = new \DateTime($matches[1] . ' 00:00:00');
+        }
+
+        $affectedVersions = [];
+        $lowestBranchDate = null;
+        foreach ($info['branches'] as $branchInfo) {
+            $affectedVersions[] = implode(',', $branchInfo['versions']);
+            if (!$date && isset($branchInfo['time']) && is_int($branchInfo['time'])) {
+                $branchDate = new \DateTime('@' . $branchInfo['time']);
+                if (!$lowestBranchDate || $branchDate < $lowestBranchDate) {
+                    $lowestBranchDate = $branchDate;
+                }
+            }
+        }
+
+        if (!$date) {
+            if ($lowestBranchDate) {
+                $date = $lowestBranchDate;
+            } else {
+                $date = (new \DateTime())->setTime(0, 0, 0);
+            }
+        }
+
+        // If the value is not set then the default value is https://packagist.org
+        $composerRepository = SecurityAdvisory::PACKAGIST_ORG;
+        if (isset($info['composer-repository'])) {
+            if ($info['composer-repository'] === false) {
+                $composerRepository = null;
+            } else {
+                $composerRepository = $info['composer-repository'];
+            }
+        }
 
         return new RemoteSecurityAdvisory(
             $fileNameWithPath,
             $info['title'],
             str_replace('composer://', '', $info['reference']),
-            $affectedVersion,
+            implode('|', $affectedVersions),
             $info['link'],
-            $info['cve'] ?? null
+            $info['cve'] ?? null,
+            $date,
+            $composerRepository
         );
     }
 }

+ 4 - 1
src/Packagist/WebBundle/Tests/SecurityAdvisory/RemoteSecurityAdvisoryTest.php

@@ -2,6 +2,7 @@
 
 namespace Packagist\WebBundle\Tests\SecurityAdvisory;
 
+use Packagist\WebBundle\Entity\SecurityAdvisory;
 use Packagist\WebBundle\SecurityAdvisory\RemoteSecurityAdvisory;
 use PHPUnit\Framework\TestCase;
 
@@ -15,7 +16,7 @@ class RemoteSecurityAdvisoryTest extends TestCase
             'cve' => null,
             'branches' => [
                 '1.x' => [
-                    'time' => '2017-05-15 09:09:00',
+                    'time' => 1494806400,
                     'versions' => ['<1.2'],
                 ],
             ],
@@ -28,5 +29,7 @@ class RemoteSecurityAdvisoryTest extends TestCase
         $this->assertNull($advisory->getCve());
         $this->assertSame('<1.2', $advisory->getAffectedVersions());
         $this->assertSame('3f/pygmentize', $advisory->getPackageName());
+        $this->assertSame('2017-05-15 00:00:00', $advisory->getDate()->format('Y-m-d H:i:s'));
+        $this->assertSame(SecurityAdvisory::PACKAGIST_ORG, $advisory->getComposerRepository());
     }
 }