/ app / Jobs / GrypeSpdx.php
GrypeSpdx.php
  1  <?php
  2  
  3  namespace App\Jobs;
  4  
  5  use App\Http\Services\VulnerabilityService;
  6  use App\Models\TOE;
  7  use App\Models\TOEVulnerability;
  8  use App\Models\Vulnerability;
  9  use App\Notifications\VulnerabilityCreated;
 10  use Exception;
 11  use Illuminate\Bus\Queueable;
 12  use Illuminate\Contracts\Queue\ShouldBeUnique;
 13  use Illuminate\Contracts\Queue\ShouldQueue;
 14  use Illuminate\Database\Eloquent\Casts\AsCollection;
 15  use Illuminate\Foundation\Bus\Dispatchable;
 16  use Illuminate\Queue\InteractsWithQueue;
 17  use Illuminate\Queue\SerializesModels;
 18  use Illuminate\Support\Collection;
 19  use Illuminate\Support\Facades\Artisan;
 20  use Illuminate\Support\Facades\Notification;
 21  use Illuminate\Support\Facades\Storage;
 22  use Symfony\Component\Process\Process;
 23  
 24  class GrypeSpdx implements ShouldQueue
 25  {
 26      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 27  
 28      public Collection $toes;
 29  
 30      /**
 31       * Create a new job instance.
 32       *
 33       * @return void
 34       */
 35      public function __construct(Collection $toes)
 36      {
 37          $this->toes = $toes;
 38      }
 39  
 40      private function vulnerability_from_match($match)
 41      {
 42          $vulnerability = $match['vulnerability'];
 43          $severity = $vulnerability['severity'];
 44          $artifact = $match['artifact'];
 45  
 46          if (count($match['relatedVulnerabilities']) > 0) {
 47              $vulnerability = $match['relatedVulnerabilities'][0];
 48          }
 49  
 50          return [
 51              'vid' => $vulnerability['id'],
 52              'severity' => $severity,
 53              'state' => $match['vulnerability']['fix']['state'],
 54              'description' => $vulnerability['description'] ?? 'N/A',
 55              'artifact_name' => $artifact['name'],
 56              'artifact_version' => $artifact['version'],
 57              'artifact_type' => $artifact['type'],
 58              'source' => $vulnerability['dataSource'],
 59          ];
 60      }
 61  
 62      private function get_vulnerabilities($toe): Collection
 63      {
 64          $vulnerabilities = collect([]);
 65          $count = 0;
 66  
 67          foreach ($this->getMatches($toe) as $match) {
 68              $data = $this->vulnerability_from_match($match);
 69              $vulnerability = Vulnerability::where($data)->first();
 70  
 71              if (!$vulnerability) {
 72                  $count++;
 73                  $vulnerability = Vulnerability::create($data);
 74              }
 75  
 76              $found = TOEVulnerability
 77                  ::where('toe_id', $toe->id)
 78                  ->where('vulnerability_id', $vulnerability->id)
 79                  ->exists();
 80  
 81              if (!$found) {
 82                  $vulnerabilities->push($vulnerability);
 83                  $toe->vulnerabilities()->attach($vulnerability);
 84              }
 85          }
 86  
 87          return $vulnerabilities;
 88      }
 89  
 90      private function create_notifications(Collection $vulnerabilities, TOE $toe): void
 91      {
 92          foreach ($toe->getNotifiableUsers() as $user) {
 93              $user->notify(new VulnerabilityCreated($vulnerabilities, $toe));
 94          }
 95      }
 96  
 97      private function getMatches($toe): array
 98      {
 99          $process = new Process(['grype', Storage::path($toe->spdx_path), '-o', 'json']);
100          $process->mustRun();
101          $data = json_decode($process->getOutput(), true);
102          return $data['matches'];
103      }
104  
105      /**
106       * Execute the job.
107       *
108       * @return void
109       */
110      public function handle()
111      {
112          foreach ($this->toes as $toe) {
113              $vulnerabilities = $this->get_vulnerabilities($toe);
114  
115              if ($vulnerabilities->count() > 0) {
116                  $this->create_notifications($vulnerabilities, $toe);
117              }
118          }
119      }
120  }