CVE Analysis

Automated analysis of HUM Jira Security (CVE) tickets. For each ticket, the tool fetches vulnerability data from MITRE and NVD, compares it against the versions shipped in the Hummingbird package repository, searches for upstream and Fedora fixes, and computes whether the ticket should be closed, moved to In Progress, or flagged for manual investigation.

How It Works

1. Ticket Selection

The tool queries Jira for HUM project tickets with component Security. Tickets can be filtered by creation period (--show-since "2 weeks"), limited to specific keys (HUM-796 HUM-518), or scanned in bulk. Closed tickets are skipped by default unless --include-closed is specified or specific ticket keys are given on the command line.

Embargoed tickets (security level “Embargoed Security Issue” or embargo status field set to true) are always skipped.

2. CVE Data Collection

For each ticket the tool:

  1. Extracts the CVE ID from the Jira summary field using a regex match for CVE-YYYY-NNNN patterns.

  2. Fetches the CVE record from the MITRE CVE API (cveawg.mitre.org/api/cve/<id>) and parses the CVE 5.0 affected block, following the CVE 5.0 Product and Version Encodings specification. This includes:

    • Parsing lessThan and lessThanOrEqual version ranges
    • Handling lessThan: "*" (no upper bound) and lessThan: "4.*" (end-of-series, all versions in the 4.x series) wildcards per the spec
    • Processing changes lists that subdivide ranges into affected and unaffected segments
    • Respecting defaultStatus at the product level
    • Filtering out versionType: "git" entries that use commit hashes instead of numeric versions (these are preserved as informational data but not used for version comparison)
    • Detecting CNA data errors where git commit hashes are used in version fields without versionType: "git", including hashes wrapped in operator syntax (e.g., < 6374ae0bcdfe...)
    • Parsing inline comparison operators (< 12.3.0, >= 4.0, <= 2.5) that are not part of the CVE 5.0 schema but are widely used by CNAs in practice
    • Parsing compound range bands (>= 2.0, < 2.2.26 and > 1.32.3, < 1.34.6) used by some CNAs to express a closed-open range in a single version string
  3. Fetches version ranges from the NVD API (services.nvd.nist.gov/rest/json/cves/2.0) and merges them with the MITRE data. When MITRE has no vendor-specific data (all vendors are “n/a”), NVD ranges replace the MITRE data entirely. An NVD API key (--nvd-api-key) enables higher rate limits. NVD references tagged “Patch” or matching commit URL patterns are also extracted and used for upstream fix detection (see below).

  4. Looks up the Hummingbird package version by scraping the Pulp repository index at packages.redhat.com for the latest RPM matching the package name extracted from the ticket summary.

3. Resolution Computation

The tool compares the shipped Hummingbird version against the affected version ranges to compute a recommended resolution:

  • Closed / Done-Errata: The repo version is not in any affected range, or the repo version is >= the fix version, or the repo version appears in the “not affected” list.
  • In Progress / affected: The repo version falls within an affected range.
  • In Progress / affected (no version data): Neither MITRE nor NVD has affected version information. The ticket is moved to In Progress for manual review but does not receive the cve-needs-attention label.
  • In Progress / needs investigation: Version data was available but could not be compared (e.g., CVE only provides git commit hashes), or multiple distinct products with different versioning schemes are listed and no cve_product override is configured. These cases receive the cve-needs-attention label.

4. Product Mismatch Detection

The tool detects when a CVE was filed against the wrong Hummingbird package. It compares the CVE vendor/product names against the Hummingbird package name and upstream repo URL from the package map. For example, a CVE for isaacs/node-tar (an npm package) filed against the tar RPM (GNU tar) is flagged as a mismatch. This detection works even without a repo URL by comparing normalized product names.

When a mismatch is detected, the tool checks the vendored dependency map (vendored_deps.csv) to see if the CVE product is a vendored dependency inside another Hummingbird SRPM. If found:

  • The warning changes from “likely misfiled” to “CVE is for vendored dependency X (version in parent SRPM)”
  • The vendored version is used for resolution comparison instead of the parent package version
  • The upstream fix search runs against the vendored dep’s repo (from vendored_source.csv) instead of the parent package’s repo
  • The output shows “Vendored version” and the correct upstream repo

When no vendored match is found:

  • The computed resolution is overridden to “New / needs investigation (product mismatch)”
  • The cve-needs-attention label is applied
  • A Jira comment is posted explaining the mismatch

5. Upstream Fix Detection

The tool uses two complementary methods to find upstream fixes:

NVD Patch References (checked first): When the NVD response includes reference URLs tagged “Patch” or matching commit URL patterns (GitHub /commit/, gitweb commitdiff, cgit commit/?id=), these are used directly as confirmed upstream fixes. This skips the forge search for that CVE, saving API calls and finding fixes that forge search might miss.

Forge Search (fallback): Using the package map (from the rpms repo metadata/*.json files), the tool looks up the upstream repository for each package and searches for fixes by forge type:

  • GitHub: Searches PRs via the GitHub API (/search/issues) for PRs mentioning the CVE ID. Fetches commit counts from the pulls API.
  • GitLab: Searches merge requests via the GitLab API on supported instances (gitlab.com, gitlab.gnome.org, gitlab.freedesktop.org, etc.). The --gitlab-token is only sent to gitlab.com to avoid 401 errors on other instances.
  • cgit: Scrapes commit log pages on cgit hosts (Savannah, Sourceware, kernel.org, busybox.net, etc.) for commits mentioning the CVE ID. Handles URL rewrites for Savannah hosts (e.g., git.savannah.gnu.org/git/ to cgit.git.savannah.gnu.org/cgit/).

Fix status is classified as:

  • upstream-fix-available: At least one merged/closed PR, committed fix, or NVD patch reference
  • upstream-fix-in-progress: At least one open PR with no merged fixes

6. Fedora Fix Detection

The tool checks for Fedora fixes through two channels:

  • Bodhi: Queries the Fedora Bodhi API for updates matching the package name that reference the CVE ID (in the cves list or notes field). Supports multi-page results. Classifies updates as fedora-fix-available (stable) or fedora-fix-in-progress (testing/pending).
  • DistGit Spec Scan: When Bodhi has no matches, fetches the Fedora DistGit spec file and scans for CVE references in patch filenames, changelog entries, and comments to detect backported fixes.

7. Fixed Build Detection

The tool automatically detects whether the current Hummingbird build fixed a CVE by checking if all CVE IDs are mentioned anywhere in the package directory (patch names, changelogs, comments in the .spec file and so on). If found, it returns the current source RPM name. Assuming that we run the analysis frequently enough, this is precise enough, otherwise it errs on the side of caution (i.e. it possibly marks a higher version as fixed even if an earlier one already carried the fix).

The detected build is shown in the output as Detected Fix: package-version-release.hum1.src.rpm.

When --resolve is specified and a fixed build is detected, the tool automatically populates the “Fixed in Build” Jira field with the detected SRPM name, but only if:

  • The field is not already set (manual values take precedence)
  • The issue is not in Closed status (respects human closure decisions)

The RPMs repository can be provided via --rpms-repo, pointing to a local clone. This is preferable with multiple runs to avoid the git clone operation. When not given, the repository will be (shallow) cloned into a temporary directory.

8. Advisory Integration (with --resolve)

When a CVE is resolved as “Closed / Done-Errata”, the tool integrates with the CEE GitLab advisories repo to document the fix:

  1. Clone: The advisories repo (default: releng/advisories) is shallow-cloned via a bot fork using netrc-based authentication (no tokens in process arguments or logs).
  2. Modify: For each resolved ticket, the advisory YAML is updated: cves.fixed entries are added, the type is changed from RHBA to RHSA, and CVE references are appended.
  3. Batch MR: All advisory changes are accumulated as individual commits on a single branch (cve-analysis/batch). One merge request is created at the end of the run covering all tickets.
  4. Review state: Tickets are transitioned to “Review” (not directly closed) and the batch MR URL is posted to each affected ticket.
  5. Auto-close: On subsequent runs, tickets in “Review” are checked: if the advisory MR has been merged, the ticket is closed as “Done-Errata”. If the MR has unresolvable rebase conflicts, a comment with manual resolution steps is posted and the advisory-mr-failed label is added.

The advisory flow is skipped when --advisories-project points to a non-production URL, allowing safe testing without modifying Jira.

9. Jira Actions (with --resolve)

When --resolve is specified, the tool modifies Jira tickets:

  • Fixed in Build: When a fixed build is detected and the field is not already set, the tool populates it with the detected SRPM name (only for non-closed issues).
  • Closed / Done-Errata: Tickets where the shipped version is not affected are transitioned to Review with an advisory MR (see above). After the MR merges, they are closed as Done-Errata on the next run.
  • Move to In Progress: Tickets where the shipped version is affected are transitioned to In Progress with a comment including affected version ranges, upstream fix status, and Fedora update links.
  • Needs Investigation: Tickets with incomplete version data get a comment explaining why automatic resolution was not possible.
  • Product Mismatch: A comment is posted explaining the mismatch and the cve-needs-attention label is applied. No transition is performed.
  • Label Management: Labels are applied and updated:
    • upstream-fix-available / upstream-fix-in-progress
    • fedora-fix-available / fedora-fix-in-progress
    • cve-needs-attention (applied when human review is needed; removed when resolved)
    • Labels are upgraded (in-progress to available) and stale labels are cleaned up on ticket closure.

Every Jira action (label add/remove, status transition, product mismatch) includes a single comment with a bold action summary heading followed by the full analysis output in a preformatted code block, giving the reader the same detail they would see on the CLI.

10. Assignee-Based Automation Control

When --resolve is active, the tool checks each ticket’s assignee. If the assignee is not the bot account (--jira-user), the tool skips all automated actions (comments, labels, transitions) for that ticket. A one-time comment is posted explaining that automation is skipped. This allows humans to take ownership of a ticket by assigning it to themselves, preventing the bot from interfering with manual work. Reassigning back to the bot account re-enables automation.

For testing purposes, use --skip-assignee-check to process all tickets regardless of assignee.

11. Human Closure Protection

When the tool encounters a Closed ticket whose analysis recommends a non-Closed resolution (e.g., In Progress), it checks the Jira changelog to determine who performed the last status transition. If a human (not the bot) closed the ticket, the tool skips the transition to respect the human’s decision. This prevents the tool from reopening tickets where a fix was backported without a version bump or where a human determined the CVE does not apply.

12. Stale Closure Detection (with --include-closed)

When --include-closed is used, the tool also analyzes Closed tickets. If the current analysis recommends a non-Closed resolution (e.g., the package version changed and is now affected), a warning is emitted flagging the ticket for review.

13. Continuous Operation

When --time-between-runs N is set (N > 0), the tool runs in a loop, re-executing the full analysis every N minutes. The default is 0 (single run). Shutdown is graceful: SIGINT/SIGTERM finishes the current cycle before exiting.

14. Feature Flag

When --feature-flag-name is set (default: cve_analysis_enabled), the tool checks a GitLab feature flag on the project specified by --feature-flag-project-id (default: 73447720, i.e. redhat/hummingbird/rpms) at the start of each cycle. If the flag is inactive, the cycle is skipped and the tool sleeps for 60 seconds before checking again.

The check uses the --gitlab-token / GITLAB_TOKEN credential, which must have Developer role or higher on the target project. The check is fail-open: if GitLab is unreachable or the token lacks permissions, the tool assumes the flag is enabled and proceeds.

Set --feature-flag-name "" to disable the check entirely.

Package Map

The package map is loaded from the rpms repo’s per-package metadata files (metadata/<package>.json). Each file contains an upstream_repo field pointing to the canonical upstream git repository, plus optional fields:

  • upstream_branch – upstream branch (for versioned packages sharing a repo)
  • cve_product – CVE vendor/product override
  • version_transform – version transform rule

The cve_product field is an optional human-curated value that specifies which CVE vendor/product entry maps to this package. It supports two formats:

  • Vendor / Product (exact match): matches a specific vendor and product pair. Example: "F5 / NGINX Open Source" for the nginx package, which excludes NGINX Plus entries that use incompatible R-versioning.
  • Vendor (vendor prefix match): matches any product whose vendor starts with the given string. Example: "Go " for golang packages, where the vendor varies (Go standard library, Go toolchain, etc.) and the product varies by module (net/url, os, crypto/x509).

When cve_product is set, the tool uses it for exact product matching in both resolution computation (filtering to the correct product in multi-product CVEs) and mismatch detection. When empty, the tool falls back to heuristic name matching.

The rpms repo is cloned automatically at startup (or provided via --rpms-repo). A CSV override can be passed via --package-map for backward compatibility.

Vendored Package Map

The vendored_source.csv and vendored_deps.csv files map vendored dependencies (Go modules, Rust crates, npm packages) inside Hummingbird SRPMs to their upstream repositories. These are generated by the generate_vendored_map.py script, which downloads SRPMs, extracts vendored lock files (vendor/modules.txt, Cargo.lock, package-lock.json), and resolves upstream repos via the crates.io, npm registry, and Go module path conventions.

During analysis, when a CVE product mismatch is detected, the tool searches vendored_deps.csv for the CVE product name. Go subpackages are matched by progressively stripping path components (e.g., github.com/jackc/pgx/v5/pgproto3 matches github.com/jackc/pgx/v5). When a match is found, the vendored version is used for resolution and the upstream fix search targets the vendored dep’s repo from vendored_source.csv. The --vendored-deps-map option can override the default path.

Prerequisites

  • Python 3.11 or later
  • Jira API token (Bearer or Basic auth)
  • Network access to redhat.atlassian.net, cveawg.mitre.org, services.nvd.nist.gov, bodhi.fedoraproject.org, src.fedoraproject.org, and packages.redhat.com
  • GitHub API token (optional, for upstream PR search)
  • GitLab API token (optional, for gitlab.com MR search)
  • CEE GitLab API token (required with --resolve, for advisory repo)

Usage

# Basic usage with Jira token from environment
export JIRA_TOKEN=your_token
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com

# Analyze specific tickets
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com HUM-796 HUM-518

# Show only tickets from the last 2 weeks
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com --show-since "2 weeks"

# Analyze with upstream fix detection (report findings without modifying Jira)
export GITHUB_TOKEN=your_github_token
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com

# Resolve tickets and apply labels
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com --resolve

# Include closed tickets and check for stale closures
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com --include-closed

# Include closed tickets but exclude specific ones
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com \
  --include-closed --exclude "HUM-555,HUM-552"

# JSON output, skip issues without CVE links
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com -o json-pretty --skip-no-cve

# Continuous mode: resolve tickets every 30 minutes
python3 -m hummingbird_tools.cve_analysis --jira-user user@example.com \
  --resolve --time-between-runs 30

Configuration

Option Environment Variable Description
--jira-token JIRA_TOKEN Jira API or Bearer token
--jira-url Jira base URL (default: https://redhat.atlassian.net)
--jira-user Username for Basic auth
--output, -o Output format: human, json, json-pretty
--show-since Filter by creation period (e.g. 2 weeks, 3 hours)
--resolve Transition Jira tickets and apply labels
--skip-assignee-check Skip assignee validation (for testing)
--include-closed Include Closed tickets in analysis; warn on stale closures
--exclude Comma-separated ticket keys to skip (e.g. HUM-555,HUM-552)
--skip-no-cve Omit issues that have no CVE link or ID
--max-results Max number of issues to fetch (default: 2000)
--github-token GITHUB_TOKEN GitHub API token for upstream PR search
--gitlab-token GITLAB_TOKEN GitLab API token for gitlab.com MR search
--nvd-api-key NVD_API_KEY NVD API key for higher rate limits
--cee-gitlab-token CEE_GITLAB_TOKEN CEE GitLab token for advisory repo operations
--advisories-project Advisories repo URL (default: releng/advisories)
--advisories-fork Bot’s fork URL for advisory MR creation
--no-merge-request Skip advisory MR creation during --resolve
--test-advisory Create advisory MR then immediately close it (for testing)
--package-map Path to CSV override; by default uses rpms repo metadata
--vendored-deps-map Path to vendored_deps.csv (auto-detected by default)
--rpms-repo Path to RPMs git repo for fixed build detection
--time-between-runs Re-run every N minutes; 0 = single run (default)
--feature-flag-project-id GitLab project ID for feature flag lookup (default: 73447720, rpms project)
--feature-flag-name Feature flag name to check each cycle (default: cve_analysis_enabled; empty = disabled)
SENTRY_DSN Optional Sentry DSN for error tracking

Managed Labels

The tool manages the following labels on Jira tickets. These are automatically applied, upgraded, and cleaned up:

Label Meaning
upstream-fix-available A merged PR or committed fix exists upstream
upstream-fix-in-progress An open PR exists upstream (no merged fix yet)
fedora-fix-available A stable Fedora Bodhi update references this CVE
fedora-fix-in-progress A testing/pending Bodhi update references this
fedora-bz-filed A Fedora Bugzilla has been filed for this CVE
cve-needs-attention Analysis needs human attention (see below)
advisory-mr-failed Advisory MR has unresolvable rebase conflicts

Labels are upgraded automatically (e.g., upstream-fix-in-progress is replaced by upstream-fix-available when a fix is merged). Stale labels are removed when tickets are closed, except fedora-bz-filed which is preserved as an audit record. The cve-needs-attention label is removed automatically when the warning condition no longer applies.

cve-needs-attention conditions

The cve-needs-attention label is applied when any of these conditions are detected:

  • Product mismatch: CVE vendor/product does not match the Hummingbird package (e.g. node-tar CVE filed against GNU tar)
  • Multiple products: CVE lists multiple distinct products with different versioning schemes and no cve_product override is configured (e.g. NGINX Open Source + NGINX Plus)
  • Git-only version data: CVE version data uses commit hashes instead of numeric versions (e.g. libsodium with lessThan: ad3004ec...)
  • CNA data error: A git commit hash is used where a version number is expected without setting versionType: "git", including hashes embedded in operator syntax (e.g., "version": "< 6374ae0bcdfe..."). The warning includes a link to the CVE 5.0 source control versions spec
  • Malformed version field: The version field contains syntax that could not be parsed (e.g., compound operator+hash ranges like >= hash1, < hash2)
  • CVE fetch failure: the MITRE CVE API request failed
  • No repo version: the Hummingbird package was not found in the Pulp repository (package name mismatch or missing SRPM)
  • Stale closure: ticket is Closed in Jira but current analysis recommends a different resolution

Human override workflow

When a human wants to take over a ticket from the bot:

  1. Assign the ticket to yourself – the bot skips all automation on tickets not assigned to the bot account. Reassign to the bot to re-enable automation.

  2. Set “Fixed in Build” – if you know the fix is in a specific SRPM, set the “Fixed in Build” field to the SRPM name (e.g. libarchive-3.8.7-1.hum1.src.rpm). The bot will use this to create the advisory MR and close the ticket, bypassing its own analysis.

Migration note: The cve-analysis-okay label is deprecated and no longer suppresses automation. Existing tickets with this label will be re-processed by the bot on its next run. To keep the bot from touching a specific ticket, reassign it to yourself before the next run. The cve-analysis-okay label can then be removed manually.

Output

Human-Readable

Project: HUM  Component: Security

  HUM-796  CVE-2026-2673 openssl: buffer overflow [hummingbird-1]
    Open since:       7 days (Mar 24 2026)
    Labels resolution: upstream-fix-available
    OpenSSL / OpenSSL:
      Affected versions: 3.5.0 < 3.5.6
      Fixed in:          3.5.6
      Hummingbird repo (latest):  3.5.5 / openssl-3.5.5-1.hum1.src.rpm
    Repo:             https://github.com/openssl/openssl
    CVE-2026-2673: upstream-fix-available (3 PRs)
      [CLOSED, 3 commits] Fix group tuple handling in DEFAULT expansion (3.5)
              https://github.com/openssl/openssl/pull/30110
    Fedora update: FEDORA-2026-abc123 openssl-3.5.6-1.fc44 (stable, security)
              https://bodhi.fedoraproject.org/updates/FEDORA-2026-abc123
    Jira current:     In Progress / (none)
    Jira resolution:  In Progress / affected (repo 3.5.5 is in affected range 3.5.0 < 3.5.6)

JSON

Each issue includes: key, summary, labels, open_since, cve_ids, cves (with version and resolution data), jira_current (status/resolution), computed_resolution, and upstream (with PR/MR search results, Bodhi updates, and Fedora version data).

Development

See the main README for development workflows.

make hummingbird-cve-analysis/setup  # Install dependencies
make check                            # Lint code (ruff)
make test                             # Run unit tests

License

This project is licensed under the GNU General Public License v3.0 or later - see the LICENSE file for details.