Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SOURCES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
+-----------------------+------------------------------------------------------------------------------------------+--------------------------------------------+
| fireeye | https://github.com/mandiant/Vulnerability-Disclosures | none |
+-----------------------+------------------------------------------------------------------------------------------+--------------------------------------------+
| tuxcare | https://osv-vulnerabilities.storage.googleapis.com/TuxCare/all.zip | rpm, deb, maven, npm, pypi, composer |
+-----------------------+------------------------------------------------------------------------------------------+--------------------------------------------+
| fix_commits | * https://github.com/torvalds/linux | |
| | * https://github.com/mirror/busybox | |
| | * https://github.com/nginx/nginx | |
Expand Down
2 changes: 2 additions & 0 deletions vulnerabilities/importers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from vulnerabilities.pipelines.v2_importers import retiredotnet_importer as retiredotnet_importer_v2
from vulnerabilities.pipelines.v2_importers import ruby_importer as ruby_importer_v2
from vulnerabilities.pipelines.v2_importers import suse_score_importer as suse_score_importer_v2
from vulnerabilities.pipelines.v2_importers import tuxcare_importer as tuxcare_importer_v2
from vulnerabilities.pipelines.v2_importers import ubuntu_osv_importer as ubuntu_osv_importer_v2
from vulnerabilities.pipelines.v2_importers import vulnrichment_importer as vulnrichment_importer_v2
from vulnerabilities.pipelines.v2_importers import xen_importer as xen_importer_v2
Expand Down Expand Up @@ -87,6 +88,7 @@
debian_importer_v2.DebianImporterPipeline,
mattermost_importer_v2.MattermostImporterPipeline,
glibc_importer_v2.GlibcImporterPipeline,
tuxcare_importer_v2.TuxCareImporterPipeline,
apache_tomcat_v2.ApacheTomcatImporterPipeline,
suse_score_importer_v2.SUSESeverityScoreImporterPipeline,
retiredotnet_importer_v2.RetireDotnetImporterPipeline,
Expand Down
85 changes: 85 additions & 0 deletions vulnerabilities/pipelines/v2_importers/tuxcare_importer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#
# 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 json
import logging
from io import BytesIO
from typing import Iterable
from zipfile import ZipFile

import requests

from vulnerabilities.importer import AdvisoryDataV2
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
from vulnerabilities.pipes.osv_v2 import parse_advisory_data_v3


class TuxCareImporterPipeline(VulnerableCodeBaseImporterPipelineV2):
"""
TuxCare Importer Pipeline
Collect advisories from TuxCare OSV feed.
"""

pipeline_id = "tuxcare_importer_v2"
datasource_id = "tuxcare"
license_url = "https://tuxcare.com/legal"
spdx_license_expression = "CC-BY-4.0"
url = "https://osv-vulnerabilities.storage.googleapis.com/TuxCare/all.zip"
precedence = 100

@classmethod
def steps(cls):
return (
cls.fetch_zip,
cls.collect_and_store_advisories,
)

def fetch_zip(self):
self.log(f"Fetching `{self.url}`")
self.advisory_zip = requests.get(self.url).content

def advisories_count(self) -> int:
with ZipFile(BytesIO(self.advisory_zip)) as zip_file:
advisory_count = sum(
1
for file in zip_file.namelist()
if file.startswith("CLSA-") and file.endswith(".json")
)
return advisory_count

def collect_advisories(self) -> Iterable[AdvisoryDataV2]:
"""Yield AdvisoryDataV2 using a zipped data dump of OSV data"""
supported_ecosystems = [
# els_os
Comment thread
Samk1710 marked this conversation as resolved.
Resolved
"rpm",
"deb",
# els_lang
"maven",
"npm",
"pypi",
"composer",
]

with ZipFile(BytesIO(self.advisory_zip)) as zip_file:
for file_name in zip_file.namelist():
if not (file_name.startswith("CLSA-") and file_name.endswith(".json")):
self.log(
f"Unsupported TuxCare advisory data file: {file_name}",
level=logging.ERROR,
)
continue
with zip_file.open(file_name) as f:
advisory_text = f.read()
vul_info = json.loads(advisory_text)
yield parse_advisory_data_v3(
raw_data=vul_info,
supported_ecosystems=supported_ecosystems,
advisory_url=self.url,
advisory_text=advisory_text.decode("utf-8"),
Comment thread
Samk1710 marked this conversation as resolved.
Resolved
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#
# 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.
#

from io import BytesIO
from pathlib import Path
from unittest import TestCase
from unittest.mock import Mock
from unittest.mock import patch
from zipfile import ZipFile

from vulnerabilities.pipelines.v2_importers.tuxcare_importer import TuxCareImporterPipeline
from vulnerabilities.tests import util_tests

TEST_DATA = Path(__file__).parent.parent.parent / "test_data" / "tuxcare"


class TestTuxCareImporterPipeline(TestCase):
"""Tests for TuxCareImporterPipeline using OSV feed data."""

@classmethod
def setUpClass(cls):
super().setUpClass()
zip_buffer = BytesIO()
with ZipFile(zip_buffer, mode="w") as zip_file:
for json_file in sorted(TEST_DATA.glob("CLSA-*.json")):
zip_file.writestr(json_file.name, json_file.read_bytes())
cls.sample_zip = zip_buffer.getvalue()

@patch("requests.get")
def test_fetch_zip(self, mock_get):
mock_get.return_value = Mock(content=self.sample_zip)

pipeline = TuxCareImporterPipeline()
pipeline.fetch_zip()

mock_get.assert_called_once_with(pipeline.url)
self.assertEqual(pipeline.advisory_zip, self.sample_zip)

def test_advisories_count(self):
pipeline = TuxCareImporterPipeline()
pipeline.advisory_zip = self.sample_zip

self.assertEqual(pipeline.advisories_count(), 6)

def test_collect_advisories(self):
pipeline = TuxCareImporterPipeline()
pipeline.advisory_zip = self.sample_zip

advisories_by_id = {
data.advisory_id: [data.to_dict()] for data in pipeline.collect_advisories()
}
self.assertEqual(len(advisories_by_id), 6)

for json_file in sorted(TEST_DATA.glob("CLSA-*.json")):
clsa_id = json_file.stem
expected_file = TEST_DATA / f"expected-{clsa_id}.json"
self.assertIn(clsa_id, advisories_by_id)
util_tests.check_results_against_json(advisories_by_id[clsa_id], expected_file)
44 changes: 44 additions & 0 deletions vulnerabilities/tests/test_data/tuxcare/CLSA-2025-1763136711.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"schema_version": "1.7.5",
"id": "CLSA-2025-1763136711",
"published": "2025-11-14T16:11:55Z",
"modified": "2026-06-04T09:45:21.689571417Z",
"upstream": [
"CVE-2022-29154",
"CVE-2024-12087",
"CVE-2024-12088"
],
"summary": "Fix CVE(s): CVE-2022-29154, CVE-2024-12087, CVE-2024-12088",
"details": " * SECURITY UPDATE: malicious remote servers to write arbitrary files inside\n the directories of connecting peers:\n - debian/patches/els/0001-CVE-2022-29154.patch: fix insufficient validation of file\n names.\n - CVE-2022-29154.\n * SECURITY UPDATE: path traversal vulnerability.\n - debian/patches/els/0002-CVE-2024-12087.patch: refuse a duplicate dirlist and\n range check dir_ndx before use\n - CVE-2024-12087\n * SECURITY UPDATE: rsync client fails to properly verify if a symbolic link\n destination sent from the server contains another symbolic link within it:\n - debian/patches/els/0003-CVE-2024-12088.patch: make --safe-links stricter.\n - CVE-2024-12088.",
"affected": [
{
"package": {
"name": "rsync",
"ecosystem": "TuxCare:Debian:10",
"purl": "pkg:deb/tuxcare/rsync?distro=debian-10"
},
"ranges": [
{
"type": "ECOSYSTEM",
"events": [
{
"introduced": "0"
},
{
"fixed": "3.1.3-6+tuxcare.els1"
}
]
}
],
"database_specific": {
"source": "https://github.com/cloudlinux/tuxcare-osv/tree/main/data/els_os/debian10els/CLSA-2025-1763136711.json"
}
}
],
"references": [
{
"type": "ADVISORY",
"url": "https://errata.tuxcare.com/els_os/debian10els/CLSA-2025-1763136711.html"
}
]
}
43 changes: 43 additions & 0 deletions vulnerabilities/tests/test_data/tuxcare/CLSA-2025-1765930349.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"schema_version": "1.9.0",
"id": "CLSA-2025-1765930349",
"published": "2026-08-04T14:52:09Z",
"modified": "2026-10-15T09:47:53.555363298Z",
"upstream": [
"AIKIDO-2024-10370"
],
"summary": "TuxCare security update for sentence-transformers (1 CVE)",
"details": "TuxCare rebuilt sentence-transformers to address 1 CVE. Fixed in 2.7.0.post1+tuxcare.",
"affected": [
{
"package": {
"name": "sentence-transformers",
"ecosystem": "TuxCare:PyPI",
"purl": "pkg:pypi/sentence-transformers"
},
"ranges": [
{
"type": "ECOSYSTEM",
"events": [
{
"introduced": "2.7.0"
},
{
"fixed": "2.7.0.post1+tuxcare"
}
]
}
],
"versions": [
"2.7.0"
],
"database_specific": {
"source": "https://github.com/cloudlinux/tuxcare-osv/tree/main/data/els_lang/pypi/CLSA-2025-1765930349.json",
"tuxcare": {
"base_version": "2.7.0",
"fixed_version": "2.7.0.post1+tuxcare"
}
}
}
]
}
50 changes: 50 additions & 0 deletions vulnerabilities/tests/test_data/tuxcare/CLSA-2026-1776213254.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"schema_version": "1.9.0",
"id": "CLSA-2026-1776213254",
"published": "2026-08-04T14:52:25Z",
"modified": "2026-10-15T09:47:59.566920710Z",
"upstream": [
"CVE-2021-21263",
"CVE-2021-43808",
"CVE-2024-52301",
"CVE-2025-27515",
"GHSA-4mg9-vhxq-vm7j",
"GHSA-jwvj-pwww-3mj5",
"GHSA-wq8p-mqvg-2p5h",
"GHSA-x7p5-p2c9-phvg"
],
"summary": "TuxCare security update for laravel/framework (8 CVEs)",
"details": "TuxCare rebuilt laravel/framework to address 8 CVEs. Fixed in 8.12.2-p1+tuxcare.",
"affected": [
{
"package": {
"name": "laravel/framework",
"ecosystem": "TuxCare:Packagist",
"purl": "pkg:composer/laravel/framework"
},
"ranges": [
{
"type": "ECOSYSTEM",
"events": [
{
"introduced": "8.12.2"
},
{
"fixed": "8.12.2-p1+tuxcare"
}
]
}
],
"versions": [
"8.12.2"
],
"database_specific": {
"source": "https://github.com/cloudlinux/tuxcare-osv/tree/main/data/els_lang/packagist/CLSA-2026-1776213254.json",
"tuxcare": {
"base_version": "8.12.2",
"fixed_version": "8.12.2-p1+tuxcare"
}
}
}
]
}
Loading