mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Co-authored-by: Aditya Tripathi <aditya@climactic.co>
113 lines
4.1 KiB
PHP
113 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\ImageScan;
|
|
|
|
final readonly class Finding
|
|
{
|
|
/**
|
|
* @param list<string> $aliases
|
|
*/
|
|
public function __construct(
|
|
public string $id,
|
|
public array $aliases,
|
|
public string $severity,
|
|
public string $package,
|
|
public string $version,
|
|
public string $type,
|
|
public string $artifactId,
|
|
public ?string $fixedIn,
|
|
public string $fixState,
|
|
public bool $kev,
|
|
public ?float $epss,
|
|
public ?float $risk,
|
|
public string $summary,
|
|
/** New findings are always "app"; kept because stored findings carry it. */
|
|
public string $layer = 'app',
|
|
public ?float $cvssScore = null,
|
|
public string $title = '',
|
|
public string $displayId = '',
|
|
public string $namespace = '',
|
|
public string $matchType = '',
|
|
) {}
|
|
|
|
/**
|
|
* @return array{id: string, aliases: list<string>, severity: string, package: string, version: string, type: string, artifactId: string, fixedIn: ?string, fixState: string, kev: bool, epss: ?float, risk: ?float, summary: string, layer: string, cvssScore: ?float, title: string, displayId: string, namespace: string, matchType: string}
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return [
|
|
'id' => $this->id,
|
|
'aliases' => $this->aliases,
|
|
'severity' => $this->severity,
|
|
'package' => $this->package,
|
|
'version' => $this->version,
|
|
'type' => $this->type,
|
|
'artifactId' => $this->artifactId,
|
|
'fixedIn' => $this->fixedIn,
|
|
'fixState' => $this->fixState,
|
|
'kev' => $this->kev,
|
|
'epss' => $this->epss,
|
|
'risk' => $this->risk,
|
|
'summary' => $this->summary,
|
|
'layer' => $this->layer,
|
|
'cvssScore' => $this->cvssScore,
|
|
'title' => $this->title,
|
|
'displayId' => $this->displayId,
|
|
'namespace' => $this->namespace,
|
|
'matchType' => $this->matchType,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public static function fromArray(array $data): self
|
|
{
|
|
$id = (string) ($data['id'] ?? '');
|
|
$aliases = array_values(array_map('strval', (array) ($data['aliases'] ?? [])));
|
|
$severity = (string) ($data['severity'] ?? 'unknown');
|
|
$package = (string) ($data['package'] ?? '');
|
|
$version = (string) ($data['version'] ?? '');
|
|
$title = ScannerText::storedTitle((string) ($data['title'] ?? ''), $package, $version, $severity);
|
|
$displayId = (string) ($data['displayId'] ?? '');
|
|
|
|
return new self(
|
|
id: $id,
|
|
aliases: $aliases,
|
|
severity: $severity,
|
|
package: $package,
|
|
version: $version,
|
|
type: (string) ($data['type'] ?? ''),
|
|
artifactId: (string) ($data['artifactId'] ?? ''),
|
|
fixedIn: isset($data['fixedIn']) ? (string) $data['fixedIn'] : null,
|
|
fixState: (string) ($data['fixState'] ?? ''),
|
|
kev: (bool) ($data['kev'] ?? false),
|
|
epss: isset($data['epss']) ? (float) $data['epss'] : null,
|
|
risk: isset($data['risk']) ? (float) $data['risk'] : null,
|
|
summary: (string) ($data['summary'] ?? ''),
|
|
layer: (string) ($data['layer'] ?? 'app'),
|
|
cvssScore: is_numeric($data['cvssScore'] ?? null) ? (float) $data['cvssScore'] : null,
|
|
title: $title,
|
|
displayId: $displayId !== '' ? $displayId : self::displayIdFor($id, $aliases),
|
|
namespace: (string) ($data['namespace'] ?? ''),
|
|
matchType: (string) ($data['matchType'] ?? ''),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* A CVE ID from the finding ID or its aliases, else the finding ID.
|
|
*
|
|
* @param list<string> $aliases
|
|
*/
|
|
public static function displayIdFor(string $id, array $aliases): string
|
|
{
|
|
foreach ([$id, ...$aliases] as $candidate) {
|
|
if (preg_match('/^CVE-\d{4}-\d{4,}$/i', $candidate) === 1) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
|
|
return $id;
|
|
}
|
|
}
|