Open-source project
bugcrowd/vulnerability-rating-taxonomy avatar
bugcrowd/vulnerability-rating-taxonomy

The Bugcrowd VRT: a shared severity scale for vulnerability classes

Bugcrowd’s baseline priority ratings for common security vulnerabilities

569 stars130 forksPythonApache-2.0

At a glance

What is it?
The Bugcrowd Vulnerability Rating Taxonomy is an open, versioned JSON document that assigns a baseline priority from P1 to P5 to common vulnerability classes, maintained by a council that meets weekly and validated in CI against a JSON schema. It is the rating vocabulary many bounty programmes use, and its value depends as much on the stability of its identifiers as on the priorities themselves.
Who is it for?
The VRT fits researchers, triage engineers and bounty programmes that need a neutral, versioned severity vocabulary instead of one invented per engagement, and it fits automation teams because the taxonomy is machine readable and schema validated.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 60 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A baseline scale for common vulnerability classes, not a severity engine

The Bugcrowd Vulnerability Rating Taxonomy began as an internal priority scale and was released publicly at the beginning of 2016 as a baseline vulnerability priority scale for bug hunters and organisations. It was open sourced in April 2017, together with formal contributor guidelines, so that outside researchers could improve it and so that the discussion about how a vulnerability should be rated would be public rather than private.

What the taxonomy actually contains is a rating for vulnerability classes. Each class has a suggested baseline technical severity, and the classes are nested so that a specific subcase can carry a different priority from the general category it belongs to. The current release is published at https://bugcrowd.com/vrt as a searchable page and a downloadable PDF, and it is also available through the Bugcrowd API, with usage documentation in the API reference.

The ratings were not invented in a vacuum. The documentation says Bugcrowd's application security engineers started with the generally accepted industry guideline and then considered the vulnerability's average acceptance rate, its average priority, and its frequency on business use case specific exclusion lists across all of Bugcrowd's programmes. That is a data informed starting point, not a standard, and the same documentation is careful to say the recommended priority from P1 to P5 might apply without context while application complexity, bounty brief restrictions or unusual impact could result in a different rating.

Category, Sub-Category and Variant as three levels of nesting

A VRT entry is classified at up to three levels, and each level has definitions exclusive to it. The top level is the Category, which describes an entire class of vulnerabilities, for example Server-Side Injection. Nested under a Category are Sub-Categories, which describe individual vulnerabilities, for example Server-Side Injection > Remote Code Execution (RCE). Nested under a Sub-Category are Variants, which describe specific subcases, for example Server-Side Injection > SQL Injection > Blind.

Each entry carries a set of data fields. The Name is the human readable vulnerability name. The Priority is the suggested baseline technical severity on a scale where P1 is Critical, P2 is High, P3 is Medium, P4 is Low and P5 is Informational. Children holds the nested entries, and only Categories and Sub-Categories can have children. The ID is often the lowercase version of the name with word separators replaced by _, and it is unique among the children of its own parent, which is what allows an ID to map across versions: an ID only changes when the entry should not be identified with the previous version of that entry.

That last property is the one that matters most in practice. The identifier, not the priority, is the thing you persist, because priorities move when the council changes its mind and identifiers are supposed to be stable.

A complete entry looks like this:

json
{
  "id": "server_security_misconfiguration",
  "name": "Server Security Misconfiguration",
  "type": "category",
  "children": [
    {
      "id": "directory_listing_enabled",
      "name": "Directory Listing Enabled",
      "type": "subcategory",
      "children": [
        {
          "id": "non_sensitive_data_exposure",
          "name": "Non-Sensitive Data Exposure",
          "type": "variant",
          "priority": 5
        }
      ]
    }
  ]
}

Note where the priority lives. A Variant can carry a priority, and a Category or Sub-Category can leave it unset, which is how the taxonomy expresses a class whose rating depends on the case.

Null priority means the rating depends on context you supply

Some entries have a null priority value, and the documentation is explicit that this represents a priority which varies based on context information. The severity of some vulnerabilities, denoted in the taxonomy as Varies, is context dependent, and the example given is an Insecure Direct Object Reference issue, whose technical severity depends heavily on the capabilities of the vulnerable function and on other context. Valid IDOR vulnerabilities can range from P4 to P1.

For a consumer of the taxonomy this is the single most important structural detail. If you build a tool that maps a finding to a severity, you have to handle two different kinds of entry: the ones with a fixed number and the ones where the taxonomy declines to give one. Code that treats a null priority as zero, or that falls back to the parent's priority, will produce ratings the taxonomy never claimed. The right behaviour is to make the context dependency explicit in your own workflow, which is a design decision you have to make rather than one the JSON hands you.

The P1 to P5 scale itself is coarse by design. Five buckets with named meanings give a triage queue a workable order without pretending to more precision than a priority rating deserves, and the mapping from P1 Critical down to P5 Informational is fixed across the taxonomy.

Collapsed identifiers and the deprecated node mapping file

Versioned taxonomies inevitably break. Entries get deleted, identifiers get collapsed when two near-duplicate subcases turn out to be the same thing, and a node moves under a different parent. The repository keeps a reference file for exactly this situation, deprecated-node-mapping.json, whose stated purpose is to serve as a reference for finding the latest mapped ids so that deprecated nodes are not lost.

The format is a flat map from the old identifier to a version and the new identifier. Collapsing two nodes into one looks like this:

json
{
  "unvalidated_redirects_and_forwards.open_redirect.get_based_all_users": {
    "2.1": "unvalidated_redirects_and_forwards.open_redirect.get_based"
  },
  "unvalidated_redirects_and_forwards.open_redirect.get_based_authenticated": {
    "2.1": "unvalidated_redirects_and_forwards.open_redirect.get_based"
  }
}

Both former identifiers point at the same target, which tells a consumer that the two subcases have been unified and that stored findings under either name now resolve to one entry. The version key is what lets you ask a specific question, namely where a given identifier lived in a given version of the taxonomy.

If you store VRT identifiers in a ticket system, a report database or a severity pipeline, this file is the artefact that keeps those stores readable. It is also the artefact most likely to be overlooked, because nothing forces you to read it until the day a lookup returns nothing.

Mappings to other classification systems live in their own directory

The repository supports converting VRT identifiers to other vulnerability classification systems, the example given being CVSS. Such mappings are supported by adding a mapping folder or files to the mappings directory, and the top level listing also contains a third-party-mappings directory for material that is not maintained here.

The mapping files mirror the structure of the main taxonomy file, with a difference: they include only the id and children attributes, plus one additional attribute named after the file itself. The traffic light example in the documentation illustrates the shape, where a hypothetical system maps every vulnerability to red, green or yellow, with a metadata block carrying a default value and a content list mirroring the tree.

This design has a practical consequence. A mapping is not a translation table but a parallel tree, so a consumer has to walk the taxonomy and the mapping together, and any entry in the taxonomy that a mapping does not cover has no answer in the target system. The default value in the metadata block is what a consumer falls back to, and choosing it is a policy decision that the file author makes rather than something the schema decides.

The traffic light example is also a good illustration of why a mapping layer is worth building. VRT priorities are ordinal, so red, green and yellow is a lossy conversion, and a mapping file makes that loss explicit and reviewable instead of hiding it inside a scoring function.

Validating the taxonomy locally with the shipped Docker image

The taxonomy is a data file with a schema, and the repository treats validation as part of the workflow. The top level contains vrt.schema.json, the schema itself, a sort_all_jsons.py script, a lib/ directory holding the validation code, and a Dockerfile that packages the check. The README also carries a badge for a workflow named validate_vrt.yml, so validation runs on pull requests in addition to being available locally.

The image definition is short enough to read in full:

dockerfile
FROM python:3.11

WORKDIR /tmp/vrt
ADD . /tmp/vrt

RUN pip install -r lib/requirements.txt

CMD [ "python3", "-B" , "./lib/validate_vrt.py" ]

The command it runs is the validator itself, so building the image and running it against a modified taxonomy file is the local check. The Python version is pinned at 3.11, the requirements come from lib/requirements.txt, and the -B flag stops Python from writing bytecode files into the copied source tree, which keeps the container run from leaving artefacts behind.

For a contributor, that is the loop: change the JSON, run the validator, and let the same validator run again in CI. For a consumer who wants to fork the taxonomy, the same image gives you a way to prove that your fork is still structurally valid before you publish it.

A council that meets weekly and publishes its minutes

The maintenance model is unusually transparent for a document that influences payouts. Each week several members of the Bugcrowd team meet to discuss vulnerability edge cases, to improve vulnerability classification and to work through external feedback. When the team reaches consensus on a proposed change, that change is committed to the repository, and the minutes of the council meetings are published in the project wiki for extra transparency.

External contribution runs through two channels. Public discussion happens in GitHub issues, and comments can also be sent by email to [email protected]. The detailed rules live in a CONTRIBUTING document under the .github directory, and a code of conduct file sits at the top level.

The cadence matters when you plan an upgrade. Releases are versioned and dated, and the recent ones are v1.19.1 from 2026-07-08, v1.19 from 2026-06-24 and v1.18 from 2026-03-09, with a CHANGELOG.md at the top level. Since council decisions land weekly but a release is not made for each of them, the taxonomy you consume can sit at a version that is behind the council's latest thinking, and the gap between the two is where a surprising priority appears.

Apache-2.0 terms and what it costs to stay on a versioned scale

The repository is licensed under Apache-2.0, with the terms in LICENSE.md. That is a permissive licence with an explicit patent grant, which is why a taxonomy like this can be copied into an internal classification system, embedded in a triage tool, or used to seed a mapping into another scheme without a licensing conversation.

The upgrade cost is the interesting part, and it is a data maintenance problem rather than a dependency problem. Because a priority can change when the council revisits an edge case, a pipeline that keys off priorities has to be re-evaluated on each version bump, and because identifiers are meant to be stable but sometimes are not, a pipeline that keys off identifiers has to read the deprecated node mapping. A third cost is the null priority case, which forces every consumer to have a policy for entries with no fixed rating.

The repository is not archived and the last push was on 2026-08-26, so the taxonomy is still being revised. The practical posture for an adopter is to pin a version, read the changelog before moving, and treat the identifier map as a migration input rather than an afterthought.

Editorial conclusion

The VRT fits researchers, triage engineers and bounty programmes that need a neutral, versioned severity vocabulary instead of one invented per engagement, and it fits automation teams because the taxonomy is machine readable and schema validated. It is the wrong tool for a single finding, where the programme brief and context decide the priority, and it is the wrong foundation for a severity model you must defend legally, since the ratings encode one organisation's view of the world. Before you adopt it, read the entry that covers your vulnerability class and check whether its priority is null or marked as varying, then review deprecated-node-mapping.json to see how the identifiers you already store have been renamed or collapsed across versions.

Frequently asked questions

What is the Bugcrowd VRT and what does it rate?

The Vulnerability Rating Taxonomy is a baseline technical severity scale for common vulnerability classes, released publicly at the beginning of 2016 and open sourced in April 2017. It assigns a suggested priority from P1 Critical down to P5 Informational, and the current release is published at https://bugcrowd.com/vrt as a searchable page, a PDF and through the Bugcrowd API.

What do the three levels of a VRT entry mean?

A Category describes a whole class of vulnerabilities, a Sub-Category describes an individual vulnerability nested under it, and a Variant describes a specific subcase nested under the Sub-Category. Only Categories and Sub-Categories can have children, and a Variant is where a priority value usually appears.

What does a null priority in the VRT mean?

It means the priority varies based on context information, which the taxonomy also denotes as Varies. The documentation gives Insecure Direct Object Reference as the example, noting that valid instances of it can range from P4 to P1 depending on the capabilities of the vulnerable function.

How do I map VRT identifiers to another system such as CVSS?

You add a mapping folder or file to the mappings directory. Those files have a structure similar to the main taxonomy file, containing only the id and children attributes plus an additional attribute named after the file itself, and a third-party-mappings directory holds material not maintained in the repository.

How is the Bugcrowd VRT maintained and licensed?

Several Bugcrowd team members meet weekly as the VRT Council, reach consensus on changes, commit them to the repository and publish the meeting minutes in the project wiki. The repository is licensed under Apache-2.0, and the last push was on 2026-08-26.

Official sources

  1. bugcrowd/vulnerability-rating-taxonomy on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bugcrowd-vulnerability-rating-taxonomy.svg)](https://hysenlabs.com/projects/bugcrowd-vulnerability-rating-taxonomy)

Was this useful?