Skip to content

Commit 2db56b7

Browse files
tarun111111claude
andcommitted
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 <noreply@anthropic.com> Signed-off-by: tarun111111 <tarunpuri2544@gmail.com>
1 parent 053c8fb commit 2db56b7

File tree

4 files changed

+348
-0
lines changed

4 files changed

+348
-0
lines changed

vulnerabilities/importers/kde.py

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
import logging
11+
import re
12+
from typing import Iterable
13+
14+
import requests
15+
from bs4 import BeautifulSoup
16+
17+
from vulnerabilities.importer import AdvisoryData
18+
from vulnerabilities.importer import Importer
19+
from vulnerabilities.importer import Reference
20+
21+
logger = logging.getLogger(__name__)
22+
23+
24+
class KdeImporter(Importer):
25+
spdx_license_expression = "LGPL-2.0-or-later"
26+
license_url = "https://kde.org/community/whatiskde/licensing/"
27+
importer_name = "KDE Importer"
28+
url = "https://kde.org/info/security/"
29+
30+
def advisory_data(self) -> Iterable[AdvisoryData]:
31+
advisory_urls = fetch_advisory_urls(self.url)
32+
for advisory_url in advisory_urls:
33+
try:
34+
advisory_text = fetch_advisory_text(advisory_url)
35+
advisory = parse_advisory(advisory_text, advisory_url)
36+
if advisory:
37+
yield advisory
38+
except Exception as e:
39+
logger.error(f"Error parsing advisory {advisory_url}: {e}")
40+
41+
42+
def fetch_advisory_urls(index_url):
43+
"""
44+
Fetch all advisory URLs from the KDE security index page.
45+
46+
Returns:
47+
List of full advisory URLs
48+
"""
49+
response = requests.get(index_url)
50+
if response.status_code != 200:
51+
logger.error(f"Failed to fetch {index_url}")
52+
return []
53+
54+
soup = BeautifulSoup(response.content, "html.parser")
55+
advisory_urls = []
56+
57+
# Find all links in the page
58+
for link in soup.find_all("a"):
59+
href = link.get("href", "")
60+
# Advisory files end with .txt and start with advisory- or contain xpdf
61+
if href.endswith(".txt") and ("advisory-" in href or "xpdf" in href):
62+
# Convert relative URL to absolute
63+
if href.startswith("./"):
64+
href = href[2:]
65+
full_url = f"https://kde.org/info/security/{href}"
66+
advisory_urls.append(full_url)
67+
68+
return advisory_urls
69+
70+
71+
def fetch_advisory_text(advisory_url):
72+
"""
73+
Fetch the text content of a KDE security advisory.
74+
75+
Returns:
76+
Advisory text as string
77+
"""
78+
response = requests.get(advisory_url)
79+
if response.status_code != 200:
80+
logger.error(f"Failed to fetch {advisory_url}")
81+
return None
82+
83+
return response.text
84+
85+
86+
def parse_advisory(advisory_text, advisory_url):
87+
"""
88+
Parse a KDE security advisory and extract relevant information.
89+
90+
Args:
91+
advisory_text: The full text content of the advisory
92+
advisory_url: URL of the advisory
93+
94+
Returns:
95+
AdvisoryData object or None
96+
"""
97+
if not advisory_text:
98+
return None
99+
100+
# Extract CVE IDs (both CVE and old CAN format)
101+
cve_pattern = r'C(?:VE|AN)-\d{4}-\d{4,7}'
102+
cve_ids = re.findall(cve_pattern, advisory_text)
103+
104+
# Convert old CAN format to CVE format
105+
aliases = []
106+
for cve_id in cve_ids:
107+
if cve_id.startswith("CAN-"):
108+
# CAN is old format, convert to CVE
109+
cve_id = cve_id.replace("CAN-", "CVE-")
110+
if cve_id not in aliases:
111+
aliases.append(cve_id)
112+
113+
# Extract summary/title
114+
summary = extract_summary(advisory_text)
115+
116+
# Extract references
117+
references = extract_references(advisory_text, advisory_url, aliases)
118+
119+
# Only create advisory if we have CVE IDs or meaningful content
120+
if not aliases and not summary:
121+
logger.warning(f"No CVE IDs or summary found in {advisory_url}")
122+
return None
123+
124+
return AdvisoryData(
125+
aliases=aliases,
126+
summary=summary,
127+
references=references,
128+
url=advisory_url,
129+
)
130+
131+
132+
def extract_summary(advisory_text):
133+
"""
134+
Extract the summary/title from the advisory text.
135+
"""
136+
lines = advisory_text.split("\n")
137+
138+
# Try to find Title: field (new format)
139+
for i, line in enumerate(lines):
140+
if line.startswith("Title:"):
141+
return line.replace("Title:", "").strip()
142+
143+
# Try to find KDE Security Advisory: line (old format)
144+
for line in lines:
145+
if "KDE Security Advisory:" in line:
146+
return line.replace("KDE Security Advisory:", "").strip()
147+
148+
# Try to find first non-empty line after PGP header
149+
skip_pgp = False
150+
for line in lines:
151+
if line.startswith("-----BEGIN PGP"):
152+
skip_pgp = True
153+
continue
154+
if skip_pgp and line.strip() and not line.startswith("Hash:"):
155+
skip_pgp = False
156+
continue
157+
if not skip_pgp and line.strip() and not line.startswith("---"):
158+
# Return first meaningful line as summary
159+
return line.strip()
160+
161+
return ""
162+
163+
164+
def extract_references(advisory_text, advisory_url, aliases):
165+
"""
166+
Extract reference URLs from the advisory text.
167+
"""
168+
references = []
169+
170+
# Add the advisory itself as a reference
171+
references.append(Reference(url=advisory_url))
172+
173+
# Add CVE references
174+
for cve_id in aliases:
175+
cve_url = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}"
176+
references.append(Reference(url=cve_url, reference_id=cve_id))
177+
178+
# Extract URLs from text
179+
url_pattern = r'https?://[^\s<>"\')]+[^\s<>"\')\.]'
180+
urls = re.findall(url_pattern, advisory_text)
181+
182+
for url in urls:
183+
# Skip if already added
184+
if any(ref.url == url for ref in references):
185+
continue
186+
# Add unique URLs
187+
references.append(Reference(url=url))
188+
189+
return references
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
-----BEGIN PGP SIGNED MESSAGE-----
2+
Hash: SHA1
3+
4+
5+
6+
KDE Security Advisory: KDM vulnerabilities
7+
Original Release Date: 2003-09-16
8+
URL: http://www.kde.org/info/security/advisory-20030916-1.txt
9+
10+
0. References
11+
http://cert.uni-stuttgart.de/archive/suse/security/2002/12/msg00101.html
12+
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0690
13+
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0692
14+
15+
16+
1. Systems affected:
17+
18+
All versions of KDM as distributed with KDE up to and including
19+
KDE 3.1.3.
20+
21+
22+
2. Overview:
23+
24+
Two issues have been discovered in KDM:
25+
26+
a) CAN-2003-0690:
27+
Privilege escalation with specific PAM modules
28+
b) CAN-2003-0692:
29+
Session cookies generated by KDM are potentially insecure
30+
31+
32+
3. Impact:
33+
34+
If KDM is used in combination with the MIT pam_krb5 module and given
35+
a valid username and password of an existing user, the login attempt
36+
succeeds and establishes a session with excessive privileges.
37+
38+
39+
4. Solution:
40+
41+
Users of KDE 3.1.x are advised to upgrade to KDE 3.1.4.
42+
43+
44+
-----BEGIN PGP SIGNATURE-----
45+
Version: GnuPG v1.2.2-rc1-SuSE (GNU/Linux)
46+
47+
iD8DBQE/Z1fBvsXr+iuy1UoRAi02AJ90TqHxCeqzqGJrN3jS7mRSd9u5xQCg6/Do
48+
LB3tubiwfy8TUy5rL7B8UFY=
49+
=2tbX
50+
-----END PGP SIGNATURE-----
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
KDE Project Security Advisory
2+
=============================
3+
4+
Title: Smb4K: Major security issues in KAuth mount helper
5+
Risk rating: Major
6+
CVE: CVE-2025-66002, CVE-2025-66003
7+
Versions: Smb4K < 4.0.5
8+
Date: 9 January 2026
9+
10+
Overview
11+
========
12+
13+
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.
14+
15+
16+
Impact
17+
======
18+
19+
An attacker can exploit the shortcomings in the KAuth mount helper and perform arbitrary unmounts.
20+
21+
22+
Solution
23+
========
24+
25+
Update Smb4K to version 4.0.5 or later.
26+
27+
28+
Credits
29+
=======
30+
31+
Thanks to Matthias Gerstner and the SUSE security team for reporting this issue.

vulnerabilities/tests/test_kde.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
import os
11+
from unittest import TestCase
12+
13+
from vulnerabilities.importers.kde import extract_summary
14+
from vulnerabilities.importers.kde import parse_advisory
15+
16+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
17+
TEST_DATA = os.path.join(BASE_DIR, "test_data/kde")
18+
19+
20+
class TestKdeImporter(TestCase):
21+
def test_parse_old_format_advisory(self):
22+
"""Test parsing old format PGP-signed advisory"""
23+
with open(os.path.join(TEST_DATA, "advisory-20030916-1.txt"), "r") as f:
24+
advisory_text = f.read()
25+
26+
advisory_url = "https://kde.org/info/security/advisory-20030916-1.txt"
27+
result = parse_advisory(advisory_text, advisory_url)
28+
29+
# Check that CVE IDs were extracted and converted from CAN format
30+
assert "CVE-2003-0690" in result.aliases
31+
assert "CVE-2003-0692" in result.aliases
32+
assert len(result.aliases) == 2
33+
34+
# Check summary was extracted
35+
assert "KDM vulnerabilities" in result.summary
36+
37+
# Check references include CVE URLs
38+
cve_urls = [ref.url for ref in result.references if "cve.mitre.org" in ref.url]
39+
assert len(cve_urls) == 2
40+
41+
def test_parse_new_format_advisory(self):
42+
"""Test parsing new format advisory"""
43+
with open(os.path.join(TEST_DATA, "advisory-20260109-1.txt"), "r") as f:
44+
advisory_text = f.read()
45+
46+
advisory_url = "https://kde.org/info/security/advisory-20260109-1.txt"
47+
result = parse_advisory(advisory_text, advisory_url)
48+
49+
# Check CVE IDs were extracted
50+
assert "CVE-2025-66002" in result.aliases
51+
assert "CVE-2025-66003" in result.aliases
52+
53+
# Check title was extracted
54+
assert "Smb4K" in result.summary
55+
56+
def test_extract_summary_old_format(self):
57+
"""Test summary extraction from old format"""
58+
advisory_text = """-----BEGIN PGP SIGNED MESSAGE-----
59+
Hash: SHA1
60+
61+
62+
63+
KDE Security Advisory: KDM vulnerabilities
64+
Original Release Date: 2003-09-16"""
65+
66+
summary = extract_summary(advisory_text)
67+
assert "KDM vulnerabilities" in summary
68+
69+
def test_extract_summary_new_format(self):
70+
"""Test summary extraction from new format"""
71+
advisory_text = """KDE Project Security Advisory
72+
=============================
73+
74+
Title: Smb4K: Major security issues
75+
Risk rating: Major"""
76+
77+
summary = extract_summary(advisory_text)
78+
assert "Smb4K" in summary

0 commit comments

Comments
 (0)