generated from spatie/package-skeleton-laravel
-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from tonysm/audit-outdated-cmds
Adds audit and outdated commands and update the es-module-shims
- Loading branch information
Showing
16 changed files
with
479 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -194,6 +194,30 @@ Which will add the correct `links` tags to your head tag in the HTML document, l | |
<link rel="modulepreload" href="https://unpkg.com/[email protected]/dist/module.esm.js"> | ||
``` | ||
|
||
## Dependency Maintenance Commands | ||
|
||
Maintaining a healthy dependency list can be tricky. Here's a couple of commands to help you with this task. | ||
|
||
### Outdated Dependencies | ||
|
||
To keep your dependencies up-to-date, make sure you run the `importmap:outdated` command from time to time: | ||
|
||
```bash | ||
php artisan importmap:outdated | ||
``` | ||
|
||
This command will scan your `config/importmap.php` file, find your current versions, then use the NPM registry API to look for the latest version of the packages you're using. It also handles locally served vendor libs that you added using the `--download` flag from the `importmap:pin` command. | ||
|
||
### Auditing Dependencies | ||
|
||
If you want to a security audit on your dependecies to see if you're using a version that's been breached, run the `importmap:audit` command from time to time. Better yet, add that command to your CI build: | ||
|
||
```bash | ||
php artisan importmap:audit | ||
``` | ||
|
||
This will also scan your `config/importmap.php` file, find your current versions, then use the NPM registry API to look for vulnerabilities on your packages. It also handles locally serverd vendor libs that you added using the `--download` flag from the `importmap:pin` command. | ||
|
||
## Known Problems | ||
|
||
### Browser Console Errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
namespace Tonysm\ImportmapLaravel\Commands; | ||
|
||
use Illuminate\Console\Command; | ||
use Illuminate\Support\Str; | ||
use Tonysm\ImportmapLaravel\Npm; | ||
use Tonysm\ImportmapLaravel\VulnerablePackage; | ||
|
||
class AuditCommand extends Command | ||
{ | ||
public $signature = 'importmap:audit'; | ||
|
||
public $description = 'Run a security audit.'; | ||
|
||
public function handle(Npm $npm): int | ||
{ | ||
$vulnerablePackages = $npm->vulnerablePackages(); | ||
|
||
if ($vulnerablePackages->isEmpty()) { | ||
$this->info("No vulnerable packages found."); | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
$this->table( | ||
['Package', 'Severity', 'Vulnerable Versions', 'Vulnerability'], | ||
$vulnerablePackages | ||
->map(fn (VulnerablePackage $package) => [$package->name, $package->severity, $package->vulnerableVersions, $package->vulnerability]) | ||
->all() | ||
); | ||
|
||
$this->newLine(); | ||
|
||
$summary = $vulnerablePackages | ||
->groupBy('severity') | ||
->map(fn ($vulns) => $vulns->count()) | ||
->sortDesc() | ||
->map(fn ($count, $severity) => "$count {$severity}") | ||
->join(", "); | ||
|
||
$this->error(sprintf( | ||
"%d %s found: %s", | ||
$vulnerablePackages->count(), | ||
Str::plural('vulnerability', $vulnerablePackages->count()), | ||
$summary, | ||
)); | ||
|
||
return self::FAILURE; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php | ||
|
||
namespace Tonysm\ImportmapLaravel\Commands; | ||
|
||
use Illuminate\Console\Command; | ||
use Illuminate\Support\Str; | ||
use Tonysm\ImportmapLaravel\Npm; | ||
use Tonysm\ImportmapLaravel\OutdatedPackage; | ||
|
||
class OutdatedCommand extends Command | ||
{ | ||
public $signature = 'importmap:outdated'; | ||
|
||
public $description = 'Checks for outdated packages.'; | ||
|
||
public function handle(Npm $npm): int | ||
{ | ||
$outdatedPackages = $npm->outdatedPackages(); | ||
|
||
if ($outdatedPackages->isEmpty()) { | ||
$this->info("No outdated packages found."); | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
$this->table( | ||
['Package', 'Current', 'Latest'], | ||
$outdatedPackages | ||
->map(fn (OutdatedPackage $package) => [$package->name, $package->currentVersion, $package->latestVersion ?: $package->error]) | ||
->all(), | ||
); | ||
|
||
$this->newLine(); | ||
|
||
$this->error(sprintf( | ||
'%d outdated %s found.', | ||
$outdatedPackages->count(), | ||
Str::plural('package', $outdatedPackages->count()), | ||
)); | ||
|
||
return self::FAILURE; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
<?php | ||
|
||
namespace Tonysm\ImportmapLaravel; | ||
|
||
use Illuminate\Support\Collection; | ||
use Illuminate\Support\Facades\File; | ||
use Illuminate\Support\Facades\Http; | ||
|
||
class Npm | ||
{ | ||
private string $baseUrl = "https://registry.npmjs.org"; | ||
|
||
public function __construct(private ?string $configPath = null) | ||
{ | ||
$this->configPath ??= base_path("routes/importmap.php"); | ||
} | ||
|
||
public function outdatedPackages(): Collection | ||
{ | ||
return $this->packagesWithVersion() | ||
->reduce(function (Collection $outdatedPackages, PackageVersion $package) { | ||
$latestVersion = null; | ||
$error = null; | ||
|
||
if (! ($response = $this->getPackage($package))) { | ||
$error = "Response error"; | ||
} elseif ($response["error"] ?? false) { | ||
$error = $response["error"]; | ||
} else { | ||
$latestVersion = $this->findLatestVersion($response); | ||
|
||
if (! $this->outdated($package->version, $latestVersion)) { | ||
return $outdatedPackages; | ||
} | ||
} | ||
|
||
return $outdatedPackages->add(new OutdatedPackage( | ||
name: $package->name, | ||
currentVersion: $package->version, | ||
latestVersion: $latestVersion, | ||
error: $error, | ||
)); | ||
}, collect()); | ||
} | ||
|
||
public function vulnerablePackages(): Collection | ||
{ | ||
$data = $this->packagesWithVersion() | ||
->mapWithKeys(fn (PackageVersion $package) => [ | ||
$package->name => [$package->version], | ||
]) | ||
->all(); | ||
|
||
return $this->getAudit($data) | ||
->collect() | ||
->flatMap(function (array $vulnerabilities, string $package) { | ||
return collect($vulnerabilities) | ||
->map(fn (array $vulnerability) => new VulnerablePackage( | ||
name: $package, | ||
severity: $vulnerability['severity'], | ||
vulnerableVersions: $vulnerability['vulnerable_versions'], | ||
vulnerability: $vulnerability['title'], | ||
)); | ||
}) | ||
->sortBy([ | ||
['name', 'asc'], | ||
['severity', 'asc'], | ||
]) | ||
->values(); | ||
} | ||
|
||
private function packagesWithVersion(): Collection | ||
{ | ||
$content = File::get($this->configPath); | ||
|
||
return $this->findPackagesFromCdnMatches($content) | ||
->merge($this->findPackagesFromLocalMatches($content)) | ||
->unique('name') | ||
->values(); | ||
} | ||
|
||
private function findPackagesFromCdnMatches(string $content) | ||
{ | ||
preg_match_all('/^Importmap\:\:pin\(.*(?<=npm:|npm\/|skypack\.dev\/|unpkg\.com\/)(.*)(?=@\d+\.\d+\.\d+)@(\d+\.\d+\.\d+(?:[^\/\s"]*)).*\)\;\r?$/m', $content, $matches); | ||
|
||
if (count($matches) !== 3) { | ||
return collect(); | ||
} | ||
|
||
return collect($matches[1]) | ||
->zip($matches[2]) | ||
->map(fn ($items) => new PackageVersion(name: $items[0], version: $items[1])) | ||
->values(); | ||
} | ||
|
||
private function findPackagesFromLocalMatches(string $content) | ||
{ | ||
preg_match_all('/^Importmap::pin\("([^"]*)".*\)\; \/\/.*@(\d+\.\d+\.\d+(?:[^\s]*)).*\r?$/m', $content, $matches); | ||
|
||
if (count($matches) !== 3) { | ||
return collect(); | ||
} | ||
|
||
return collect($matches[1]) | ||
->zip($matches[2]) | ||
->map(fn ($items) => new PackageVersion(name: $items[0], version: $items[1])) | ||
->values(); | ||
} | ||
|
||
private function getPackage(PackageVersion $package) | ||
{ | ||
$response = Http::get($this->baseUrl . "/" . $package->name); | ||
|
||
if (! $response->ok()) { | ||
return null; | ||
} | ||
|
||
return $response->json(); | ||
} | ||
|
||
private function findLatestVersion(array $json) | ||
{ | ||
$latestVersion = data_get($json, "dist-tags.latest"); | ||
|
||
if ($latestVersion) { | ||
return $latestVersion; | ||
} | ||
|
||
if (! isset($json["versions"])) { | ||
return; | ||
} | ||
|
||
return collect($json["versions"]) | ||
->keys() | ||
->sort(fn ($versionA, $versionB) => version_compare($versionB, $versionA)) | ||
->values() | ||
->first(); | ||
} | ||
|
||
private function outdated(string $currentVersion, string $latestVersion) | ||
{ | ||
return version_compare($currentVersion, $latestVersion) === -1; | ||
} | ||
|
||
private function getAudit(array $packages) | ||
{ | ||
$response = Http::asJson() | ||
->post($this->baseUrl . "/-/npm/v1/security/advisories/bulk", $packages); | ||
|
||
if (! $response->ok()) { | ||
return collect(); | ||
} | ||
|
||
return $response->collect(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
namespace Tonysm\ImportmapLaravel; | ||
|
||
class OutdatedPackage | ||
{ | ||
public function __construct( | ||
public string $name, | ||
public string $currentVersion, | ||
public ?string $latestVersion = null, | ||
public ?string $error = null, | ||
) { | ||
// | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
namespace Tonysm\ImportmapLaravel; | ||
|
||
class PackageVersion | ||
{ | ||
public function __construct( | ||
public string $name, | ||
public string $version, | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
namespace Tonysm\ImportmapLaravel; | ||
|
||
class VulnerablePackage | ||
{ | ||
public function __construct( | ||
public string $name, | ||
public string $severity, | ||
public string $vulnerableVersions, | ||
public string $vulnerability, | ||
) { | ||
} | ||
} |
Oops, something went wrong.