# cvelib

A library and a command line interface for the CVE Services API.

**Note**: version 1.7.0 and later of cvelib is compatible with CVE Services 2.5.0 and CVE JSON schema 5.1.1.

## Requirements

- Python version 3.9 or greater
- [pip](https://pypi.org/project/pip/)

## Installation

### Linux, MacOS, Windows


#### pip

```bash
python3 -m pip install --user cvelib
```

For more information on installing Python packages from PyPI, see the
[Python Packaging User Guide](https://packaging.python.org/tutorials/installing-packages/#installing-from-pypi).

If you are using Windows, `pip` may not add the path to use the `cve` command to your environment.
If it was not added, you will most likely see the error:

```
cve : The term 'cve' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
```

To resolve this error, add the file path for where your `cve.exe` file resides (for example,
`C:\Users\<username>\AppData\Roaming\Python\Python39\Scripts`) to your `PATH` variable. You can
edit your environment variables by searching *Edit the system environment variables* from the Start menu.

#### uv

To run the `cve` command using [uvx](https://docs.astral.sh/uv/guides/tools/), execute:

```
uvx --from cvelib cve --help
```

or to install it, execute:

```
uv tool install cvelib
```

### Podman/Docker

You can fetch a specific version of the `cvelib` library installed in a container image at
https://quay.io/repository/prodsecdev/cvelib. You can set up an alias to run the `cve` command using this container
image:

```bash
alias cve='podman run -it --rm quay.io/prodsecdev/cvelib'
# OR
alias cve='docker run -it --rm quay.io/prodsecdev/cvelib'
```

The `latest` container image tag will always point to the latest available version of the `cvelib` package in PyPI.

## CLI Setup and Configuration

Each CLI command executed requires the user to authenticate to the CVE Services API. You can provide
the authentication details with every command (using options `-u/--username`, `-o/--org`, and
`-a/--api-key`), or set them in the following environment variables:

### Linux & MacOS

```bash
$ export CVE_USER=margo
$ export CVE_ORG=acme
$ export CVE_API_KEY=<api_key>
```

### Windows Command Line

```
C:\> setx CVE_USER margo
C:\> setx CVE_ORG acme
C:\> setx CVE_API_KEY <api_key>
```

### Windows PowerShell

```
PS C:\> $Env:CVE_USER="margo"
PS C:\> $Env:CVE_ORG="acme"
PS C:\> $Env:CVE_API_KEY="api_key"
```

### Podman/Docker

To pass the configuration variables to the `cvelib` container, define them in an `.env` file:

```
CVE_USER=margo
CVE_ORG=acme
CVE_API_KEY=<api_key>
```

Then, specify that file in your Podman/Docker command, for example:

```bash
podman run -it --rm --env-file=.env quay.io/prodsecdev/cvelib ping
```

Alternatively, you can set the environment variables as shown in the sections above and pass them to the container
using:

```bash
podman run -it --rm -e CVE_ORG -e CVE_API_KEY -e CVE_USER quay.io/prodsecdev/cvelib ping
```

### Additional Configuration

Additional options that have an accompanying environment variable include:

* `-e/--environment` or `CVE_ENVIRONMENT`: allows you to configure the deployment environment
  (that is, the URL at which CVE Services is available) to interface with. Allowed values: `prod`,
  `test`, and `dev`. Separate credentials are required for each environment. The `test` and `dev`
  environments may not be consistently available during the development life cycle of CVE Services.

* `--api-url` or `CVE_API_URL`: allows you to override the URL for the CVE Services API that would
  otherwise be determined by the deployment environment you selected. This is useful for local
  testing to point to a CVE Services API instance running on localhost (for example,
  `export CVE_API_URL=http://localhost:3000/api/`).

* `-i/--interactive` or `CVE_INTERACTIVE`: every create/update action will require confirmation
  before a request is sent to CVE Services. Truthy values for the environment variable are:
  `1`, `t`, `yes`.

* `CVE_GENERATOR`: override the default value of `cvelib x.y.z` that is injected into the
  `x_generator` field of every published or updated CVE record. If you'd prefer to omit setting
  the field entirely, set the value to `-` (dash character; `export CVE_GENERATOR=-`). Existing
  `x_generator` values are not overwritten.

### Command Autocompletion

Autocompletion of subcommands is supported for the following shells:

#### Bash

Add the following line to your `~/.bashrc` file:

```bash
eval "$(_CVE_COMPLETE=bash_source cve)"
```

#### ZSH

Add the following line to your `~/.zshrc` file:

```bash
eval "$(_CVE_COMPLETE=zsh_source cve)"
```

#### Fish

Add the following line to a `~/.config/fish/completions/cve.fish` file:

```bash
eval (env _CVE_COMPLETE=fish_source cve)
```

## CLI Usage Examples

Available options and commands can be displayed by running `cve --help`. The following are
examples of some commonly used operations.

Reserve one CVE ID in the current year (you will be prompted to confirm your action):

```bash
cve --interactive reserve
```

Reserve three non-sequential CVE IDs for a specific year:

```bash
cve reserve 3 --year 2021 --random
```

Publish a CVE record for an already-reserved CVE ID:

```bash
cve publish CVE-2022-1234 --cve-json '{"affected": [], "descriptions": [], "providerMetadata": {}, "references": []}'
```

For information on the required properties in a given CVE JSON record, see the `cnaPublishedContainer` schema in:
https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/CVE_JSON_5.0_schema.json.

List all rejected CVEs for year 2018:

```bash
cve list --year 2018 --state reject
```

Assuming you have the `ADMIN` role (also called an _Org Admin_), create a new user in your
organization with:

```bash
cve user create -u foo@bar.com --name-first Foo --name-last Bar
```

Mark a user as inactive (again, assuming you have the `ADMIN` role):

```bash
cve user update -u foo@bar.com --mark-inactive
```

Reset your own API key:

```bash
cve user reset-key
```

List all users in your organization:

```bash
cve org users
```

See `-h/--help` of any command for a complete list of sub-commands and options.

## Library Usage Example

`cvelib` also exposes a Python interface to CVE Services that can be used within any Python application that includes
`cvelib` as its dependency. The following is an example Python function that fetches a CVE record for a given CVE ID:

```python
import os
from cvelib.cve_api import CveApi

def fetch_cve_record(cve_id: str) -> dict:
    cve_api = CveApi(
        username=os.getenv("CVE_USER"),
        org=os.getenv("CVE_ORG"),
        api_key=os.getenv("CVE_API_KEY"),
    )
    cve = cve_api.show_cve_record(cve_id)
    return cve
```

For more information, see the individual methods defined in the
[`CveApi` interface](https://github.com/RedHatProductSecurity/cvelib/blob/master/cvelib/cve_api.py).

## Other CVE Services Clients

- Client-side library written in JavaScript: https://github.com/xdrr/cve.js
- A web-based client interface and a client library in JavaScript: https://github.com/CERTCC/cveClient
- A web-based tool for creating and editing CVE records in the CVE JSON v5 format:
  https://github.com/Vulnogram/Vulnogram
  - A hosted instance is available at: https://vulnogram.github.io/#editor

## Development Setup

[uv](https://github.com/astral-sh/uv) is the recommended tool for local development:

```bash
git clone https://github.com/RedHatProductSecurity/cvelib.git && cd cvelib
uv sync --dev
```

The `uv sync --dev` command will:
- Create a virtual environment in `.venv/`
- Install the project in editable mode
- Install all development dependencies (test, dev groups)

To run all of the below commands without `uv run`, activate the virtual environment:

```bash
source .venv/bin/activate
```

To enable command autocompletion when using a virtual environment, add the line noted in `Command Autocompletion`
above to your virtual environment's activate file:

```bash
echo 'eval "$(_CVE_COMPLETE=bash_source cve)"' >> .venv/bin/activate
```

This project uses [ruff formatter](https://docs.astral.sh/ruff/formatter/) for code formatting.
To reformat the entire code base after you make any changes, run:

```bash
uv run ruff format .
```

To sort all imports using [ruff's import sorting](https://docs.astral.sh/ruff/rules/#isort-i), run:

```bash
uv run ruff check --select I --fix .
```

Running tests and linters:

```bash
# Run all tests and format/lint checks (also run as a Github action)
uv run tox
# Run lint check only
uv run tox -e ruff-lint
# Run format check only
uv run tox -e ruff-format
# Run tests using a specific version of Python
uv run tox -e py313
# Run a single test using a specific version of Python
uv run tox -e py313 -- tests/test_cli.py::test_cve_show
```

Any changes in the commands, their options, or help texts must be reflected in the generated man pages. To refresh
them, run:

```bash
uv run click-man cve
# OR
uv run tox -e manpages
```

## Releasing

1. Make version bump release commit
   ([example](https://github.com/RedHatProductSecurity/cvelib/commit/0e188b48b61a3659d1e923c08e4f980c034bf445))
   that also refreshes all man pages and updates any compatibility statements in the README file. Merge the PR to master.
2. Remove previously built packages and build new ones:
   ```shell
   $ rm dist/*
   $ uv build
   $ ls dist/  # You should see two artifacts in this directory afterwards, e.g.:
   cvelib-0.6.0-py3-none-any.whl  cvelib-0.6.0.tar.gz
   ```
3. Publish package to PyPI test:
   ```shell
   uv publish --publish-url https://test.pypi.org/legacy/`
   ```
4. Test that installation and basic functionality work:
   ```shell
   uv run --no-cache --with cvelib --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple cve -h
   ```
   Replace `cve -h` with other commands to test other functionality.
5. Publish package to PyPI:
   ```shell
   uv publish
   ```
6. Tag the version bump commit as 'x.y.z' and push to master:
   ```shell
   git tag 1.8.0
   git push --tags
   ```
   This triggers a new container image build in Quay.io: https://quay.io/repository/prodsecdev/cvelib?tab=builds.
7. When the container image build completes, move the _latest_ tag to point to new release:
   https://quay.io/repository/prodsecdev/cvelib?tab=tags
   (click cog icon next to new version) -> Add new tag -> latest -> Move tag)
8. Create a release in GitHub manually against the newly pushed tag:
   https://github.com/RedHatProductSecurity/cvelib/releases. Use the template from a previous release and add a list of
   changes from the changelog.

---

[CVE](https://cve.org) is a registered trademark of [The MITRE Corporation](https://www.mitre.org).
