diff --git a/markdown/2024_insights/01_data_collection.md b/markdown/2024_insights/01_data_collection.md new file mode 100644 index 0000000..4d95ac1 --- /dev/null +++ b/markdown/2024_insights/01_data_collection.md @@ -0,0 +1,96 @@ +--- +jupyter: + jupytext: + text_representation: + extension: .md + format_name: markdown + format_version: '1.3' + jupytext_version: 1.16.6 + kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# 2024 Insights - Data Collection + +```python +import os +import requests +import zipfile +from pathlib import Path +``` + +## Project Setup + +Before proceeding with data collection, we need to ensure that the necessary directories for storing raw and processed data are in place. This step is crucial to maintain an organized structure for our project, especially when working with multiple datasets over time. + +The following Python code will check if the required directories exist (`raw` and `processed` under `2024_insights`), and if not, it will create them. This approach ensures that the environment is always correctly set up before any data processing begins, even if you're running this notebook on a new machine or a fresh clone of the repository. + + +```python +# Directories to create +dirs = [ + "../../data/2024_insights/raw/", + "../../data/2024_insights/processed/", +] + +# Create 2024 Insights data directories if they don't exist +for d in dirs: + os.makedirs(d, exist_ok=True) +``` + +# Data Collection + +To automate the downloading, unzipping, and saving of required datasets, execute the Python code in the **next cell**. + +This script will: +- Download the NIST NVD (2023 and 2024) and CISA KEV datasets. +- Extract JSON files from ZIP archives. +- Save all files to the directory: `/data/2024_insights/raw/`. + +Once the script has run successfully, proceed to the data preprocessing steps in the next notebook. + +```python +# Target directory for raw data +DATA_DIR = Path("../../data/2024_insights/raw") +DATA_DIR.mkdir(parents=True, exist_ok=True) + +# URLs for datasets +DATASETS = { + "nvdcve-1.1-2024.json.zip": "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2024.json.zip", + "nvdcve-1.1-2023.json.zip": "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2023.json.zip", + "known_exploited_vulnerabilities.csv": "https://www.cisa.gov/sites/default/files/csv/known_exploited_vulnerabilities.csv", +} + +def download_file(url, dest): + """Download a file from a URL to a destination.""" + print(f"Downloading: {url}") + response = requests.get(url, stream=True) + if response.status_code == 200: + with open(dest, "wb") as file: + for chunk in response.iter_content(chunk_size=1024): + file.write(chunk) + print(f"Saved to: {dest}") + else: + print(f"Failed to download {url} - Status code: {response.status_code}") + +def unzip_file(zip_path, dest_dir): + """Unzip a file to a destination directory.""" + with zipfile.ZipFile(zip_path, "r") as zip_ref: + zip_ref.extractall(dest_dir) + print(f"Unzipped {zip_path} to {dest_dir}") + +# Main execution +for filename, url in DATASETS.items(): + dest_path = DATA_DIR / filename + + # Download the file + download_file(url, dest_path) + + # If it's a ZIP file, extract its contents + if filename.endswith(".zip"): + unzip_file(dest_path, DATA_DIR) + dest_path.unlink() # Remove the ZIP file after extraction + +``` diff --git a/markdown/2024_insights/02_data_cleaning.md b/markdown/2024_insights/02_data_cleaning.md new file mode 100644 index 0000000..34c2d3a --- /dev/null +++ b/markdown/2024_insights/02_data_cleaning.md @@ -0,0 +1,206 @@ +--- +jupyter: + jupytext: + text_representation: + extension: .md + format_name: markdown + format_version: '1.3' + jupytext_version: 1.16.6 + kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Weekly CVE - Data Cleaning + +```python +import json + +import pandas as pd +``` + +## Script to Process NVD Data and Integrate CISA KEV Data + +This script processes NVD JSON data for a specified year and enriches it with CISA Known Exploited Vulnerabilities (KEV) data. + +### Features: +1. **Processes NVD Data**: + - Extracts key fields such as CVE ID, description, CWE, CVSS scores, vendor, product, and publication dates. + - Adds `CVE_Assigner` from the `CVE_data_meta.ASSIGNER` field. + +2. **Enriches with CISA KEV Data**: + - Merges with CISA KEV dataset to include information on exploited vulnerabilities. + - Identifies if a CVE is part of CISA KEV. + +3. **Generates Additional Fields**: + - Adds derived fields such as `Published_Year`, `Published_Month`, and `Published_YearMonth` for trend analysis. + +4. **Final Output**: + - Saves the processed data to a CSV file, ready for analysis and visualization. + +### Final Columns: +- **CVE_ID**: Unique identifier for the vulnerability. +- **Description**: English description of the CVE. +- **CWE**: Common Weakness Enumeration for the vulnerability. +- **CVSS_Base_Score**: CVSS v3 base score. +- **CVSS_Severity**: Severity rating (Critical, High, Medium, Low). +- **CVSS_Vector**: Attack vector for the CVE. +- **Exploitability_Score**: CVSS exploitability score. +- **Impact_Score**: CVSS impact score. +- **Vendor**: Vendor of the vulnerable product. +- **Product**: Vulnerable product name. +- **CVE_Assigner**: Organization or individual who assigned the CVE. +- **Published_Date**: Date the CVE was published. +- **Last_Modified_Date**: Date the CVE was last modified. +- **Published_Year**, **Published_Month**, **Published_YearMonth**: Derived fields for time-based analysis. +- **CISA_KEV**: Boolean indicating if the CVE is in the CISA KEV catalog. +- **KEV_DateAdded**: Date the CVE was added to CISA KEV. +- **KEV_Vendor**: Vendor information from CISA KEV. +- **KEV_Product**: Product information from CISA KEV. +- **KEV_ShortDescription**: Short description from CISA KEV. +- **KEV_KnownRansomware**: Ransomware association, if any. + + +```python +import json +import pandas as pd +from datetime import datetime + +def process_nvd_data(year): + # Define input & output paths + file_path = f'../../data/2024_insights/raw/nvdcve-1.1-{year}.json' + cisa_kev_path = '../../data/2024_insights/raw/known_exploited_vulnerabilities.csv' + output_path_template = '../../data/2024_insights/processed/nvd_data_{}.csv' + + # Load the NVD JSON data for the given year + with open(file_path, 'r') as file: + nvd_data = json.load(file) + + records = [] + for item in nvd_data['CVE_Items']: + cve = item['cve'] + cve_id = cve['CVE_data_meta']['ID'] + cve_assigner = cve.get('CVE_data_meta', {}).get('ASSIGNER', None) + + # Extract English description (if any) + description = next( + (desc['value'] for desc in cve['description']['description_data'] if desc['lang'] == 'en'), + 'No description available' + ) + + # Extract CWE + cwe = next(( + desc['value'] + for problem in cve['problemtype']['problemtype_data'] + for desc in problem['description'] + if desc['lang'] == 'en' + ), None) + + # Extract CVSS v3 details + impact_data = item.get('impact', {}) + base_metric_v3 = impact_data.get('baseMetricV3', {}) + cvss_data = base_metric_v3.get('cvssV3', {}) + + cvss_base_score = cvss_data.get('baseScore') + cvss_severity = cvss_data.get('baseSeverity', 'UNKNOWN').upper() + cvss_vector = cvss_data.get('vectorString') + exploitability_score = base_metric_v3.get('exploitabilityScore') + impact_score = base_metric_v3.get('impactScore') + + # Parse published & modified dates + published_date_str = item.get('publishedDate') + last_modified_date_str = item.get('lastModifiedDate') + published_date = pd.to_datetime(published_date_str, errors='coerce') or pd.NaT + last_modified_date = pd.to_datetime(last_modified_date_str, errors='coerce') or pd.NaT + + # Create derived date fields + published_year = published_date.year if pd.notnull(published_date) else None + published_month = published_date.month if pd.notnull(published_date) else None + published_ym = published_date.tz_localize(None).to_period('M') if pd.notnull(published_date) else None + + # Build records for each vendor-product pair + cpe_found = False + for node in item.get('configurations', {}).get('nodes', []): + for cpe_match in node.get('cpe_match', []): + if cpe_match.get('vulnerable'): + cpe_parts = cpe_match['cpe23Uri'].split(':') + vendor = cpe_parts[3].title() if len(cpe_parts) > 3 else "Unknown Vendor" + product = cpe_parts[4].title() if len(cpe_parts) > 4 else "Unknown Product" + + records.append([ + cve_id, description, cwe, cvss_base_score, cvss_severity, + cvss_vector, exploitability_score, impact_score, vendor, + product, cve_assigner, published_date_str, last_modified_date_str, + published_date, last_modified_date, published_year, + published_month, published_ym + ]) + cpe_found = True + + if not cpe_found: + records.append([ + cve_id, description, cwe, cvss_base_score, cvss_severity, + cvss_vector, exploitability_score, impact_score, None, + None, cve_assigner, published_date_str, last_modified_date_str, + published_date, last_modified_date, published_year, + published_month, published_ym + ]) + + # Convert to DataFrame + columns = [ + 'CVE_ID', 'Description', 'CWE', 'CVSS_Base_Score', 'CVSS_Severity', + 'CVSS_Vector', 'Exploitability_Score', 'Impact_Score', 'Vendor', + 'Product', 'CVE_Assigner', 'Published_Date_Str', 'Last_Modified_Date_Str', + 'Published_Date', 'Last_Modified_Date', 'Published_Year', + 'Published_Month', 'Published_YearMonth' + ] + df = pd.DataFrame(records, columns=columns) + + # Merge with the CISA KEV data + kev_df = pd.read_csv(cisa_kev_path, parse_dates=['dateAdded']) + kev_df.rename( + columns={ + 'cveID': 'CVE_ID', + 'dateAdded': 'KEV_DateAdded', + 'vendorProject': 'KEV_Vendor', + 'product': 'KEV_Product', + 'shortDescription': 'KEV_ShortDescription', + 'knownRansomwareCampaignUse': 'KEV_KnownRansomware' + }, + inplace=True + ) + kev_df['KEV_KnownRansomware'] = kev_df['KEV_KnownRansomware'].fillna("Unknown").str.capitalize() + kev_df['KEV_Notes'] = kev_df['notes'].str.split(';').str[0].str.strip() + + df = df.merge( + kev_df[['CVE_ID', 'KEV_DateAdded', 'KEV_Vendor', 'KEV_Product', 'KEV_ShortDescription', 'KEV_KnownRansomware']], + on='CVE_ID', + how='left' + ) + df['CISA_KEV'] = df['KEV_DateAdded'].notna() + + # Define final column order + final_columns = [ + 'CVE_ID', 'Description', 'CWE', 'CVSS_Base_Score', 'CVSS_Severity', + 'CVSS_Vector', 'Exploitability_Score', 'Impact_Score', 'Vendor', + 'Product', 'CVE_Assigner', 'Published_Date', 'Last_Modified_Date', + 'Published_Year', 'Published_Month', 'Published_YearMonth', + 'CISA_KEV', 'KEV_DateAdded', 'KEV_Vendor', 'KEV_Product', + 'KEV_ShortDescription', 'KEV_KnownRansomware' + ] + df = df[final_columns] + + # Save processed data to CSV + output_path = output_path_template.format(year) + df.to_csv(output_path, index=False, encoding='utf-8') + + return df + +# Process NVD data for 2023 +df_2023 = process_nvd_data(2023) + +# Process NVD data for 2024 +df_2024 = process_nvd_data(2024) +df_2024.head() + +``` diff --git a/notebooks/2024_insights/01_data_collection.ipynb b/notebooks/2024_insights/01_data_collection.ipynb new file mode 100644 index 0000000..42e529b --- /dev/null +++ b/notebooks/2024_insights/01_data_collection.ipynb @@ -0,0 +1,170 @@ +{ + "cells": [ + { + "metadata": {}, + "cell_type": "markdown", + "source": "# 2024 Insights - Data Collection", + "id": "57b272f469640885" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-28T11:15:04.233557Z", + "start_time": "2024-12-28T11:15:04.228830Z" + } + }, + "cell_type": "code", + "source": [ + "import os\n", + "import requests\n", + "import zipfile\n", + "from pathlib import Path" + ], + "id": "f0ea410ba01c8838", + "outputs": [], + "execution_count": 10 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Project Setup\n", + "\n", + "Before proceeding with data collection, we need to ensure that the necessary directories for storing raw and processed data are in place. This step is crucial to maintain an organized structure for our project, especially when working with multiple datasets over time.\n", + "\n", + "The following Python code will check if the required directories exist (`raw` and `processed` under `2024_insights`), and if not, it will create them. This approach ensures that the environment is always correctly set up before any data processing begins, even if you're running this notebook on a new machine or a fresh clone of the repository.\n" + ], + "id": "f67300782f9b0953" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-28T11:15:04.256638Z", + "start_time": "2024-12-28T11:15:04.251514Z" + } + }, + "cell_type": "code", + "source": [ + "# Directories to create\n", + "dirs = [\n", + " \"../../data/2024_insights/raw/\",\n", + " \"../../data/2024_insights/processed/\",\n", + "]\n", + "\n", + "# Create 2024 Insights data directories if they don't exist\n", + "for d in dirs:\n", + " os.makedirs(d, exist_ok=True)" + ], + "id": "99e5bc4542e6d1d7", + "outputs": [], + "execution_count": 11 + }, + { + "cell_type": "markdown", + "id": "8656d56c321c5758", + "metadata": {}, + "source": [ + "# Data Collection\n", + "\n", + "To automate the downloading, unzipping, and saving of required datasets, execute the Python code in the **next cell**.\n", + "\n", + "This script will:\n", + "- Download the NIST NVD (2023 and 2024) and CISA KEV datasets.\n", + "- Extract JSON files from ZIP archives.\n", + "- Save all files to the directory: `/data/2024_insights/raw/`.\n", + "\n", + "Once the script has run successfully, proceed to the data preprocessing steps in the next notebook." + ] + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-28T11:15:07.053129Z", + "start_time": "2024-12-28T11:15:04.282886Z" + } + }, + "cell_type": "code", + "source": [ + "# Target directory for raw data\n", + "DATA_DIR = Path(\"../../data/2024_insights/raw\")\n", + "DATA_DIR.mkdir(parents=True, exist_ok=True)\n", + "\n", + "# URLs for datasets\n", + "DATASETS = {\n", + " \"nvdcve-1.1-2024.json.zip\": \"https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2024.json.zip\",\n", + " \"nvdcve-1.1-2023.json.zip\": \"https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2023.json.zip\",\n", + " \"known_exploited_vulnerabilities.csv\": \"https://www.cisa.gov/sites/default/files/csv/known_exploited_vulnerabilities.csv\",\n", + "}\n", + "\n", + "def download_file(url, dest):\n", + " \"\"\"Download a file from a URL to a destination.\"\"\"\n", + " print(f\"Downloading: {url}\")\n", + " response = requests.get(url, stream=True)\n", + " if response.status_code == 200:\n", + " with open(dest, \"wb\") as file:\n", + " for chunk in response.iter_content(chunk_size=1024):\n", + " file.write(chunk)\n", + " print(f\"Saved to: {dest}\")\n", + " else:\n", + " print(f\"Failed to download {url} - Status code: {response.status_code}\")\n", + "\n", + "def unzip_file(zip_path, dest_dir):\n", + " \"\"\"Unzip a file to a destination directory.\"\"\"\n", + " with zipfile.ZipFile(zip_path, \"r\") as zip_ref:\n", + " zip_ref.extractall(dest_dir)\n", + " print(f\"Unzipped {zip_path} to {dest_dir}\")\n", + "\n", + "# Main execution\n", + "for filename, url in DATASETS.items():\n", + " dest_path = DATA_DIR / filename\n", + "\n", + " # Download the file\n", + " download_file(url, dest_path)\n", + "\n", + " # If it's a ZIP file, extract its contents\n", + " if filename.endswith(\".zip\"):\n", + " unzip_file(dest_path, DATA_DIR)\n", + " dest_path.unlink() # Remove the ZIP file after extraction\n" + ], + "id": "eb1fbb84fe88fb50", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Downloading: https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2024.json.zip\n", + "Saved to: ../../data/2024_insights/raw/nvdcve-1.1-2024.json.zip\n", + "Unzipped ../../data/2024_insights/raw/nvdcve-1.1-2024.json.zip to ../../data/2024_insights/raw\n", + "Downloading: https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2023.json.zip\n", + "Saved to: ../../data/2024_insights/raw/nvdcve-1.1-2023.json.zip\n", + "Unzipped ../../data/2024_insights/raw/nvdcve-1.1-2023.json.zip to ../../data/2024_insights/raw\n", + "Downloading: https://www.cisa.gov/sites/default/files/csv/known_exploited_vulnerabilities.csv\n", + "Saved to: ../../data/2024_insights/raw/known_exploited_vulnerabilities.csv\n" + ] + } + ], + "execution_count": 12 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/2024_insights/02_data_cleaning.ipynb b/notebooks/2024_insights/02_data_cleaning.ipynb new file mode 100644 index 0000000..d62eecb --- /dev/null +++ b/notebooks/2024_insights/02_data_cleaning.ipynb @@ -0,0 +1,469 @@ +{ + "cells": [ + { + "metadata": {}, + "cell_type": "markdown", + "source": "# Weekly CVE - Data Cleaning", + "id": "1b921cb1fb53aa42" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-28T11:13:10.299842Z", + "start_time": "2024-12-28T11:13:10.294992Z" + } + }, + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "import pandas as pd" + ], + "id": "871cf814b4861404", + "outputs": [], + "execution_count": 16 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Script to Process NVD Data and Integrate CISA KEV Data\n", + "\n", + "This script processes NVD JSON data for a specified year and enriches it with CISA Known Exploited Vulnerabilities (KEV) data.\n", + "\n", + "### Features:\n", + "1. **Processes NVD Data**:\n", + " - Extracts key fields such as CVE ID, description, CWE, CVSS scores, vendor, product, and publication dates.\n", + " - Adds `CVE_Assigner` from the `CVE_data_meta.ASSIGNER` field.\n", + "\n", + "2. **Enriches with CISA KEV Data**:\n", + " - Merges with CISA KEV dataset to include information on exploited vulnerabilities.\n", + " - Identifies if a CVE is part of CISA KEV.\n", + "\n", + "3. **Generates Additional Fields**:\n", + " - Adds derived fields such as `Published_Year`, `Published_Month`, and `Published_YearMonth` for trend analysis.\n", + "\n", + "4. **Final Output**:\n", + " - Saves the processed data to a CSV file, ready for analysis and visualization.\n", + "\n", + "### Final Columns:\n", + "- **CVE_ID**: Unique identifier for the vulnerability.\n", + "- **Description**: English description of the CVE.\n", + "- **CWE**: Common Weakness Enumeration for the vulnerability.\n", + "- **CVSS_Base_Score**: CVSS v3 base score.\n", + "- **CVSS_Severity**: Severity rating (Critical, High, Medium, Low).\n", + "- **CVSS_Vector**: Attack vector for the CVE.\n", + "- **Exploitability_Score**: CVSS exploitability score.\n", + "- **Impact_Score**: CVSS impact score.\n", + "- **Vendor**: Vendor of the vulnerable product.\n", + "- **Product**: Vulnerable product name.\n", + "- **CVE_Assigner**: Organization or individual who assigned the CVE.\n", + "- **Published_Date**: Date the CVE was published.\n", + "- **Last_Modified_Date**: Date the CVE was last modified.\n", + "- **Published_Year**, **Published_Month**, **Published_YearMonth**: Derived fields for time-based analysis.\n", + "- **CISA_KEV**: Boolean indicating if the CVE is in the CISA KEV catalog.\n", + "- **KEV_DateAdded**: Date the CVE was added to CISA KEV.\n", + "- **KEV_Vendor**: Vendor information from CISA KEV.\n", + "- **KEV_Product**: Product information from CISA KEV.\n", + "- **KEV_ShortDescription**: Short description from CISA KEV.\n", + "- **KEV_KnownRansomware**: Ransomware association, if any.\n" + ], + "id": "93ed0cf85de8d85d" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-28T11:14:42.688653Z", + "start_time": "2024-12-28T11:13:10.419926Z" + } + }, + "cell_type": "code", + "source": [ + "import json\n", + "import pandas as pd\n", + "from datetime import datetime\n", + "\n", + "def process_nvd_data(year):\n", + " # Define input & output paths\n", + " file_path = f'../../data/2024_insights/raw/nvdcve-1.1-{year}.json'\n", + " cisa_kev_path = '../../data/2024_insights/raw/known_exploited_vulnerabilities.csv'\n", + " output_path_template = '../../data/2024_insights/processed/nvd_data_{}.csv'\n", + "\n", + " # Load the NVD JSON data for the given year\n", + " with open(file_path, 'r') as file:\n", + " nvd_data = json.load(file)\n", + "\n", + " records = []\n", + " for item in nvd_data['CVE_Items']:\n", + " cve = item['cve']\n", + " cve_id = cve['CVE_data_meta']['ID']\n", + " cve_assigner = cve.get('CVE_data_meta', {}).get('ASSIGNER', None)\n", + "\n", + " # Extract English description (if any)\n", + " description = next(\n", + " (desc['value'] for desc in cve['description']['description_data'] if desc['lang'] == 'en'),\n", + " 'No description available'\n", + " )\n", + "\n", + " # Extract CWE\n", + " cwe = next((\n", + " desc['value']\n", + " for problem in cve['problemtype']['problemtype_data']\n", + " for desc in problem['description']\n", + " if desc['lang'] == 'en'\n", + " ), None)\n", + "\n", + " # Extract CVSS v3 details\n", + " impact_data = item.get('impact', {})\n", + " base_metric_v3 = impact_data.get('baseMetricV3', {})\n", + " cvss_data = base_metric_v3.get('cvssV3', {})\n", + "\n", + " cvss_base_score = cvss_data.get('baseScore')\n", + " cvss_severity = cvss_data.get('baseSeverity', 'UNKNOWN').upper()\n", + " cvss_vector = cvss_data.get('vectorString')\n", + " exploitability_score = base_metric_v3.get('exploitabilityScore')\n", + " impact_score = base_metric_v3.get('impactScore')\n", + "\n", + " # Parse published & modified dates\n", + " published_date_str = item.get('publishedDate')\n", + " last_modified_date_str = item.get('lastModifiedDate')\n", + " published_date = pd.to_datetime(published_date_str, errors='coerce') or pd.NaT\n", + " last_modified_date = pd.to_datetime(last_modified_date_str, errors='coerce') or pd.NaT\n", + "\n", + " # Create derived date fields\n", + " published_year = published_date.year if pd.notnull(published_date) else None\n", + " published_month = published_date.month if pd.notnull(published_date) else None\n", + " published_ym = published_date.tz_localize(None).to_period('M') if pd.notnull(published_date) else None\n", + "\n", + " # Build records for each vendor-product pair\n", + " cpe_found = False\n", + " for node in item.get('configurations', {}).get('nodes', []):\n", + " for cpe_match in node.get('cpe_match', []):\n", + " if cpe_match.get('vulnerable'):\n", + " cpe_parts = cpe_match['cpe23Uri'].split(':')\n", + " vendor = cpe_parts[3].title() if len(cpe_parts) > 3 else \"Unknown Vendor\"\n", + " product = cpe_parts[4].title() if len(cpe_parts) > 4 else \"Unknown Product\"\n", + "\n", + " records.append([\n", + " cve_id, description, cwe, cvss_base_score, cvss_severity,\n", + " cvss_vector, exploitability_score, impact_score, vendor,\n", + " product, cve_assigner, published_date_str, last_modified_date_str,\n", + " published_date, last_modified_date, published_year,\n", + " published_month, published_ym\n", + " ])\n", + " cpe_found = True\n", + "\n", + " if not cpe_found:\n", + " records.append([\n", + " cve_id, description, cwe, cvss_base_score, cvss_severity,\n", + " cvss_vector, exploitability_score, impact_score, None,\n", + " None, cve_assigner, published_date_str, last_modified_date_str,\n", + " published_date, last_modified_date, published_year,\n", + " published_month, published_ym\n", + " ])\n", + "\n", + " # Convert to DataFrame\n", + " columns = [\n", + " 'CVE_ID', 'Description', 'CWE', 'CVSS_Base_Score', 'CVSS_Severity',\n", + " 'CVSS_Vector', 'Exploitability_Score', 'Impact_Score', 'Vendor',\n", + " 'Product', 'CVE_Assigner', 'Published_Date_Str', 'Last_Modified_Date_Str',\n", + " 'Published_Date', 'Last_Modified_Date', 'Published_Year',\n", + " 'Published_Month', 'Published_YearMonth'\n", + " ]\n", + " df = pd.DataFrame(records, columns=columns)\n", + "\n", + " # Merge with the CISA KEV data\n", + " kev_df = pd.read_csv(cisa_kev_path, parse_dates=['dateAdded'])\n", + " kev_df.rename(\n", + " columns={\n", + " 'cveID': 'CVE_ID',\n", + " 'dateAdded': 'KEV_DateAdded',\n", + " 'vendorProject': 'KEV_Vendor',\n", + " 'product': 'KEV_Product',\n", + " 'shortDescription': 'KEV_ShortDescription',\n", + " 'knownRansomwareCampaignUse': 'KEV_KnownRansomware'\n", + " },\n", + " inplace=True\n", + " )\n", + " kev_df['KEV_KnownRansomware'] = kev_df['KEV_KnownRansomware'].fillna(\"Unknown\").str.capitalize()\n", + " kev_df['KEV_Notes'] = kev_df['notes'].str.split(';').str[0].str.strip()\n", + "\n", + " df = df.merge(\n", + " kev_df[['CVE_ID', 'KEV_DateAdded', 'KEV_Vendor', 'KEV_Product', 'KEV_ShortDescription', 'KEV_KnownRansomware']],\n", + " on='CVE_ID',\n", + " how='left'\n", + " )\n", + " df['CISA_KEV'] = df['KEV_DateAdded'].notna()\n", + "\n", + " # Define final column order\n", + " final_columns = [\n", + " 'CVE_ID', 'Description', 'CWE', 'CVSS_Base_Score', 'CVSS_Severity',\n", + " 'CVSS_Vector', 'Exploitability_Score', 'Impact_Score', 'Vendor',\n", + " 'Product', 'CVE_Assigner', 'Published_Date', 'Last_Modified_Date',\n", + " 'Published_Year', 'Published_Month', 'Published_YearMonth',\n", + " 'CISA_KEV', 'KEV_DateAdded', 'KEV_Vendor', 'KEV_Product',\n", + " 'KEV_ShortDescription', 'KEV_KnownRansomware'\n", + " ]\n", + " df = df[final_columns]\n", + "\n", + " # Save processed data to CSV\n", + " output_path = output_path_template.format(year)\n", + " df.to_csv(output_path, index=False, encoding='utf-8')\n", + "\n", + " return df\n", + "\n", + "# Process NVD data for 2023\n", + "df_2023 = process_nvd_data(2023)\n", + "\n", + "# Process NVD data for 2024\n", + "df_2024 = process_nvd_data(2024)\n", + "df_2024.head()\n" + ], + "id": "b0607cd68a5fb696", + "outputs": [ + { + "data": { + "text/plain": [ + " CVE_ID Description \\\n", + "0 CVE-2024-0001 A condition exists in FlashArray Purity whereb... \n", + "1 CVE-2024-0001 A condition exists in FlashArray Purity whereb... \n", + "2 CVE-2024-0002 A condition exists in FlashArray Purity whereb... \n", + "3 CVE-2024-0002 A condition exists in FlashArray Purity whereb... \n", + "4 CVE-2024-0002 A condition exists in FlashArray Purity whereb... \n", + "\n", + " CWE CVSS_Base_Score CVSS_Severity \\\n", + "0 CWE-1188 9.8 CRITICAL \n", + "1 CWE-1188 9.8 CRITICAL \n", + "2 NVD-CWE-noinfo 9.8 CRITICAL \n", + "3 NVD-CWE-noinfo 9.8 CRITICAL \n", + "4 NVD-CWE-noinfo 9.8 CRITICAL \n", + "\n", + " CVSS_Vector Exploitability_Score \\\n", + "0 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H 3.9 \n", + "1 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H 3.9 \n", + "2 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H 3.9 \n", + "3 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H 3.9 \n", + "4 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H 3.9 \n", + "\n", + " Impact_Score Vendor Product ... Last_Modified_Date \\\n", + "0 5.9 Purestorage Purity\\/\\/Fa ... 2024-09-27 14:08:00+00:00 \n", + "1 5.9 Purestorage Purity\\/\\/Fa ... 2024-09-27 14:08:00+00:00 \n", + "2 5.9 Purestorage Purity\\/\\/Fa ... 2024-09-27 14:13:00+00:00 \n", + "3 5.9 Purestorage Purity\\/\\/Fa ... 2024-09-27 14:13:00+00:00 \n", + "4 5.9 Purestorage Purity\\/\\/Fa ... 2024-09-27 14:13:00+00:00 \n", + "\n", + " Published_Year Published_Month Published_YearMonth CISA_KEV KEV_DateAdded \\\n", + "0 2024 9 2024-09 False NaT \n", + "1 2024 9 2024-09 False NaT \n", + "2 2024 9 2024-09 False NaT \n", + "3 2024 9 2024-09 False NaT \n", + "4 2024 9 2024-09 False NaT \n", + "\n", + " KEV_Vendor KEV_Product KEV_ShortDescription KEV_KnownRansomware \n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "\n", + "[5 rows x 22 columns]" + ], + "text/html": [ + "
\n", + " | CVE_ID | \n", + "Description | \n", + "CWE | \n", + "CVSS_Base_Score | \n", + "CVSS_Severity | \n", + "CVSS_Vector | \n", + "Exploitability_Score | \n", + "Impact_Score | \n", + "Vendor | \n", + "Product | \n", + "... | \n", + "Last_Modified_Date | \n", + "Published_Year | \n", + "Published_Month | \n", + "Published_YearMonth | \n", + "CISA_KEV | \n", + "KEV_DateAdded | \n", + "KEV_Vendor | \n", + "KEV_Product | \n", + "KEV_ShortDescription | \n", + "KEV_KnownRansomware | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "CVE-2024-0001 | \n", + "A condition exists in FlashArray Purity whereb... | \n", + "CWE-1188 | \n", + "9.8 | \n", + "CRITICAL | \n", + "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | \n", + "3.9 | \n", + "5.9 | \n", + "Purestorage | \n", + "Purity\\/\\/Fa | \n", + "... | \n", + "2024-09-27 14:08:00+00:00 | \n", + "2024 | \n", + "9 | \n", + "2024-09 | \n", + "False | \n", + "NaT | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "
1 | \n", + "CVE-2024-0001 | \n", + "A condition exists in FlashArray Purity whereb... | \n", + "CWE-1188 | \n", + "9.8 | \n", + "CRITICAL | \n", + "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | \n", + "3.9 | \n", + "5.9 | \n", + "Purestorage | \n", + "Purity\\/\\/Fa | \n", + "... | \n", + "2024-09-27 14:08:00+00:00 | \n", + "2024 | \n", + "9 | \n", + "2024-09 | \n", + "False | \n", + "NaT | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "
2 | \n", + "CVE-2024-0002 | \n", + "A condition exists in FlashArray Purity whereb... | \n", + "NVD-CWE-noinfo | \n", + "9.8 | \n", + "CRITICAL | \n", + "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | \n", + "3.9 | \n", + "5.9 | \n", + "Purestorage | \n", + "Purity\\/\\/Fa | \n", + "... | \n", + "2024-09-27 14:13:00+00:00 | \n", + "2024 | \n", + "9 | \n", + "2024-09 | \n", + "False | \n", + "NaT | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "
3 | \n", + "CVE-2024-0002 | \n", + "A condition exists in FlashArray Purity whereb... | \n", + "NVD-CWE-noinfo | \n", + "9.8 | \n", + "CRITICAL | \n", + "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | \n", + "3.9 | \n", + "5.9 | \n", + "Purestorage | \n", + "Purity\\/\\/Fa | \n", + "... | \n", + "2024-09-27 14:13:00+00:00 | \n", + "2024 | \n", + "9 | \n", + "2024-09 | \n", + "False | \n", + "NaT | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "
4 | \n", + "CVE-2024-0002 | \n", + "A condition exists in FlashArray Purity whereb... | \n", + "NVD-CWE-noinfo | \n", + "9.8 | \n", + "CRITICAL | \n", + "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | \n", + "3.9 | \n", + "5.9 | \n", + "Purestorage | \n", + "Purity\\/\\/Fa | \n", + "... | \n", + "2024-09-27 14:13:00+00:00 | \n", + "2024 | \n", + "9 | \n", + "2024-09 | \n", + "False | \n", + "NaT | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "NaN | \n", + "
5 rows × 22 columns
\n", + "