Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cvss Bundle Laravel Package

ywh/cvss-bundle

A Symfony bundle for working with CVSS (Common Vulnerability Scoring System) vectors and scores, providing services, Twig extensions, and helpers to parse, validate, and calculate vulnerability severity ratings in your applications.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require ywh/cvss-bundle

Register the bundle the Symfony2 way (app/AppKernel.php):

public function registerBundles()
{
    $bundles = array(
        // ...
        new YWH\CvssBundle\YwhCvssBundle(),
    );
}

In modern Symfony (3.4+/4+), use config/bundles.php instead and check whether the bundle's composer.json PHP constraint actually allows your runtime — a Symfony2-era bundle very often pins php: ~5.3|~5.4 and symfony/symfony: ~2.3.

Where to look first, in this order:

  1. README.md — the canonical vector string format and the service ID.
  2. Resources/config/services.xml (or services.yml) — this is the source of truth for service IDs and constructor arguments, not the docs.
  3. DependencyInjection/Configuration.php — the real config tree, its defaults, and the extension alias (ywh_cvss is the likely alias, but confirm it; an alias mismatch is the #1 "bundle does nothing" cause).
  4. The calculator class itself — check whether it returns a scalar score, an array, or a value object, and whether it throws or returns null on bad input.

Minimal configuration is usually just enabling the extension:

# app/config/config.yml
ywh_cvss: ~

First use case — score a vector you already have:

$calculator = $this->get('ywh_cvss.calculator'); // confirm ID in services.xml

$vector = 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H';
$score  = $calculator->calculate($vector);   // 9.8

echo $score; // feed this straight into a severity badge

The moment that works, wrap it in a small application-level service (see below) and stop calling the bundle directly from controllers. CVSS is a pure function of a string; you want one seam to swap implementations, add caching, and normalize error handling.

Implementation Patterns

Store both the vector and the derived values

Never store only the score. The vector is the audit trail; the score is a denormalized index for sorting and filtering. Compute on write, persist both:

class Vulnerability
{
    /** @ORM\Column(type="string", length=255) */
    private $cvssVector;

    /** @ORM\Column(type="decimal", precision=3, scale=1, nullable=true) */
    private $cvssScore;

    /** @ORM\Column(type="string", length=20, nullable=true) */
    private $cvssSeverity;
}

decimal rather than float prevents 9.800000000000001 leaking into JSON responses and into ORDER BY.

Wrap the bundle in your own calculator

namespace AppBundle\Security;

use YWH\CvssBundle\...; // whatever the concrete class is

class CvssScorer
{
    private $calculator;

    public function __construct($calculator)
    {
        $this->calculator = $calculator;
    }

    /**
     * @return array{score: float|null, severity: string|null, valid: bool}
     */
    public function score($vector)
    {
        if (!is_string($vector) || '' === trim($vector)) {
            return ['score' => null, 'severity' => null, 'valid' => false];
        }

        try {
            $score = $this->calculator->calculate(trim($vector));
        } catch (\Exception $e) {
            return ['score' => null, 'severity' => null, 'valid' => false];
        }

        return [
            'score'    => (float) $score,
            'severity' => $this->severityFor((float) $score),
            'valid'    => true,
        ];
    }

    private function severityFor($score)
    {
        if ($score >= 9.0) { return 'critical'; }
        if ($score >= 7.0) { return 'high'; }
        if ($score >= 4.0) { return 'medium'; }
        if ($score >  0.0) { return 'low'; }

        return 'none';
    }
}

Deriving severity yourself is deliberate: score→label thresholds are a product decision (CVSS v2 and v3 use different band edges), and you don't want a bundle upgrade silently re-banding your UI.

Use a Doctrine lifecycle callback

Don't ask every controller to remember to re-score. Put it on the entity:

/** @ORM\HasLifecycleCallbacks() */
class Vulnerability
{
    /** @ORM\PrePersist @ORM\PreUpdate */
    public function refreshCvssScore()
    {
        $result = $this->scorer->score($this->cvssVector);
        $this->cvssScore    = $result['score'];
        $this->cvssSeverity = $result['severity'];
    }
}

Injecting a service into an entity is awkward; the more testable variant is a Doctrine prePersist/preUpdate event subscriber that owns the scoring. Prefer the subscriber.

Validate the vector at the form boundary

The bundle's parser is a calculator, not a validator. Build a constraint so bad vectors never reach persistence and the user gets a field error instead of a 500:

class ValidCvssVector extends Constraint
{
    public function validatedBy()
    {
        return 'app.validator.cvss_vector';
    }
}
class CvssVectorValidator extends ConstraintValidator
{
    private $scorer;

    public function validate($value, Constraint $constraint)
    {
        if (null === $value || '' === $value) {
            return;
        }

        if (!$this->scorer->score($value)['valid']) {
            $this->context->buildViolation('Not a valid CVSS vector.')
                ->addViolation();
        }
    }
}

This is the highest-value integration point: it turns the bundle from "a library I call somewhere" into a first-class part of your input contract.

Display helpers

A Twig macro is cheaper than a Twig extension and keeps the bundle out of your templating layer:

{% macro badge(score, severity) %}
    {% if score is not null %}
        <span class="cvss cvss-{{ severity }}"
              title="CVSS base score">{{ score|number_format(1) }} {{ severity|upper }}</span>
    {% else %}
        <span class="cvss cvss-none">N/A</span>
    {% endif %}
{% endmacro %}

If you'd rather centralize it, a Twig extension that delegates to your CvssScorer is fine — just make sure it does not re-parse the vector on every render. Pass the stored score.

Bulk operations and the API layer

When importing findings or rendering a report, score in a single pass and cache by vector string:

$cache = [];
foreach ($findings as $finding) {
    $vector = $finding->getCvssVector();
    if (!isset($cache[$vector])) {
        $cache[$vector] = $this->scorer->score($vector);
    }
    $finding->applyCvss($cache[$vector]);
}

Vectors are highly repetitive across a dataset; this is often a 10–50× saving on import jobs.

Gotchas and Tips

This is a Symfony2 bundle and Symfony2 is long EOL. Expect to fork. Realistically you will: bump the PHP constraint, replace getContainerExtension() conventions if the bundle hasn't already, and re-test. Budget for it up front rather than discovering it at install. If it supports only CVSS v2 or only v3.0, note that CVSS v3.1 (the current base standard) may not be accepted by the parser — a vector prefixed CVSS:3.1/ could be rejected outright.

Vector prefix handling is the most common parse failure. v3 vectors must start with CVSS:3.0/ or CVSS:3.1/; v2 vectors have no prefix at all (AV:N/AC:L/Au:N/C:P/I:P/A:P). If you accept user input, normalize whitespace and case before passing it in, and decide explicitly whether you support both versions. Mixed-version data in one column is a schema smell — store the version.

Rounding will not match the NVD calculator if you do it yourself. CVSS v3.1 defines Roundup: round up to one decimal, so 4.02 → 4.1, and 4.0 stays 4.0. round() and ceil() both get this wrong on some inputs. If the bundle exposes the raw computed score, trust it; if you post-process or re-implement any part of the formula, test against the official spec examples. Base scores are also not simple averages across Impact and Exploitability when Scope is Changed — that formula is separate.

"CVSS score" usually means base score only. Temporal and environmental metrics are frequently unimplemented in small bundles. If your product surfaces "adjusted" scores, verify whether the calculator consumes E:, RL:, CR: etc. at all, or silently ignores them.

Treat the vector as untrusted input. Users will paste vectors with trailing whitespace, newlines from a PDF, or from a scanner that uses a different metric order. Wrap every call in a try/catch at the boundary and never let a bad vector break a listing page. Log the failing string (truncated) so you can spot scanner-specific quirks.

Cache the calculator service, not the result set. The service is cheap but stateless calls in a loop are still function-call overhead plus repeated parsing. Cache at the vector-string level as shown above; a Symfony cache pool keyed on md5($vector) also works if the dataset spans requests.

Configuration quirks. If the extension alias in DependencyInjection/*Extension.php doesn't match what you wrote in your YAML, Symfony throws There is no extension able to load the configuration for "..." — the message points at your config, not at the bundle, and people waste an hour on it. Also check whether the bundle ships a Configuration class at all; bundles without one require the alias to be configured with ~ and nothing else.

Extension points to prefer. Rather than editing the bundle, decorate its calculator service or bind your own implementation to the same interface. If no interface exists, create one in your app and register the bundle's class as the implementation. That gives you a test double and a clean upgrade path — critical for a low-star, zero-dependent package where you are effectively the maintainer.

Verify before you build on it. There are no dependents and no stars; treat correctness as unvalidated. Run two or three known vectors (e.g. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H → 9.8, CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:U/C:L/I:N/A:N → 1.8) through it as a smoke test in CI. If they don't match the NVD calculator, stop and evaluate a maintained alternative — writing the base-score formula yourself is roughly a day's work and removes the dependency entirely.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route