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
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
permissions:
contents: read
security-events: write
steps:
- name: Run TuxCare VEX Auto-Triage
uses: tuxcare/vex-auto-triage@v1
with:
ecosystems: 'java,python'
token: ${{ secrets.GITHUB_TOKEN }}| Input | Required | Default | Description |
|---|---|---|---|
ecosystems |
Yes | - | Comma-separated list of ecosystems to process: java, python, javascript, php |
token |
Yes | - | GitHub token with security_events write permission |
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 |
The action requires the following permissions:
permissions:
contents: read # To access repository
security-events: write # To dismiss alerts- uses: tuxcare/vex-auto-triage@v1
with:
ecosystems: 'java'
token: ${{ secrets.GITHUB_TOKEN }}- uses: tuxcare/vex-auto-triage@v1
with:
ecosystems: 'java,python,javascript,php'
token: ${{ secrets.GITHUB_TOKEN }}Test the action without dismissing alerts:
- uses: tuxcare/vex-auto-triage@v1
with:
ecosystems: 'java'
token: ${{ secrets.GITHUB_TOKEN }}
dry-run: 'true'
verbosity: 'DEBUG'Process only the first 10 alerts:
- uses: tuxcare/vex-auto-triage@v1
with:
ecosystems: 'java'
token: ${{ secrets.GITHUB_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
permissions:
contents: read
security-events: write
steps:
- uses: tuxcare/vex-auto-triage@v1
with:
ecosystems: 'java,python'
token: ${{ secrets.GITHUB_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.
Quick diagnosis:
export GITHUB_TOKEN="your-token"
export GITHUB_REPOSITORY="owner/repo"
python debug_permissions.pyMost common fix: Ensure your workflow has proper permissions:
permissions:
contents: read
security-events: write # ← Required!See TROUBLESHOOTING.md for detailed solutions.
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
Error: Resource not accessible by integration
Solution: Ensure your workflow has the required permissions:
permissions:
security-events: writeIf 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.
The provided token must have security_events write permission. Use ${{ secrets.GITHUB_TOKEN }} which is automatically scoped to the repository.
- 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
- No version checking for unpublished versions: Action assumes VEX URLs are correct
- 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/tuxcare/vex-auto-triage/issues
- Check logs with
verbosity: DEBUG - Test with
dry-run: truefirst
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
See LICENSE file for details.
See CHANGELOG.md for version history.