From 2db56b7e6bcc28858c4984c0f44eef421fc92c78 Mon Sep 17 00:00:00 2001 From: tarun111111 Date: Tue, 17 Feb 2026 22:08:59 +0530 Subject: [PATCH] Add KDE security advisory importer - Create KdeImporter class to fetch and parse KDE security advisories - Support both old PGP-signed format and new plain text format - Extract CVE IDs (including old CAN- format conversion) - Parse advisory titles/summaries and references - Add tests for both advisory formats Fixes #1939 Co-Authored-By: Claude Signed-off-by: tarun111111 --- vulnerabilities/importers/kde.py | 189 ++++++++++++++++++ .../test_data/kde/advisory-20030916-1.txt | 50 +++++ .../test_data/kde/advisory-20260109-1.txt | 31 +++ vulnerabilities/tests/test_kde.py | 78 ++++++++ 4 files changed, 348 insertions(+) create mode 100644 vulnerabilities/importers/kde.py create mode 100644 vulnerabilities/tests/test_data/kde/advisory-20030916-1.txt create mode 100644 vulnerabilities/tests/test_data/kde/advisory-20260109-1.txt create mode 100644 vulnerabilities/tests/test_kde.py diff --git a/vulnerabilities/importers/kde.py b/vulnerabilities/importers/kde.py new file mode 100644 index 000000000..eb602a87a --- /dev/null +++ b/vulnerabilities/importers/kde.py @@ -0,0 +1,189 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import logging +import re +from typing import Iterable + +import requests +from bs4 import BeautifulSoup + +from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import Importer +from vulnerabilities.importer import Reference + +logger = logging.getLogger(__name__) + + +class KdeImporter(Importer): + spdx_license_expression = "LGPL-2.0-or-later" + license_url = "https://kde.org/community/whatiskde/licensing/" + importer_name = "KDE Importer" + url = "https://kde.org/info/security/" + + def advisory_data(self) -> Iterable[AdvisoryData]: + advisory_urls = fetch_advisory_urls(self.url) + for advisory_url in advisory_urls: + try: + advisory_text = fetch_advisory_text(advisory_url) + advisory = parse_advisory(advisory_text, advisory_url) + if advisory: + yield advisory + except Exception as e: + logger.error(f"Error parsing advisory {advisory_url}: {e}") + + +def fetch_advisory_urls(index_url): + """ + Fetch all advisory URLs from the KDE security index page. + + Returns: + List of full advisory URLs + """ + response = requests.get(index_url) + if response.status_code != 200: + logger.error(f"Failed to fetch {index_url}") + return [] + + soup = BeautifulSoup(response.content, "html.parser") + advisory_urls = [] + + # Find all links in the page + for link in soup.find_all("a"): + href = link.get("href", "") + # Advisory files end with .txt and start with advisory- or contain xpdf + if href.endswith(".txt") and ("advisory-" in href or "xpdf" in href): + # Convert relative URL to absolute + if href.startswith("./"): + href = href[2:] + full_url = f"https://kde.org/info/security/{href}" + advisory_urls.append(full_url) + + return advisory_urls + + +def fetch_advisory_text(advisory_url): + """ + Fetch the text content of a KDE security advisory. + + Returns: + Advisory text as string + """ + response = requests.get(advisory_url) + if response.status_code != 200: + logger.error(f"Failed to fetch {advisory_url}") + return None + + return response.text + + +def parse_advisory(advisory_text, advisory_url): + """ + Parse a KDE security advisory and extract relevant information. + + Args: + advisory_text: The full text content of the advisory + advisory_url: URL of the advisory + + Returns: + AdvisoryData object or None + """ + if not advisory_text: + return None + + # Extract CVE IDs (both CVE and old CAN format) + cve_pattern = r'C(?:VE|AN)-\d{4}-\d{4,7}' + cve_ids = re.findall(cve_pattern, advisory_text) + + # Convert old CAN format to CVE format + aliases = [] + for cve_id in cve_ids: + if cve_id.startswith("CAN-"): + # CAN is old format, convert to CVE + cve_id = cve_id.replace("CAN-", "CVE-") + if cve_id not in aliases: + aliases.append(cve_id) + + # Extract summary/title + summary = extract_summary(advisory_text) + + # Extract references + references = extract_references(advisory_text, advisory_url, aliases) + + # Only create advisory if we have CVE IDs or meaningful content + if not aliases and not summary: + logger.warning(f"No CVE IDs or summary found in {advisory_url}") + return None + + return AdvisoryData( + aliases=aliases, + summary=summary, + references=references, + url=advisory_url, + ) + + +def extract_summary(advisory_text): + """ + Extract the summary/title from the advisory text. + """ + lines = advisory_text.split("\n") + + # Try to find Title: field (new format) + for i, line in enumerate(lines): + if line.startswith("Title:"): + return line.replace("Title:", "").strip() + + # Try to find KDE Security Advisory: line (old format) + for line in lines: + if "KDE Security Advisory:" in line: + return line.replace("KDE Security Advisory:", "").strip() + + # Try to find first non-empty line after PGP header + skip_pgp = False + for line in lines: + if line.startswith("-----BEGIN PGP"): + skip_pgp = True + continue + if skip_pgp and line.strip() and not line.startswith("Hash:"): + skip_pgp = False + continue + if not skip_pgp and line.strip() and not line.startswith("---"): + # Return first meaningful line as summary + return line.strip() + + return "" + + +def extract_references(advisory_text, advisory_url, aliases): + """ + Extract reference URLs from the advisory text. + """ + references = [] + + # Add the advisory itself as a reference + references.append(Reference(url=advisory_url)) + + # Add CVE references + for cve_id in aliases: + cve_url = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}" + references.append(Reference(url=cve_url, reference_id=cve_id)) + + # Extract URLs from text + url_pattern = r'https?://[^\s<>"\')]+[^\s<>"\')\.]' + urls = re.findall(url_pattern, advisory_text) + + for url in urls: + # Skip if already added + if any(ref.url == url for ref in references): + continue + # Add unique URLs + references.append(Reference(url=url)) + + return references diff --git a/vulnerabilities/tests/test_data/kde/advisory-20030916-1.txt b/vulnerabilities/tests/test_data/kde/advisory-20030916-1.txt new file mode 100644 index 000000000..bfe5e5516 --- /dev/null +++ b/vulnerabilities/tests/test_data/kde/advisory-20030916-1.txt @@ -0,0 +1,50 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + + + +KDE Security Advisory: KDM vulnerabilities +Original Release Date: 2003-09-16 +URL: http://www.kde.org/info/security/advisory-20030916-1.txt + +0. References + http://cert.uni-stuttgart.de/archive/suse/security/2002/12/msg00101.html + http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0690 + http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0692 + + +1. Systems affected: + + All versions of KDM as distributed with KDE up to and including + KDE 3.1.3. + + +2. Overview: + + Two issues have been discovered in KDM: + + a) CAN-2003-0690: + Privilege escalation with specific PAM modules + b) CAN-2003-0692: + Session cookies generated by KDM are potentially insecure + + +3. Impact: + + If KDM is used in combination with the MIT pam_krb5 module and given + a valid username and password of an existing user, the login attempt + succeeds and establishes a session with excessive privileges. + + +4. Solution: + + Users of KDE 3.1.x are advised to upgrade to KDE 3.1.4. + + +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.2.2-rc1-SuSE (GNU/Linux) + +iD8DBQE/Z1fBvsXr+iuy1UoRAi02AJ90TqHxCeqzqGJrN3jS7mRSd9u5xQCg6/Do +LB3tubiwfy8TUy5rL7B8UFY= +=2tbX +-----END PGP SIGNATURE----- diff --git a/vulnerabilities/tests/test_data/kde/advisory-20260109-1.txt b/vulnerabilities/tests/test_data/kde/advisory-20260109-1.txt new file mode 100644 index 000000000..3c172773a --- /dev/null +++ b/vulnerabilities/tests/test_data/kde/advisory-20260109-1.txt @@ -0,0 +1,31 @@ +KDE Project Security Advisory +============================= + +Title: Smb4K: Major security issues in KAuth mount helper +Risk rating: Major +CVE: CVE-2025-66002, CVE-2025-66003 +Versions: Smb4K < 4.0.5 +Date: 9 January 2026 + +Overview +======== + +The privileged KAuth mount helper of Smb4K runs with full root privileges and implements two KAuth actions accessible via D-Bus: mounting and unmounting a network share. + + +Impact +====== + +An attacker can exploit the shortcomings in the KAuth mount helper and perform arbitrary unmounts. + + +Solution +======== + +Update Smb4K to version 4.0.5 or later. + + +Credits +======= + +Thanks to Matthias Gerstner and the SUSE security team for reporting this issue. diff --git a/vulnerabilities/tests/test_kde.py b/vulnerabilities/tests/test_kde.py new file mode 100644 index 000000000..1cdc4fd75 --- /dev/null +++ b/vulnerabilities/tests/test_kde.py @@ -0,0 +1,78 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import os +from unittest import TestCase + +from vulnerabilities.importers.kde import extract_summary +from vulnerabilities.importers.kde import parse_advisory + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA = os.path.join(BASE_DIR, "test_data/kde") + + +class TestKdeImporter(TestCase): + def test_parse_old_format_advisory(self): + """Test parsing old format PGP-signed advisory""" + with open(os.path.join(TEST_DATA, "advisory-20030916-1.txt"), "r") as f: + advisory_text = f.read() + + advisory_url = "https://kde.org/info/security/advisory-20030916-1.txt" + result = parse_advisory(advisory_text, advisory_url) + + # Check that CVE IDs were extracted and converted from CAN format + assert "CVE-2003-0690" in result.aliases + assert "CVE-2003-0692" in result.aliases + assert len(result.aliases) == 2 + + # Check summary was extracted + assert "KDM vulnerabilities" in result.summary + + # Check references include CVE URLs + cve_urls = [ref.url for ref in result.references if "cve.mitre.org" in ref.url] + assert len(cve_urls) == 2 + + def test_parse_new_format_advisory(self): + """Test parsing new format advisory""" + with open(os.path.join(TEST_DATA, "advisory-20260109-1.txt"), "r") as f: + advisory_text = f.read() + + advisory_url = "https://kde.org/info/security/advisory-20260109-1.txt" + result = parse_advisory(advisory_text, advisory_url) + + # Check CVE IDs were extracted + assert "CVE-2025-66002" in result.aliases + assert "CVE-2025-66003" in result.aliases + + # Check title was extracted + assert "Smb4K" in result.summary + + def test_extract_summary_old_format(self): + """Test summary extraction from old format""" + advisory_text = """-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + + + +KDE Security Advisory: KDM vulnerabilities +Original Release Date: 2003-09-16""" + + summary = extract_summary(advisory_text) + assert "KDM vulnerabilities" in summary + + def test_extract_summary_new_format(self): + """Test summary extraction from new format""" + advisory_text = """KDE Project Security Advisory +============================= + +Title: Smb4K: Major security issues +Risk rating: Major""" + + summary = extract_summary(advisory_text) + assert "Smb4K" in summary