PHP code example of gumslone / laravel-vulns

1. Go to this page and download the library: Download gumslone/laravel-vulns library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

gumslone / laravel-vulns example snippets


use Gumslone\Vulns\VulnSearch;

$search = app(VulnSearch::class);              // Laravel
// $search = new VulnSearch([$osv, $nvd, …]);  // plain PHP

$vulns = $search->searchPurl('pkg:npm/[email protected]');
$vulns = $search->searchCpe('cpe:2.3:a:prasathmani:tiny_file_manager:2.6:*:*:*:*:*:*:*');
$vulns = $search->searchCommit('https://github.com/owner/repo/commit/bf04e5f2'); // or a bare sha
$vulns = $search->search(new PackageData(name: 'lodash', version: '4.17.20', ecosystem: 'npm'));

// One entry point for anything a user pastes — advisory id (CVE, GHSA, EUVD,
// PYSEC-, RUSTSEC-, GO-, MAL-, RHSA-, DSA-, USN-, …), purl, CPE, commit sha,
// or any package URL. Unrecognisable input throws.
$vulns = $search->searchAny('CVE-2021-44228');
$vulns = $search->searchAny('cpe:2.3:a:tukaani:xz:5.6.0:*:*:*:*:*:*:*');
$vulns = $search->searchAny('https://github.com/vrana/adminer/releases/download/v5.5.1/adminer.zip');

// URL search understands commit pages natively; with
// gumslone/laravel-package-url installed (suggested, not 

if ($search->errors()) {
    logger()->warning('Vulnerability sources failed', $search->errors());
}

$search->only('nvd')->searchCpe($cpe);              // one source
$search->only(['osv', 'github'])->searchPurl($purl); // a subset
$search->except('snyk')->search($package);           // everything but one

$search->availableSources(); // ['osv','github','nvd','cve_search','euvd','snyk',
                             //  'oss_index','redhat','shodan_cvedb','mitre','vulncheck']
$search->sources();          // the enabled subset this instance will query

$search->prioritize(['nvd', 'osv'])->search($package); // trust NVD's score first

$search->preferLatest()->search($package);
// or globally: VULNS_MERGE=latest

$fresh = $search->latest('CVE-2021-44228');   // ?VulnerabilityData
$search->errors();                            // feeds that failed — null + errors ≠ "gone"

$vuln->cvssV3Score;        // 9.8  — severity  (also cvssV4Score, cvssV2Score)
$vuln->effectiveCvssScore(); // newest standard first: v4 → v3 → v2
$vuln->epssScore;          // 0.94 — probability of exploitation (0..1)
$vuln->epssPercentile;     // 0.999 — relative to all scored CVEs
$vuln->isKnownExploited;   // true — listed in CISA KEV

$change = $fresh->changesSince($stored);   // VulnChange

$change->impact();      // ChangeImpact::None | Minor | Major
$change->isMajor();     // score increased OR downgraded, severity shift,
                        // affected ranges edited, a fix appeared, listed in
                        // CISA KEV, or EPSS crossed the 0.1 triage threshold
$change->changes;       // [ChangeType::ScoreIncreased, ...]
$change->details;       // ['cvss_v3_score' => [5.0, 8.1], ...]
$change->summary();     // "score increased (5 → 8.1), description updated"

$change = $search->refresh($stored);   // ?VulnChange — null when no source knows it any more
$change?->current;                     // the fresh merged record, ready to store
$change?->isMajor();

$byPackage = $search->searchBatch([
    'lodash'  => PackageData::fromPurl('pkg:npm/[email protected]'),
    'guzzle'  => PackageData::fromPurl('pkg:composer/guzzlehttp/[email protected]'),
]);
$byPackage['lodash']; // VulnerabilityData[]

$search->fetchById('CVE-2021-44228');
$search->fetchById('GHSA-jfh8-c2jp-5v3q');

$search->searchBatch(['lodash' => $pkg]);
$search->coverage()['lodash'];
// ['queried' => ['osv', 'github', 'nvd'], 'skipped' => ['snyk', 'mitre'], 'failed' => ['euvd']]

$report = $search->report(['lodash' => $lodash, 'left-pad' => $leftPad]);

$report->for('lodash');            // VulnerabilityData[]
$report->errors;                   // ['nvd' => '503 Service Unavailable']
$report->isComplete();             // false — a source failed or was cut short
$report->isConclusive('left-pad'); // may an EMPTY answer be shown as clean?
$report->inconclusiveKeys();       // packages to flag as "not covered / under-reported"
json_encode($report);              // {results, errors, coverage}

$search->search(new PackageData(name: 'lodash', version: '5.0.0', ecosystem: 'npm'));
// "< 1.0.0"            → dropped   (every range readable, none matches)
// ">= 4.0.0, < 5.1.0"  → kept
// "< 6.0.0-beta.1"     → kept      (can't be ordered — "can't tell" is never "not affected")
// no ranges at all     → kept

$search->filterByVersion(false)->search($pkg);   // everything, unfiltered

$search->completeAliases()->search($package);            // OSV + GitHub, at most 50 lookups per search
$search->completeAliases(['osv'], max: 20)->search($package);
// or globally: VULNS_COMPLETE_ALIASES=true

$vuln->aliases;   // ['GHSA-p77j-4mvh-x3m3', 'GO-2026-4762']
$vuln->altId();   // 'GHSA-p77j-4mvh-x3m3' — the GHSA when known, else the first other alias
$vuln->ghsaId();

$stored = VulnerabilityData::fromArray($row);   // toArray() shape, snake or camelCase keys
$search->refresh($stored)?->isMajor();

$v->isMalware();             // MAL- ids, Snyk "malware" issues, OSV malicious-package origins
$v->isActivelyExploited();   // CISA KEV, or SSVC exploitation "active" on the CVE record
$v->ssvc;                    // ['exploitation' => 'active', 'automatable' => 'no',
                             //  'technical_impact' => 'total', …] — CISA's SSVC decision
                             //  points from the CVE record's ADP container (MITRE source)
VersionRange::recommendedFix('4.17.20', $v->fixedVersions);  // "4.17.21" — the lowest fix
                             // at or above your version, same major line first; null when past all

Schedule::command('vulns:audit --no-dev --min-severity=high')->daily()->emailOutputOnFailure('[email protected]');

use Gumslone\Vulns\Data\PackageData;
use Gumslone\Vulns\Sources\NvdSource;

$nvd = app(NvdSource::class);                       // Laravel (config-wired)
// $nvd = new NvdSource(new CpeResolver, options: ['api_key' => env('NVD_API_KEY')]);

$vulns = $nvd->queryPackage(PackageData::fromCpe('cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*'));
$vulns = $nvd->queryBatch([$pkgA, $pkgB]);          // batch/pooled where supported
$one   = $nvd->fetchById('CVE-2021-44228');         // single advisory

PackageData::fromPurl('pkg:composer/laravel/[email protected]')->toCpe23();
// "cpe:2.3:a:laravel:framework:11.0:*:*:*:*:*:*:*"
PackageData::fromCpe('cpe:2.3:a:tukaani:xz:5.6.0:*:*:*:*:*:*:*')->toPurl();
// "pkg:generic/[email protected]"
PackageData::fromCommit('https://github.com/owner/repo/commit/bf04e5f2')->purl;
// "pkg:github/owner/repo@bf04e5f2" — OSV also matches the commit against git ranges
PackageData::fromCommit('https://github.com/owner/repo/commit/bf04e5f2')->toCpe23();
// "cpe:2.3:a:owner:repo:bf04e5f2:*:*:*:*:*:*:*" (a bare sha converts to null —
// a hash has no vendor/product identity)
PackageData::commitFromUrl('https://gitlab.com/g/p/-/commit/bf04e5f2');
// "bf04e5f2" — the id out of any forge commit link
PackageData::fromCommit('…/commit/bf04e5f2')->toCommitUrl();
// back to the forge page (GitLab keeps its /-/ route)

$vulns = app(VulnSearch::class)->searchPurl('pkg:npm/[email protected]');
// → OSV + GitHub + NVD (+ Snyk, once its two values are set) …

// config/vulns.php
'nvd' => ['enabled' => true, 'api_key' => env('NVD_API_KEY')],
'snyk' => ['enabled' => true, 'api_token' => env('SNYK_API_TOKEN'), 'org_id' => env('SNYK_ORG_ID')],

new NvdSource(new CpeResolver, options: [
    'api_key' => getenv('NVD_API_KEY') ?: null,
    'timeout' => 30,
]);

use Gumslone\Vulns\Data\PackageData;
use Gumslone\Vulns\Sources\OsvSource;

$vulns = app(OsvSource::class)->queryPackage(
    new PackageData(name: 'lodash', version: '4.17.20', ecosystem: 'npm')
);

// …or every enabled source at once
foreach (app('vulns.enabled_sources') as $source) {
    $found = $source->queryPackage($package);
}

use Gumslone\Vulns\Facades\Vulns;

$vulns  = Vulns::searchPurl('pkg:npm/[email protected]');
$report = Vulns::only(['osv', 'github'])->report($packages);

Event::listen(SourceFailed::class, fn (SourceFailed $e) => Log::warning("vulns: {$e->source} {$e->message}"));

$this->app->bind(InternalAdvisories::class, fn () => new InternalAdvisories(config('services.advisories')));
$this->app->tag([InternalAdvisories::class], 'vulns.sources');

$source = new OsvSource(
    options: ['timeout' => 15],
    cache: new Gumslone\Vulns\Support\ArrayCache,
);
$vulns = $source->queryPackage(new PackageData(name: 'left-pad', version: '1.0', ecosystem: 'npm'));

$v = $search->searchPurl('pkg:npm/[email protected]')[0];

$v->vulnId            // "CVE-2019-10744"
$v->canonicalId()     // "CVE-2019-10744"  — the CVE even when a source keyed it on a GHSA
$v->source            // "nvd"             — which source won the merge
$v->severity          // Severity::Critical  (->value === "critical")
$v->cvssV3Score       // 9.1               — a score always has its vector and a
$v->cvssV3Vector      // "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H"   vector its score (see below)
$v->cvssV2Score       // 6.4               (also cvssV2Vector)
$v->cvssV4Score       // 8.7|null          — v4 computed from the vector when
$v->cvssV4Vector      //                     the source publishes only CVSS:4.0
$v->effectiveCvssScore() // 8.7            — newest standard first: v4 → v3 → v2
$v->cvss()            // CvssVector — the newest vector as an object (scores, metrics, adjustment)
$v->inferredFields    // ["cvss_v2_vector"] — what this record filled in itself
$v->epssScore         // 0.9432            — probability of exploitation within
$v->epssPercentile    // 0.999               30 days (FIRST.org EPSS)
$v->isKnownExploited  // true              — listed in CISA KEV
$v->kevSince          // DateTimeInterface — when CISA listed it
$v->kevDueDate        // DateTimeInterface — US federal remediation deadline
$v->usedInRansomware  // true              — confirmed ransomware campaign use
$v->isWithdrawn       // false             — retracted/rejected advisories don't look live
$v->isDisputed        // false             — contested by the vendor
$v->exploitMaturity() // ExploitMaturity::Poc — none | poc | weaponized, derived
$v->exploitReferences() // ["https://www.exploit-db.com/…"] — the exploit links
$v->attackVector()    // "network" | "adjacent" | "local" | "physical" | null
$v->isNetworkExploitable()      // true  — for policy gates
$v->

use Gumslone\Vulns\Support\CvssVector;

$advisory = CvssVector::parse('CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H');
$advisory->baseScore();                                    // 9.8

$now = $advisory->withTemporal(['E' => 'P', 'RL' => 'O', 'RC' => 'C']);
$now->temporalScore();                                     // 8.8  — PoC only, fix available

$here = $now->withEnvironmental(['MAV' => 'L', 'MPR' => 'H', 'CR' => 'L']);
$here->environmentalScore();                               // 5.8  — local, admin-only, low confidentiality need
$here->baseScore();                                        // 9.8  — still the advisory's number
$here->score();                                            // 5.8  — "the score this vector expresses"
(string) $here;
// "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:P/RL:O/RC:C/CR:L/MAV:L/MPR:H"

$a = 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:P/RL:O/MAV:L/CR:L';  // A: the modifiers you want
$b = 'CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:U/RL:W/MAV:A/CR:H';  // B: the base you want; its own modifiers go

$c = CvssVector::parse($b)->withModifiersOf($a);

(string) $c;              // "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:P/RL:O/CR:L/MAV:L"
                          //  └── B's base ─────────────────────────────┘ └── A's modifiers ──┘
$c->baseScore();          // 9.6 — B's base, untouched
$c->temporalScore();      // 8.6 — B's base × A's E:P/RL:O
$c->environmentalScore(); // 7.6 — B's base in A's environment (was 7.9 with B's own MAV:A/CR:H)

CvssVector::parse($b)->withTemporalOf($a);       // …/E:P/RL:O/CR:H/MAV:A  — A's temporal, B's own environmental kept
CvssVector::parse($b)->withEnvironmentalOf($a);  // …/E:U/RL:W/CR:L/MAV:L  — B's own temporal kept, A's environmental

$a = 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:P/MAV:L';
$b = 'CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:U/RL:W/MAV:A/CR:H';
$B = CvssVector::parse($b);

$B->withModifiersOf($a);  // …/E:P/MAV:L               only A's modifiers — B's RL:W and CR:H dropped
$B->merge($a);            // …/E:P/RL:W/CR:H/MAV:L     overlay: A wins where both set one, B keeps the rest
$B->fill($a);             // …/E:U/RL:W/CR:H/MAV:A     gaps only: B keeps everything, A adds only what B lacked (nothing here)

$v4 = CvssVector::parse('CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N');
$v4->baseScore();                                   // 9.3
$v4->withTemporal(['E' => 'P'])->score();           // 8.9
$v4->with(['E' => 'U', 'MAV' => 'L'])->score();     // 6.1

$adjusted = $vuln->withCvssModifiers(['E' => 'P', 'MAV' => 'L']);   // metrics onto the record's vector
$adjusted->cvssV3Score;             // 9.8 — the advisory's base score, untouched
$adjusted->adjustedCvssScore();     // what the vector now expresses (environmental here)
$adjusted->cvss();                  // the CvssVector, for the individual scores

$vuln->withCvssModifiersOf($myEnvironmentVector);   // the advisory's base + ONLY your modifiers

$vuln->affects('4.17.20', 'lodash');   // true  — inside an affected range
$vuln->affects('4.17.21', 'lodash');   // false — PROVABLY outside every range about lodash
$vuln->affects('1.1.1k', 'openssl');   // null  — can't tell: treat as possibly affected
$vuln->recommendedFix('4.17.20');      // '4.17.21' — lowest fix at/above, same major line first

use Gumslone\Vulns\Export\{OsvExporter, CycloneDxExporter, OpenVexExporter, SarifExporter};

OsvExporter::export($vuln);                                   // OSV-schema document
CycloneDxExporter::export($vuln, ['pkg:npm/[email protected]']); // CycloneDX 1.6 vulnerabilities[] entry (+ optional VEX analysis)
SarifExporter::export($packages, $report->results);           // SARIF 2.1.0 log for code-scanning UIs

$statement = OpenVexExporter::statement($vuln, ['pkg:npm/[email protected]'], 'not_affected', 'vulnerable_code_not_in_execute_path');
OpenVexExporter::document([$statement], author: '[email protected]');   // OpenVEX 0.2.0

use Gumslone\Vulns\Testing\FakeSource;

$search = new VulnSearch([
    new FakeSource('osv', ['lodash' => [$vuln]]),
    (new FakeSource('nvd'))->failing('503 Service Unavailable'),
]);
bash
php artisan vulns:search CVE-2021-44228
php artisan vulns:search pkg:npm/[email protected] --source=osv --source=nvd
php artisan vulns:search 'cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*' --json --latest
bash
php artisan vulns:audit                                   # both lockfiles in the project root
php artisan vulns:audit --no-dev --min-severity=high      # production dependencies, high and up
php artisan vulns:audit --format=sarif > vulns.sarif      # GitHub code scanning / GitLab / Azure DevOps
php artisan vulns:audit --format=json --lock=frontend/package-lock.json
bash
php artisan vendor:publish --tag=vulns-config
bash
php artisan vendor:publish --tag=vulns-config