Automatically dismiss GitHub Dependabot security alerts based on TuxCare VEX (Vulnerability Exploitability eXchange) data.
TuxCare provides Extended Lifecycle Support (ELS) for end-of-life software packages, including security patches for Java, Python, JavaScript, and PHP libraries. When using TuxCare's patched versions, you may still receive Dependabot alerts for vulnerabilities that have already been fixed in the TuxCare-maintained versions.
This GitHub Action bridges that gap by automatically dismissing alerts for vulnerabilities that TuxCare has marked as "resolved" in their VEX documentation, reducing alert fatigue and false positives.
- Multi-ecosystem support: Java (Maven), Python (pip), JavaScript (npm), PHP (Composer)
- Smart version matching: Compares vulnerable version ranges with TuxCare patched versions
- Rate limiting: Respects GitHub API rate limits with automatic backoff
- Dry-run mode: Test the action without actually dismissing alerts
- Comprehensive logging: Detailed logs with GitHub Actions annotations
- Fresh data: Fetches latest VEX data on every run (no stale cache issues)
- Retry logic: Exponential backoff for transient failures
Before using this action, you need to:
Enable Dependabot in your repository to receive security alerts:
- Go to your repository on GitHub
- Click Settings → Advanced Security
- Enable Dependabot alerts (if not already enabled)
- Enable Dependabot security updates (optional, but recommended)
Check that you have Dependabot alerts:
- Go to your repository's Security tab
- Click Dependabot in the left sidebar
- You should see a list of open vulnerability alerts
- Note the ecosystems (Maven, pip, npm, Composer) of your alerts
If you have no alerts, Dependabot hasn't found any vulnerabilities in your dependencies.
This action requires a Personal Access Token (PAT) with the appropriate permissions to access and dismiss Dependabot alerts.
Create a PAT:
-
Go to GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
-
Click Generate new token → Generate new token (classic)
-
Configure the token:
- Note:
VEX Auto-Triage - Expiration: 90 days or custom (set a calendar reminder to regenerate)
- Select Scopes:
- repo (selects all sub-items of
repo)
- repo (selects all sub-items of
- Note:
-
Click Generate token and copy it immediately
Add token to repository secrets:
- Go to your repository Settings → Secrets and variables → Actions
- Click New repository secret
- Name:
VEX_TRIAGE_TOKEN - Value: Paste your PAT
- Click Add secret
After the action runs (you may run it manually), verify alerts were dismissed:
- Go to Security → Dependabot
- Click the Closed tab at the top
- Each dismissal will show:
- The bot that dismissed it (dependabot)
- Timestamp
- Link to the related code snippet
You can also check the action's output in the Actions tab to see a summary of dismissed alerts.
Add this workflow to your repository at .github/workflows/vex-triage.yml:
name: TuxCare VEX Auto-Triage
on:
schedule:
- cron: '0 2 * * *' # Daily at 2 AM
workflow_dispatch: # Manual trigger
jobs:
triage:
runs-on: ubuntu-latest
steps:
- name: Run TuxCare VEX Auto-Triage
uses: cloudlinux/vex-triage@v1
with:
ecosystems: 'java,python'
token: ${{ secrets.VEX_TRIAGE_TOKEN }}| Input | Required | Default | Description |
|---|---|---|---|
ecosystems |
Yes | - | Comma-separated list of ecosystems to process: java, python, javascript, php |
token |
Yes | - | Personal Access Token (PAT) with repo permission, stored in secrets (e.g., ${{ secrets.VEX_TRIAGE_TOKEN }}). See Prerequisites for setup. |
dry-run |
No | false |
If true, simulates dismissals without actually dismissing alerts |
max-alerts |
No | 0 |
Maximum number of alerts to process (0 = unlimited, useful for testing) |
verbosity |
No | INFO |
Logging level: DEBUG, INFO, WARNING, ERROR |
Your Personal Access Token (PAT) must have the repo permissions:
See Prerequisites for detailed instructions on creating a PAT with these permissions.
- uses: cloudlinux/vex-triage@v1
with:
ecosystems: 'java'
token: ${{ secrets.VEX_TRIAGE_TOKEN }}- uses: cloudlinux/vex-triage@v1
with:
ecosystems: 'java,python,javascript,php'
token: ${{ secrets.VEX_TRIAGE_TOKEN }}Test the action without dismissing alerts:
- uses: cloudlinux/vex-triage@v1
with:
ecosystems: 'java'
token: ${{ secrets.VEX_TRIAGE_TOKEN }}
dry-run: 'true'
verbosity: 'DEBUG'Process only the first 10 alerts:
- uses: cloudlinux/vex-triage@v1
with:
ecosystems: 'java'
token: ${{ secrets.VEX_TRIAGE_TOKEN }}
max-alerts: '10'name: Manual VEX Triage
on:
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run mode'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: cloudlinux/vex-triage@v1
with:
ecosystems: 'java,python'
token: ${{ secrets.VEX_TRIAGE_TOKEN }}
dry-run: ${{ inputs.dry_run }}- Fetch VEX Data: Downloads latest VEX files from TuxCare for selected ecosystems
- Index CVEs: Parses VEX data into efficient lookup structures
- Fetch Alerts: Retrieves all open Dependabot security alerts from GitHub
- Match & Triage: For each alert:
- Extracts CVE identifier
- Checks if CVE exists in VEX data
- Verifies package name matches
- Confirms vulnerability is marked as "resolved"
- Validates version compatibility
- Dismisses alert if all checks pass
- Report Results: Outputs summary with dismissed and skipped alerts
The action uses exact version matching to ensure accuracy and prevent false dismissals:
- Extract actual version: Gets the exact version from your manifest (pom.xml, requirements.txt, etc.)
- Check TuxCare suffix: Verifies the actual version has the
tuxcaresuffix - Compare base versions: If using TuxCare version, compares the base version with VEX data
- Validate: Confirms the fix applies to the specific package and CVE
Critical: Alerts are only dismissed if your repository is using the TuxCare patched version (with tuxcare suffix), not just because a patched version exists in VEX.
Your pom.xml: log4j:log4j:1.2.17
Alert: CVE-2021-4104 in log4j affecting >= 1.2.0, <= 1.2.17
VEX Data: TuxCare has log4j@1.2.17.tuxcare.1 marked as "resolved"
Action:
- Extracts actual version:
1.2.17(no.tuxcaresuffix) - Recognizes repository is NOT using TuxCare patched version
- Skips dismissal - you don't have the fix
Your pom.xml: log4j:log4j:1.2.17.tuxcare.1
Alert: CVE-2021-4104 in log4j affecting >= 1.2.0, <= 1.2.17
VEX Data: TuxCare has log4j@1.2.17.tuxcare.1 marked as "resolved"
Action:
- Extracts actual version:
1.2.17.tuxcare.1(has.tuxcaresuffix) ✓ - Normalizes both versions:
1.2.17==1.2.17✓ - Verifies package name matches ✓
- Confirms state is "resolved" ✓
- Dismisses the alert - you have the fix
The action fetches VEX data from TuxCare's public endpoints:
- Java: https://security.tuxcare.com/vex/cyclonedx/els_lang_java/vex.json
- Python: https://security.tuxcare.com/vex/cyclonedx/els_lang_python/vex.json
- JavaScript: https://security.tuxcare.com/vex/cyclonedx/els_lang_javascript/vex.json
- PHP: https://security.tuxcare.com/vex/cyclonedx/els_lang_php/vex.json
VEX data is fetched fresh on every run to ensure you're always working with the latest information.
The action outputs a JSON summary with detailed statistics:
{
"total_alerts": 50,
"dismissed_count": 8,
"dismissed": [
{
"number": 42,
"package": "com.google.guava:guava",
"cve": "CVE-2020-8908",
"tuxcare_version": "30.1-jre.tuxcare",
"vex_package": "com.google.guava:guava"
}
],
"skipped_count": 42,
"skipped_by_reason": {
"ecosystem-not-selected": 20,
"cve-not-in-vex": 15,
"no-positive-vex-match": 7
},
"ecosystems_processed": ["java", "python"],
"execution_time_seconds": 12.5
}Results are also displayed in the GitHub Actions UI as a step summary with tables showing:
- Overview statistics
- List of dismissed alerts
- Breakdown of skip reasons
- Execution time
The action creates GitHub Actions annotations for:
- Notices: Successfully dismissed alerts
- Warnings: Rate limiting or retries
- Errors: Configuration or API errors
Alerts may be skipped for various reasons:
| Reason | Description |
|---|---|
no-cve |
No CVE identifier found in alert |
unsupported-ecosystem |
Ecosystem not supported by action |
ecosystem-not-selected |
Ecosystem not in selected list |
no-vex-data |
Failed to load VEX data for ecosystem |
cve-not-in-vex |
CVE not found in VEX data |
no-actual-version |
Cannot determine actual version from manifest |
no-positive-vex-match |
Repository not using TuxCare patched version |
invalid-package |
Package name format invalid |
dismiss-failed |
API call to dismiss alert failed |
If the action reports 0 alerts but you can see alerts in the GitHub UI, this is almost always a permissions issue.
Solutions:
-
Verify your PAT has the correct permissions:
- Follow the Prerequisites section to create a properly configured PAT
- Ensure the token has
Security events: Read and writepermission - Verify the token has access to your repository
- Confirm the secret is named correctly in your workflow (e.g.,
VEX_TRIAGE_TOKEN)
-
Verify Dependabot is enabled:
- See Prerequisites section
- Check that alerts are visible in Security → Dependabot alerts
- Ensure you have open (not closed) alerts
Possible causes:
- VEX data doesn't cover your packages: TuxCare VEX only includes packages they provide ELS for
- Version mismatch: Your package version might not match TuxCare's patched version
- Wrong ecosystem selected: Ensure you've selected the correct ecosystems
Solution: Run with dry-run: true and verbosity: DEBUG to see detailed matching logic.
The action automatically handles rate limiting, but if you hit limits:
- The action will wait until the rate limit resets
- Consider running less frequently (e.g., daily instead of hourly)
- Check your organization's rate limits
If VEX data fails to download:
- The action will retry up to 3 times with exponential backoff
- Check TuxCare's status page for service issues
- Verify network connectivity from GitHub Actions
This action implicitly trusts TuxCare VEX data as authoritative. Dismissed alerts can always be manually re-opened if needed.
Alerts are dismissed with reason "INACCURATE" (GitHub doesn't have a "PATCHED" reason). GitHub maintains a full audit trail with timestamps.
- All dismissed alerts can be manually re-opened in the GitHub Security tab
- GitHub maintains full history of dismissals
- Dry-run mode allows testing without side effects
- Ecosystem coverage: Only Maven, pip, npm, and Composer are supported
- VEX dependency: Requires TuxCare VEX data for your packages
- API rate limits: Subject to GitHub GraphQL API limits (typically 5000/hour)
- Website: https://tuxcare.com/
- VEX Repository: https://security.tuxcare.com/vex/
- Support: support@tuxcare.com
- Documentation: https://docs.github.com/en/code-security/dependabot
For issues with this action:
- GitHub Issues: https://github.com/cloudlinux/vex-triage/issues
- Check logs with
verbosity: DEBUG - Test with
dry-run: truefirst
This project uses modern Python tooling with uv for fast dependency management and pyproject.toml for configuration.
# Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Clone the repository
git clone <repo-url>
cd tuxcare-vex
# Create virtual environment and install dependencies (10x faster than pip!)
uv venv
source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
uv pip install -e ".[dev]"Production dependency:
# Edit pyproject.toml [project.dependencies]
dependencies = [
"new-package>=1.0.0",
]Development dependency:
# Edit pyproject.toml [project.optional-dependencies.dev]
[project.optional-dependencies]
dev = [
"new-dev-tool>=1.0.0",
]Then install:
uv pip install -e ".[dev]"This project requires Python 3.11+ for modern features:
- PEP 604: Union types with
|operator - PEP 634: Structural pattern matching
- Better error messages and performance
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Run tests and linting:
./run_tests.sh - Submit a pull request
- Use modern Python 3.11+ features (type hints, pattern matching)
- Add tests for all new functionality
- Keep test coverage high
- Follow existing code style (black + ruff)
- Update documentation for user-facing changes
See LICENSE file for details.