# ZeroDayFeedParser
A Java-based cybersecurity threat intelligence tool that fetches live CVE data from the NIST National Vulnerability Database (NVD), ranks vulnerabilities by CVSS severity, exports results to CSV, and fires real-time Discord alerts for critical threats.
Built as a capstone project demonstrating REST API integration, JSON parsing, threat ranking logic, and automated alerting.
---
## Table of Contents
- [Features](#features)
- [Project Structure](#project-structure)
- [Prerequisites](#prerequisites)
- [Setup & Installation](#setup--installation)
- [Configuration](#configuration)
- [Running the Application](#running-the-application)
- [Output](#output)
- [Architecture Overview](#architecture-overview)
- [Class Reference](#class-reference)
- [NVD API Notes](#nvd-api-notes)
- [Discord Webhook Setup](#discord-webhook-setup)
- [Extending the Project](#extending-the-project)
- [Known Limitations](#known-limitations)
- [Dependencies](#dependencies)
---
## Features
- Fetches the 20 most recent CVEs from the NVD 2.0 REST API
- Parses CVSS v3.1 / v3.0 severity scores and affected product/vendor from CPE data
- Ranks all threats by severity (highest first)
- Filters to High (≥ 7.0) and Critical (≥ 9.0) entries for export and alerting
- Exports ranked results to a timestamped CSV in reports/
- Posts Critical CVEs to a Discord channel via webhook
- Supports --dry-run mode to suppress Discord alerts during testing
- Gracefully handles NVD rate limiting with a clear error message
- Reads API key and webhook URL from environment variables — no credentials in source
---
## Project Structure
ZeroDayFeedParser/
├── pom.xml
├── reports/ # Auto-created on first run; CSV output lands here
└── src/
  └── main/
  └── java/
  └── com/
  └── zeroday/
  ├── Main.java # Entry point
  ├── engine/
  │ └── ThreatRanker.java # Sorting and filtering logic
  ├── export/
  │ ├── CsvExporter.java # CSV file writer
  │ └── DiscordAlerter.java # Discord webhook client
  ├── feed/
  │ └── NvdParser.java # NVD 2.0 API client + JSON parser
  └── model/
  └── ThreatEntry.java # CVE data model
---
## Prerequisites
- Java 17 or higher
- Apache Maven 3.8+
- Internet access (to reach services.nvd.nist.gov)
- A Discord server with a webhook URL (optional — only needed for alerts)
- A free NVD API key (optional — increases rate limit from 5 to 50 req/30s)
---
## Setup & Installation
**1. Clone or download the project**
git clone https://github.com/yourname/ZeroDayFeedParser.git
cd ZeroDayFeedParser
**2. Install dependencies via Maven**
mvn clean install
Maven will pull three dependencies automatically:
- jsoup 1.17.2 — HTML/HTTP utilities
- jackson-databind 2.16.1 — JSON parsing
- rome 1.18.0 — RSS feed support (reserved for future feed parsers)
**3. Set environment variables (see [Configuration](#configuration))**
**4. Run**
mvn compile exec:java -Dexec.mainClass="com.zeroday.Main"
---
## Configuration
ZeroDayFeedParser uses environment variables for all sensitive values. Never hardcode credentials in source files.
### NVD API Key (recommended, not required)
Without a key, the NVD API allows **5 requests per 30 seconds**. With a free key, this increases to **50 requests per 30 seconds**.
Register at: https://nvd.nist.gov/developers/request-an-api-key
\# Windows Command Prompt
set NVD\_API\_KEY=your-key-here
\# PowerShell
$env:NVD\_API\_KEY="your-key-here"
\# macOS / Linux
export NVD\_API\_KEY="your-key-here"
### Discord Webhook URL (optional)
If this variable is not set, the DiscordAlerter will log a warning and skip alerting gracefully — it will not crash.
\# Windows Command Prompt
set DISCORD\_WEBHOOK\_URL=https://discord.com/api/webhooks/your-webhook-here
\# PowerShell
$env:DISCORD\_WEBHOOK\_URL="https://discord.com/api/webhooks/your-webhook-here"
\# macOS / Linux
export DISCORD\_WEBHOOK\_URL="https://discord.com/api/webhooks/your-webhook-here"
---
## Running the Application
### Normal run
Fetches CVEs, ranks them, writes CSV, and sends Discord alerts for Critical entries.
mvn compile exec:java -Dexec.mainClass="com.zeroday.Main"
### Dry-run mode
Suppresses Discord alerts. Useful for testing, demos, and development.
mvn compile exec:java -Dexec.mainClass="com.zeroday.Main" -Dexec.args="--dry-run"
### Example console output
\[Main] Fetched 20 CVEs from NVD.
\[Main] 7 High/Critical CVEs after filtering.
\[CsvExporter] Wrote 7 entries to reports/zeroday-2025-04-19.csv
\--- High / Critical CVEs ---
\[2025-04-18] CVE-2025-21234 9.8 (Critical) apache/tomcat
\[2025-04-18] CVE-2025-20981 8.8 (High) microsoft/windows
\[2025-04-17] CVE-2025-19874 8.1 (High) linux/kernel
...
---
## Output
### CSV report
A new CSV file is written to reports/ on every run, named by date:
reports/zeroday-2025-04-19.csv
Columns:
| Column | Description |
|---|---|
| CVE | CVE identifier (e.g. CVE-2025-21234) |
| Severity | CVSS base score (0.0 – 10.0) |
| Label | Critical / High / Medium / Low |
| Product | Vendor/product from CPE data |
| Date | NVD published date |
| Patch Link | Direct link to the NVD detail page |
| Summary | English description of the vulnerability |
### Discord alert format
For every CVE with a severity of 9.0 or above, a message is posted to your configured Discord channel:
\*\*Zero-Day Alert!\*\* `CVE-2025-21234` scored \*\*9.8\*\* (Critical)
Product: apache/tomcat
Patch: https://nvd.nist.gov/vuln/detail/CVE-2025-21234
---
## Architecture Overview
NVD 2.0 REST API
  │
  ▼
  NvdParser.java — Fetches JSON, maps each vulnerability to a ThreatEntry
  │
  ▼
ThreatRanker.java — rank(): sorts by severity descending
  — filterHighAndAbove(): keeps CVSS ≥ 7.0
  │
  ├──────────────────────────────────────┐
  ▼ ▼
CsvExporter.java DiscordAlerter.java
Writes reports/zeroday-DATE.csv POSTs webhook for CVSS ≥ 9.0
The ThreatEntry model is the shared data object passed between all layers. It is intentionally a plain Java object (no framework annotations) to keep the model portable and testable.
---
## Class Reference
### Main.java
Entry point. Orchestrates the full pipeline: fetch → rank → filter → export → alert. Accepts --dry-run as a command-line argument to suppress Discord calls.
### model/ThreatEntry.java
Immutable data class representing a single CVE. Fields: id, product, summary, patchLink, severity (double), date (LocalDate).
### feed/NvdParser.java
HTTP client targeting the NVD 2.0 REST API (services.nvd.nist.gov/rest/json/cves/2.0). Parses the JSON response tree to extract CVE ID, English description, CVSS v3.1 score (falls back to v3.0), published date, and vendor/product from CPE match data. Reads the NVD\_API\_KEY environment variable and attaches it as a request header if present. Throws a descriptive RuntimeException on HTTP 403 (rate limit) instead of silently returning empty results.
### engine/ThreatRanker.java
Contains two methods deliberately kept separate:
- rank(List<ThreatEntry>) — sorts all threats by severity descending, no filtering. The caller controls what gets exported.
- filterHighAndAbove(List<ThreatEntry>) — returns only entries with CVSS ≥ 7.0.
- getSeverityLabel(double) — maps a score to Critical / High / Medium / Low.
### export/CsvExporter.java
Writes a UTF-8 CSV to reports/zeroday-DATE.csv. Creates the reports/ directory if it does not exist. Escapes embedded double-quotes in summaries per RFC 4180. Includes the severity label column alongside the raw score.
### export/DiscordAlerter.java
Sends a formatted Discord webhook message for each entry with CVSS ≥ 9.0. Reads the webhook URL from the DISCORD\_WEBHOOK\_URL environment variable. If the variable is unset or blank, it logs a notice and returns without throwing. Logs a warning to stderr if the webhook returns anything other than HTTP 204.
---
## NVD API Notes
### API version
This project uses the **NVD 2.0 REST API**. The legacy 1.1 JSON feeds (nvdcve-1.1-recent.json) were retired in December 2023 and return 404. Do not use them.
Live endpoint used by this project:
https://services.nvd.nist.gov/rest/json/cves/2.0?resultsPerPage=20
### Rate limits
| Scenario | Limit |
|---|---|
| No API key | 5 requests per 30 seconds |
| With free API key | 50 requests per 30 seconds |
If the limit is exceeded, the NVD returns HTTP 403. NvdParser catches this and throws a RuntimeException with a clear message rather than silently returning zero results.
### CVSS scoring
The parser tries CVSS v3.1 first, then falls back to v3.0. If neither is present (some older or disputed CVEs), severity defaults to 0.0 and the entry will be excluded by filterHighAndAbove.
### CPE product extraction
Vendor and product names are extracted from the first CPE match string in the configurations block. CPE format: cpe:2.3:a:vendor:product:version:.... This is best-effort — some CVEs have no CPE data and will show "Unknown".
---
## Discord Webhook Setup
1. Open your Discord server settings → Integrations → Webhooks
2. Click **New Webhook**, give it a name (e.g. "ZeroDay Alerts"), and select a channel
3. Click **Copy Webhook URL**
4. Set it as the DISCORD\_WEBHOOK\_URL environment variable (see [Configuration](#configuration))
The alerter will only post CVEs with CVSS ≥ 9.0 (Critical). High-severity CVEs (7.0–8.9) are exported to CSV only.
---
## Extending the Project
### Adding a second feed parser
Create a new class in com.zeroday.feed that returns List<ThreatEntry>. The CIRCL CVE API is a good option — it requires no authentication and returns recent CVEs in a clean JSON format:
https://cve.circl.lu/api/last/10
Then merge the results in Main.java before passing them to ThreatRanker:
List<ThreatEntry> all = new ArrayList<>();
all.addAll(nvdParser.fetchAndParse());
all.addAll(circlParser.fetchAndParse());
List<ThreatEntry> ranked = ranker.rank(all);### Changing the severity filter threshold
Edit the constant in ThreatRanker.filterHighAndAbove():
.filter(t -> t.getSeverity() >= 7.0) // change 7.0 to your desired cutoff### Changing the number of CVEs fetched
Edit the resultsPerPage query parameter in NvdParser:
private static final String NVD\_URL =
  "https://services.nvd.nist.gov/rest/json/cves/2.0?resultsPerPage=50";Maximum allowed by NVD is 2000 per request.
### Scheduling automatic runs
On Windows, use Task Scheduler to run the Maven command on a timed interval. On Linux/macOS, add a cron job:
\# Run every 6 hours
0 \*/6 \* \* \* cd /path/to/ZeroDayFeedParser \&\& mvn -q compile exec:java -Dexec.mainClass="com.zeroday.Main"
---
## Known Limitations
- **HackerOneParser is not implemented.** The class stub in the repository is non-functional. HackerOne does not have a free public reports endpoint. It has been excluded from the main pipeline.
- **No persistent deduplication.** Running the tool twice in one day will produce duplicate rows in the CSV and duplicate Discord alerts for the same CVEs. A simple solution is to track posted CVE IDs in a local file or SQLite database.
- **Product field is best-effort.** CPE data is not present on all CVEs and the extraction logic uses the first CPE match only. Complex multi-vendor CVEs may show only one vendor.
- **No retry logic.** A transient network failure will throw an exception rather than retrying. For production use, wrap the HTTP call in a simple retry loop with exponential backoff.
---
## Dependencies
| Dependency | Version | Purpose |
|---|---|---|
| org.jsoup:jsoup | 1.17.2 | HTTP utilities |
| com.fasterxml.jackson.core:jackson-databind | 2.16.1 | JSON parsing |
| com.rometools:rome | 1.18.0 | RSS feed support (reserved) |
All dependencies are declared in pom.xml and resolved automatically by Maven. Java's built-in java.net.http.HttpClient (Java 11+) is used for all HTTP calls — no additional HTTP client library is required.
---
## License
This project was developed as a cybersecurity capstone submission. All source code is original work by the author.