Posted by:

David Greenwood

David Greenwood, Chief of Signal

If you are reading this blog post via a 3rd party source it is very likely that many parts of it will not render correctly. Please view the post on for the full interactive viewing experience.

In this post I will show you how we designed the cve2stix web app.

So far I’ve described the cve2stix tool. You can use the cve2stix tool without the web app described in this post. However, based on feedback from the community, we quickly realised a hosted version of cve2stix would prove useful to many.

Essentially what I am building with cve2stix is an API product, so the web app will by definition fairly simple. It’s main function will be allowing people register to use the API.

The API in the cve2stix tool is built on-top of the Django REST framework.

cve2stix can be thought of a few key parts:

  • the cve2stix library: which includes all the logic to download and parse CVE’s from NVD
  • Django REST framework: what the cve2stix API is built on-top of
  • Django admin: a lightweight web framework that handles user management and authentication (authentication and sign up is actually handled by Auth0)

Django is also my go-to framework for building new web apps. It means I can get the app out of the door quickly using many parts that already exist in the Django ecosystem (like the REST framework).

When looking at building out the web app I knew I needed admin and user facing pages and first set out defining a scope of what I wanted to build.

  • Public pages (unauthenticated)
    • Authentication
      • will exclusively use SSO (Google, Microsoft, Github)
    • API docs (no authentication required) * ReDoc and Swagger pages
    • A page to search for CVEs
      • allow users to search for a CVE based on vendor+product.
    • A page to view individual CVEs
      • similar to NVD CVE page, but also shows STIX bundle with download
  • Public pages (authenticated)
    • A page for an authenticated user to
      • view/regenerate API keys (aka the “user area”)
      • see usage of API (quota)
      • see subscription
    • A page for users to manage their subscription
      • including ability to cancel/upgrade plan using Stripe as the payment processor using Stripe checkout
  • Admin pages (authenticated)
    • View users by email (and search for users)
    • Manage plans

The most complex element here is payment processing. That said, I’ve used a brilliant Django boilerplate, SaaS Pegasus, in the past that makes this trivial. I’ll be using it again here.

As such, almost all parts of this app could be taken off-the-shelf:

Public pages (unauthenticated)

I didn’t want to spend to much time on a sleek UI. I decided to use the generic ones that ship with Swagger/Redoc/SaaS Pegasus/Auth0/Stripe/etc.



Auth0 login

I didn’t really want to deal with credential management. Auth0 login provides register/login/password reset flows out of the box, whilst allowing for SSO register/login.

For now have decided to only implement the following signup options; Google SSO, LinkedIn SSO, Microsoft SSO and Github SSO.

One of the reasons with going with Auth0 is that it also allows us to track a single account for a user across Signal Corps product at the Auth0 layer.

When registering (before clicking create account using SSO provider) a user must agree to the terms. A user can optionally choose to join the mailing list for cve2stix before creating an account using SSO (assuming admins have enabled a mailing list).

API docs



cve2stix Swagger API



cve2stix Redoc API

The neat thing about both these tools is that the OpenAPI spec can be downloaded and imported to other tools a user might have (e.g. Postman).

CVE search page


cve2stix CVE search

The filters and content on this page are powered by a couple of the API endpoints.

A user can select one vendor. Once a vendor is selected, the products for that vendor will appear that can also be selected (multis-elect).

To do this the GET <HOST>/api/<VERSION>/cpe/vendors?keywordSearch= endpoint is used. As the user types a character, the keywordSearch is updated and request executed to provide an autocomplete response to the user (only first 50 results will ever be shown, as this is max items returned in the response). Once a single vendor is selected, the products shown in the response for that vendor will be shown in the product dropdown.

For CVSSv3 Severity and Impact filters, the GET <HOST>/api/<VERSION>/cve endpoint is used with the cvss3ExploitabilityScoreMin and/or cvss3ImpactScoreMin parameters included. e.g. GET <HOST>/api/<VERSION>/cve?cvss3ExploitabilityScoreMin=2.3&cvss3ImpactScoreMin=3.4

If a user has also selected a vendor and optionally one or more products, then this will included in the filter using the cpeMatchString (one or more times). e.g. for all Apple products to be includes in the filter; GET <HOST>/api/<VERSION>/cve?cvss3ExploitabilityScoreMin=2.3&cvss3ImpactScoreMin=3.4&cpeMatchString=cpe:2.3:a:apple&cpeMatchString=cpe:2.3:h:apple&cpeMatchString=cpe:2.3:o:apple

These requests will return matching CVEs in the response with all the detail needed to populate the table, which also only ever shows 50 results (but paginated) to match the API response.

Clicking on each CVE ID takes a user to the CVE Detail page…

CVE Detail page


cve2stix CVE detail

The content on this page is powered by a single endpoint GET <HOST>/api/<VERSION>/cve/<CVE_ID>.

The description is taken from the Vulnerability SDOs description property.

The CVSSv3 content is taken from inside the extension property of the Vulnerability SDO. Here’s a sample response for reference.

     "extensions": {
        "extension-definition--b2b5f2cd-49e6-4091-a0e0-c0bb71543e23": {
          "nvd_cve": {
            "id": "CVE-2020-11263",
            "metrics": {
              "cvssMetricV31": [
                  "type": "Primary",
                  "source": "[email protected]",
                  "cvssData": {
                    "scope": "CHANGED",
                    "version": "3.1",
                    "baseScore": 8.2,
                    "attackVector": "LOCAL",
                    "baseSeverity": "HIGH",
                    "vectorString": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
                    "integrityImpact": "HIGH",
                    "userInteraction": "NONE",
                    "attackComplexity": "LOW",
                    "availabilityImpact": "HIGH",
                    "privilegesRequired": "HIGH",
                    "confidentialityImpact": "HIGH"
                  "impactScore": 6,
                  "exploitabilityScore": 1.5

The graph on the page is powered by STIX View. This loads STIX bundles to display the data. As the API response from this endpoint contains a valid JSON bundle in the response, this can be loaded in the viewer.

If a user is on a plan that supports it (see plans), the user can also download the JSON STIX bundle from the response too.

Public pages (authenticated)

Account management page


cve2stix Account Management

The account details are all obtained using the GET <HOST>/api/<VERSION>/users?id= endpoint, where id is the authenticated user.

When a user is created an API key is automatically generated. This is mainly to do with the service account (cve2stix account that handles app requests to the API) that is generated on app creation.

API keys are not shown to user after generation. That means user must regenerate their API key to see it printed on regeneration before they can start using it. A user can regenerate their key at any time, but they must enter their account email address to confirm this action.

User can also manageme their subscription to the mailing list on this page.

A user can also permanently delete their account which will delete any account specific data, including any aggregated data (e.g. daily historical requests to the API) or mailing list subscriptions.

Each user account is assigned to a plan. By default this is a free plan (no monthly cost), but a user can upgrade manually (or done via admin) to another plan at any time.

Manage subscription page


cve2stix Plan Management

Plans have 4 variables when created by admins:

  • default: boolean. Only one plan can ever be default at a time. The default plan is auto assigned to new users.
  • a name
  • a description
  • cost (in USD per month): billing cycle is based on date user signed up
  • maximum number of daily requests (integer)
  • STIX UI download ability (boolean)
  • state, either
    • public: visible in UI and can be manually chosen by user
    • archived: not visible in the UI for selection by users, but historic plan that older user signed up to. Admins can archive plans when they need to update plan price/options.
    • custom: not visible in the UI and assigned manually by admins to a user account. Typically these are generated for users that need a very large volume of API request and need a unique billing cost agreed separately between admin and user.

e.g. the following plans could exist…

  • Name: Free
    • Default: true
    • $0.00 /mo
    • 50 request /day
    • STIX UI download: false
    • type: public
  • Name: Web
    • Default: false
    • $15.00 /mo
    • 500 requests /day
    • STIX UI download: true
    • type: public
  • Name: Web+API
    • Default: false
    • $25 /mo
    • 10,000 requests /day
    • STIX UI download: true
    • type: public
  • Name: Legacy plan 1
    • Default: false
    • $15.00 /mo
    • 5,000 requests /day
    • STIX UI download: true
    • type: archive
  • Name: Custom plan 1
    • Default: false
    • $100.00 /mo
    • 500,000 requests /day
    • STIX UI download: true
    • type: custom

At least one public/default plan must exist. On app creation, cve2stix will create a default free public plan.

In the UI, a user can change their plan at any time. Depending on the action the change will be handled in different ways;

  • if user upgrades (to higher price plan): they will be upgraded immediately but will be charged pro-rata for the additional cost of new plan for the billing cycle. As an example, a user upgrades 50% through the current billing cycle. The old plan was $10 / mo. The new plan they are upgrading to is $20 month. The difference is $10 / month. As the user is 50% through the current billing cycle, the additional payment is 50% * $10 = $5 to upgrade. On the next billing cycle they will be charged $20/mo.
  • if a user downgrades (to lower price plan): they will remain on their current plan until the end of the current billing period. At the end of the billing period their account will automatically be downgraded to the lower plan.

This functionality is built into SaaSPegasus/Stripe.

Admin pages (authenticated)


All views on this page use the default Django Admin UI elements.

On app creation two accounts are created:

  1. service account: this cannot be logged into. This account has full permissions to the API and the API key used to make the requests from the UI to the API.
  2. staff account: this can be logged into. This is the default staff account for app creation and should be used to assign other staff members. That is, other staff should create their own accounts, and this default staff account can be used to assign them staff privileges, after which the default account should be deleted (at least one staff account needs to always exist).

The admin pages accessible to staff allow them to manage plans and users.

  • View users by email (and search for users), for each user; * ability to see daily API usage * ability to see web app * ability to delete account (and all data)
  • View users by email (and search for users), for each user; * email (and type SSO or not) * plan name, billing cycle dates, and payment history (and ability to modify plan) * sign up date * daily API usage * web app last login date * ability to delete account (and all data)
    • Manage plans
      • ability to create / edit plans

A note on setup

When setting up cve2stix a few credential variables (set using an .env file) are optional:

cve2stix coming soon…

Hopefully the past six posts in this series have given you a deeper insight into cve2stix and how you can use it.

The code for cve2stix will soon be available to download on GitHub here (Apache 2.0).

I hope you’ll find cve2stix useful, and I am always very happy to receive feedback either via GitHub issues, or directly via our Slack community.

Discuss this post

Signals Corps Slack

Never miss an update

Sign up to receive new articles in your inbox as they published.