Skip to content

Commit 577c9a9

Browse files
committed
Merge branch 'dev' of https://github.com/MaibornWolff/SecObserve into stackable
2 parents 18afad2 + e468189 commit 577c9a9

64 files changed

Lines changed: 1184 additions & 395 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/check_backend.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
steps:
1515
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
1616
- name: Set up Python 3.12
17-
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
17+
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
1818
with:
1919
python-version: 3.12
2020

.github/workflows/publish_docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
contents: write
1616
steps:
1717
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
18-
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
18+
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
1919
with:
2020
python-version: 3.x
2121
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3

.github/workflows/scan_sca_current.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
name: Checkout
1616
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
1717
with:
18-
ref: 'v1.29.1'
18+
ref: 'v1.29.2'
1919
-
2020
name: Run SCA vulnerability scanners
2121
uses: MaibornWolff/secobserve_actions_templates/actions/vulnerability_scanner@2f7b500fde2de2bdea7eef3b6df5503c2476916f # main

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,6 @@ jobs:
6767

6868
# Upload the results to GitHub's code scanning dashboard.
6969
- name: "Upload to code-scanning"
70-
uses: github/codeql-action/upload-sarif@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12
70+
uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13
7171
with:
7272
sarif_file: results.sarif

backend/application/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.29.1"
1+
__version__ = "1.29.2"
22

33
import pymysql
44

backend/application/core/api/filters.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
OrderingFilter,
1414
)
1515

16+
from application.commons.api.extended_ordering_filter import ExtendedOrderingFilter
1617
from application.commons.types import Age_Choices
1718
from application.core.models import (
1819
Branch,
@@ -236,15 +237,15 @@ class ObservationFilter(FilterSet):
236237
branch_name = CharFilter(field_name="branch__name", lookup_expr="icontains")
237238
cve_known_exploited = BooleanFilter(field_name="cve_known_exploited", method="get_cve_known_exploited")
238239

239-
ordering = OrderingFilter(
240+
ordering = ExtendedOrderingFilter(
240241
# tuple-mapping retains order
241242
fields=(
242243
("id", "id"),
243244
("product__name", "product_data.name"),
244245
("product__product_group__name", "product_data.product_group_name"),
245246
("branch__name", "branch_name"),
246247
("title", "title"),
247-
("numerical_severity", "current_severity"),
248+
(("numerical_severity", "id"), "current_severity"),
248249
("current_status", "current_status"),
249250
("origin_component_name_version", "origin_component_name_version"),
250251
(
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Generated by Django 5.1.7 on 2025-03-26 16:07
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("core", "0061_observation_cve_found_in_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="branch",
15+
name="osv_linux_distribution",
16+
field=models.CharField(
17+
blank=True,
18+
choices=[
19+
("AlmaLinux", "AlmaLinux"),
20+
("Alpine", "Alpine"),
21+
("Chainguard", "Chainguard"),
22+
("Debian", "Debian"),
23+
("Mageia", "Mageia"),
24+
("openSUSE", "openSUSE"),
25+
("Photon OS", "Photon OS"),
26+
("Red Hat", "Red Hat"),
27+
("Rocky Linux", "Rocky Linux"),
28+
("SUSE", "SUSE"),
29+
("Ubuntu", "Ubuntu"),
30+
("Wolfi", "Wolfi"),
31+
],
32+
max_length=12,
33+
),
34+
),
35+
migrations.AlterField(
36+
model_name="product",
37+
name="osv_linux_distribution",
38+
field=models.CharField(
39+
blank=True,
40+
choices=[
41+
("AlmaLinux", "AlmaLinux"),
42+
("Alpine", "Alpine"),
43+
("Chainguard", "Chainguard"),
44+
("Debian", "Debian"),
45+
("Mageia", "Mageia"),
46+
("openSUSE", "openSUSE"),
47+
("Photon OS", "Photon OS"),
48+
("Red Hat", "Red Hat"),
49+
("Rocky Linux", "Rocky Linux"),
50+
("SUSE", "SUSE"),
51+
("Ubuntu", "Ubuntu"),
52+
("Wolfi", "Wolfi"),
53+
],
54+
max_length=12,
55+
),
56+
),
57+
]

backend/application/core/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ class PURL_Type:
158158
class OSVLinuxDistribution:
159159
DISTRIBUTION_ALMALINUX = "AlmaLinux"
160160
DISTRIBUTION_ALPINE = "Alpine"
161+
DISTRIBUTION_CHAINGUARD = "Chainguard"
161162
DISTRIBUTION_DEBIAN = "Debian"
162163
DISTRIBUTION_MAGEIA = "Mageia"
163164
DISTRIBUTION_OPENSUSE = "openSUSE"
@@ -166,10 +167,12 @@ class OSVLinuxDistribution:
166167
DISTRIBUTION_ROCKY_LINUX = "Rocky Linux"
167168
DISTRIBUTION_SUSE = "SUSE"
168169
DISTRIBUTION_UBUNTU = "Ubuntu"
170+
DISTRIBUTION_WOLFI = "Wolfi"
169171

170172
OSV_LINUX_DISTRIBUTION_CHOICES = [
171173
(DISTRIBUTION_ALMALINUX, DISTRIBUTION_ALMALINUX),
172174
(DISTRIBUTION_ALPINE, DISTRIBUTION_ALPINE),
175+
(DISTRIBUTION_CHAINGUARD, DISTRIBUTION_CHAINGUARD),
173176
(DISTRIBUTION_DEBIAN, DISTRIBUTION_DEBIAN),
174177
(DISTRIBUTION_MAGEIA, DISTRIBUTION_MAGEIA),
175178
(DISTRIBUTION_OPENSUSE, DISTRIBUTION_OPENSUSE),
@@ -178,4 +181,5 @@ class OSVLinuxDistribution:
178181
(DISTRIBUTION_ROCKY_LINUX, DISTRIBUTION_ROCKY_LINUX),
179182
(DISTRIBUTION_SUSE, DISTRIBUTION_SUSE),
180183
(DISTRIBUTION_UBUNTU, DISTRIBUTION_UBUNTU),
184+
(DISTRIBUTION_WOLFI, DISTRIBUTION_WOLFI),
181185
]

backend/application/import_observations/parsers/cyclone_dx/parser.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def _get_component(self, component_data: dict[str, Any]) -> Optional[Component]:
192192
if not component_data.get("bom-ref"):
193193
return None
194194

195-
cyclonedx_licenses = []
195+
cyclonedx_licenses: list[str] = []
196196
licenses = component_data.get("licenses", [])
197197
if licenses and licenses[0].get("expression"):
198198
cyclonedx_licenses.append(licenses[0].get("expression"))
@@ -206,6 +206,14 @@ def _get_component(self, component_data: dict[str, Any]) -> Optional[Component]:
206206
if component_license and component_license not in cyclonedx_licenses:
207207
cyclonedx_licenses.append(component_license)
208208

209+
if len(cyclonedx_licenses) > 1:
210+
cyclonedx_licenses = [license.replace(",", "") for license in cyclonedx_licenses]
211+
unsaved_license = "[" + ", ".join(cyclonedx_licenses) + "]"
212+
elif cyclonedx_licenses:
213+
unsaved_license = cyclonedx_licenses[0]
214+
else:
215+
unsaved_license = ""
216+
209217
return Component(
210218
bom_ref=component_data.get("bom-ref", ""),
211219
name=component_data.get("name", ""),
@@ -214,7 +222,7 @@ def _get_component(self, component_data: dict[str, Any]) -> Optional[Component]:
214222
purl=component_data.get("purl", ""),
215223
cpe=component_data.get("cpe", ""),
216224
json=component_data,
217-
unsaved_license=", ".join(cyclonedx_licenses),
225+
unsaved_license=unsaved_license,
218226
)
219227

220228
def _create_observations( # pylint: disable=too-many-locals

backend/application/import_observations/parsers/osv/parser.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from packageurl import PackageURL
88

99
from application.core.models import Branch, Observation, Product
10+
from application.core.types import OSVLinuxDistribution
1011
from application.import_observations.parsers.base_parser import BaseParser
1112
from application.import_observations.services.osv_cache import get_osv_vulnerability
1213
from application.import_observations.types import ExtendedSemVer, Parser_Type
@@ -242,6 +243,8 @@ def _get_affected(
242243
if not package_osv_ecosystem and product.osv_linux_distribution:
243244
package_osv_ecosystem = product.osv_linux_distribution
244245

246+
package_osv_ecosystem = self._get_linux_package_osv_ecosystem(parsed_purl, package_osv_ecosystem)
247+
245248
for affected_item in osv_vulnerability.get("affected", []):
246249
package = affected_item.get("package", {})
247250
affected_ecosystem = package.get("ecosystem")
@@ -251,6 +254,67 @@ def _get_affected(
251254

252255
return affected
253256

257+
def _get_linux_package_osv_ecosystem(
258+
self, parsed_purl: PackageURL, package_osv_ecosystem: Optional[str]
259+
) -> Optional[str]:
260+
if not package_osv_ecosystem:
261+
package_osv_ecosystem = self._get_linux_package_osv_ecosystem_apk(parsed_purl)
262+
if not package_osv_ecosystem:
263+
package_osv_ecosystem = self._get_linux_package_osv_ecosystem_deb(parsed_purl)
264+
return package_osv_ecosystem
265+
266+
def _get_linux_package_osv_ecosystem_apk(self, parsed_purl: PackageURL) -> Optional[str]:
267+
package_osv_ecosystem = None
268+
269+
if parsed_purl.qualifiers and isinstance(parsed_purl.qualifiers, dict):
270+
package_type = parsed_purl.type
271+
if package_type == "apk" and parsed_purl.namespace == "alpine":
272+
distro = parsed_purl.qualifiers.get("distro")
273+
if distro:
274+
if distro.startswith("alpine-"):
275+
distro = distro[7:]
276+
distro_parts = distro.split(".")
277+
if len(distro_parts) >= 2 and distro_parts[0].isdigit() and distro_parts[1].isdigit():
278+
distro_version = f"{distro_parts[0]}.{distro_parts[1]}"
279+
package_osv_ecosystem = f"{OSVLinuxDistribution.DISTRIBUTION_ALPINE}:v{distro_version}"
280+
elif package_type == "apk" and parsed_purl.namespace == "chainguard":
281+
package_osv_ecosystem = OSVLinuxDistribution.DISTRIBUTION_CHAINGUARD
282+
elif package_type == "apk" and parsed_purl.namespace == "wolfi":
283+
package_osv_ecosystem = OSVLinuxDistribution.DISTRIBUTION_WOLFI
284+
285+
return package_osv_ecosystem
286+
287+
def _get_linux_package_osv_ecosystem_deb(self, parsed_purl: PackageURL) -> Optional[str]:
288+
package_osv_ecosystem = None
289+
290+
if parsed_purl.qualifiers and isinstance(parsed_purl.qualifiers, dict):
291+
package_type = parsed_purl.type
292+
if package_type == "deb" and parsed_purl.namespace == "debian":
293+
distro = parsed_purl.qualifiers.get("distro")
294+
if distro:
295+
if distro.startswith("debian-"):
296+
distro = distro[7:]
297+
distro_parts = distro.split(".")
298+
if len(distro_parts) >= 1 and distro_parts[0].isdigit():
299+
package_osv_ecosystem = f"{OSVLinuxDistribution.DISTRIBUTION_DEBIAN}:{distro_parts[0]}"
300+
elif package_type == "deb" and parsed_purl.namespace == "ubuntu":
301+
distro = parsed_purl.qualifiers.get("distro")
302+
if distro:
303+
if distro.startswith("ubuntu-"):
304+
distro = distro[7:]
305+
distro_parts = distro.split(".")
306+
if len(distro_parts) >= 2:
307+
if distro_parts[0].isdigit() and int(distro_parts[0]) % 2 == 0 and distro_parts[1] == "04":
308+
package_osv_ecosystem = (
309+
f"{OSVLinuxDistribution.DISTRIBUTION_UBUNTU}:{distro_parts[0]}.{distro_parts[1]}:LTS"
310+
)
311+
else:
312+
package_osv_ecosystem = (
313+
f"{OSVLinuxDistribution.DISTRIBUTION_UBUNTU}:{distro_parts[0]}.{distro_parts[1]}"
314+
)
315+
316+
return package_osv_ecosystem
317+
254318
def _get_package_name(self, parsed_purl: PackageURL) -> str:
255319
package_name = parsed_purl.name
256320
package_namespace = parsed_purl.namespace

0 commit comments

Comments
 (0)