Skip to content

Commit d2df61b

Browse files
committed
Merge branch 'dev' of https://github.com/MaibornWolff/SecObserve into stackable
2 parents 79f5bc6 + 0b68e9e commit d2df61b

File tree

103 files changed

+6253
-1830
lines changed

Some content is hidden

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

103 files changed

+6253
-1830
lines changed

.github/workflows/build_push_dev.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ jobs:
3636
run: echo "CREATED=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV
3737
-
3838
name: Build and push backend
39-
id: build-and-push-backend
40-
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
39+
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
4140
with:
4241
context: .
4342
file: ./docker/backend/prod/django/Dockerfile
@@ -52,8 +51,7 @@ jobs:
5251
run: cosign sign -y oci.stackable.tech/stackable/secobserve-backend@${{ steps.build-and-push-backend.outputs.digest }}
5352
-
5453
name: Build and push frontend
55-
id: build-and-push-frontend
56-
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
54+
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
5755
with:
5856
context: .
5957
file: ./docker/frontend/prod/Dockerfile

.github/workflows/build_push_release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
run: echo "CREATED=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV
3737
-
3838
name: Build and push backend
39-
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
39+
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
4040
with:
4141
context: .
4242
file: ./docker/backend/prod/django/Dockerfile
@@ -50,7 +50,7 @@ jobs:
5050
VERSION=${{ github.event.inputs.release }}
5151
-
5252
name: Build and push frontend
53-
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
53+
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
5454
with:
5555
context: .
5656
file: ./docker/frontend/prod/Dockerfile

.github/workflows/publish_docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
1919
with:
2020
python-version: 3.x
21-
- uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
21+
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
2222
with:
2323
key: ${{ github.ref }}
2424
path: .cache

.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.22.2'
18+
ref: 'v1.23.0'
1919
-
2020
name: Run SCA vulnerability scanners
2121
uses: MaibornWolff/secobserve_actions_templates/actions/vulnerability_scanner@5476f0de11c46875081d9767ec166c1e030e9ef0 # 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@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
70+
uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
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.22.4"
1+
__version__ = "1.23.0"
22

33
import pymysql
44

backend/application/access_control/api/serializers.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from typing import Optional
22

3-
from django.core.validators import MinValueValidator
3+
from django.core.validators import MaxValueValidator, MinValueValidator
44
from rest_framework.serializers import (
55
CharField,
6-
ChoiceField,
76
IntegerField,
87
ModelSerializer,
98
Serializer,
@@ -21,7 +20,7 @@
2120
get_authorization_group_member,
2221
)
2322
from application.access_control.services.authorization import get_user_permissions
24-
from application.access_control.services.roles_permissions import Permissions, Roles
23+
from application.access_control.services.roles_permissions import Permissions
2524
from application.commons.services.global_request import get_current_user
2625
from application.core.models import Product_Authorization_Group_Member, Product_Member
2726

@@ -283,7 +282,7 @@ class AuthenticationResponseSerializer(Serializer):
283282

284283
class ProductApiTokenSerializer(Serializer):
285284
id = IntegerField(validators=[MinValueValidator(0)])
286-
role = ChoiceField(choices=Roles)
285+
role = IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
287286

288287

289288
class ApiTokenSerializer(ModelSerializer):

backend/application/core/api/filters.py

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,17 +339,58 @@ class ObservationLogFilter(FilterSet):
339339
origin_component_name_version = CharFilter(
340340
field_name="observation__origin_component_name_version", lookup_expr="icontains"
341341
)
342+
origin_docker_image_name_tag_short = CharFilter(
343+
field_name="observation__origin_docker_image_name_tag_short",
344+
lookup_expr="icontains",
345+
)
346+
origin_endpoint_hostname = CharFilter(
347+
field_name="observation__origin_endpoint_hostname", lookup_expr="icontains"
348+
)
349+
origin_source_file = CharFilter(
350+
field_name="observation__origin_source_file", lookup_expr="icontains"
351+
)
352+
origin_cloud_qualified_resource = CharFilter(
353+
field_name="observation__origin_cloud_qualified_resource",
354+
lookup_expr="icontains",
355+
)
356+
origin_kubernetes_qualified_resource = CharFilter(
357+
field_name="observation__origin_kubernetes_qualified_resource",
358+
lookup_expr="icontains",
359+
)
342360

343361
ordering = OrderingFilter(
344362
# tuple-mapping retains order
345363
fields=(
346364
("id", "id"),
347365
("user__full_name", "user_full_name"),
348-
("observation__product__name", "product_name"),
349-
("observation__branch__name", "branch_name"),
350-
("observation__origin_component_name_version", "origin_component_name_version"),
351-
("product__product_group__name", "product.product_group_name"),
352-
("observation__title", "observation"),
366+
("observation__product__name", "observation_data.product_data.name"),
367+
(
368+
"observation__product__product_group__name",
369+
"observation_data.product_data.product_group_name",
370+
),
371+
("observation__branch__name", "observation_data.branch_name"),
372+
("observation__title", "observation_data.title"),
373+
(
374+
"observation__origin_component_name_version",
375+
"observation_data.origin_component_name_version",
376+
),
377+
(
378+
"observation__origin_docker_image_name_tag_short",
379+
"observation_data.origin_docker_image_name_tag_short",
380+
),
381+
(
382+
"observation__origin_endpoint_hostname",
383+
"observation_data.origin_endpoint_hostname",
384+
),
385+
("observation__origin_source_file", "observation_data.origin_source_file"),
386+
(
387+
"observation__origin_cloud_qualified_resource",
388+
"observation_data.origin_cloud_qualified_resource",
389+
),
390+
(
391+
"observation__origin_kubernetes_qualified_resource",
392+
"observation_data.origin_kubernetes_qualified_resource",
393+
),
353394
("severity", "severity"),
354395
("status", "status"),
355396
("comment", "comment"),

backend/application/core/api/serializers_observation.py

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ def update(self, instance: Observation, validated_data: dict):
301301
actual_status = instance.current_status
302302
actual_vex_justification = instance.current_vex_justification
303303
actual_vex_remediations = instance.current_vex_remediations
304+
actual_risk_acceptance_expiry_date = instance.risk_acceptance_expiry_date
304305

305306
instance.origin_component_name = ""
306307
instance.origin_component_version = ""
@@ -317,36 +318,52 @@ def update(self, instance: Observation, validated_data: dict):
317318

318319
observation: Observation = super().update(instance, validated_data)
319320

320-
if actual_severity != observation.current_severity:
321-
actual_severity = observation.current_severity
322-
else:
323-
actual_severity = ""
321+
log_severity = (
322+
observation.current_severity
323+
if actual_severity != observation.current_severity
324+
else ""
325+
)
324326

325-
if actual_status != observation.current_status:
326-
actual_status = observation.current_status
327-
else:
328-
actual_status = ""
327+
log_status = (
328+
observation.current_status
329+
if actual_status != observation.current_status
330+
else ""
331+
)
329332

330-
if actual_vex_justification != observation.current_vex_justification:
331-
actual_vex_justification = observation.current_vex_justification
332-
else:
333-
actual_vex_justification = ""
333+
log_vex_justification = (
334+
observation.current_vex_justification
335+
if actual_vex_justification != observation.current_vex_justification
336+
else ""
337+
)
334338

335339
if actual_vex_remediations != observation.current_vex_remediations:
336340
actual_vex_remediations = observation.current_vex_remediations
337341
else:
338342
actual_vex_remediations = ""
339343

340-
if actual_severity or actual_status:
344+
log_risk_acceptance_expiry_date = (
345+
observation.risk_acceptance_expiry_date
346+
if actual_risk_acceptance_expiry_date
347+
!= observation.risk_acceptance_expiry_date
348+
else None
349+
)
350+
351+
if (
352+
log_severity
353+
or log_status
354+
or log_vex_justification
355+
or log_risk_acceptance_expiry_date
356+
or actual_vex_remediations
357+
):
341358
create_observation_log(
342359
observation=observation,
343-
severity=actual_severity,
344-
status=actual_status,
360+
severity=log_severity,
361+
status=log_status,
345362
comment="Observation changed manually",
346363
vex_justification=actual_vex_justification,
347364
vex_remediations=actual_vex_remediations,
348365
assessment_status=Assessment_Status.ASSESSMENT_STATUS_AUTO_APPROVED,
349-
risk_acceptance_expiry_date=observation.risk_acceptance_expiry_date,
366+
risk_acceptance_expiry_date=log_risk_acceptance_expiry_date,
350367
)
351368

352369
check_security_gate(observation.product)
@@ -577,10 +594,7 @@ class Meta:
577594

578595

579596
class ObservationLogListSerializer(ModelSerializer):
580-
observation_title = SerializerMethodField()
581-
product_name = SerializerMethodField()
582-
branch_name = SerializerMethodField()
583-
origin_component_name_version = SerializerMethodField()
597+
observation_data = ObservationListSerializer(source="observation")
584598
user_full_name = SerializerMethodField()
585599
approval_user_full_name = SerializerMethodField()
586600

@@ -590,9 +604,6 @@ def get_user_full_name(self, obj: Observation_Log) -> Optional[str]:
590604

591605
return None
592606

593-
def get_observation_title(self, obj: Observation_Log) -> str:
594-
return obj.observation.title
595-
596607
def get_approval_user_full_name(self, obj: Observation_Log) -> Optional[str]:
597608
if obj.approval_user:
598609
return obj.approval_user.full_name
@@ -642,3 +653,7 @@ class PotentialDuplicateSerializer(ModelSerializer):
642653
class Meta:
643654
model = Potential_Duplicate
644655
fields = "__all__"
656+
657+
658+
class CountSerializer(Serializer):
659+
count = IntegerField()

backend/application/core/api/views.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from rest_framework.permissions import IsAuthenticated
1313
from rest_framework.request import Request
1414
from rest_framework.response import Response
15-
from rest_framework.status import HTTP_204_NO_CONTENT
15+
from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT
1616
from rest_framework.viewsets import GenericViewSet, ModelViewSet
1717

1818
from application.access_control.services.authorization import user_has_permission_or_403
@@ -40,6 +40,7 @@
4040
UserHasServicePermission,
4141
)
4242
from application.core.api.serializers_observation import (
43+
CountSerializer,
4344
EvidenceSerializer,
4445
ObservationAssessmentSerializer,
4546
ObservationBulkAssessmentSerializer,
@@ -643,6 +644,18 @@ def bulk_assessment(self, request):
643644
)
644645
return Response(status=HTTP_204_NO_CONTENT)
645646

647+
@extend_schema(
648+
methods=["GET"],
649+
request=None,
650+
responses={HTTP_200_OK: CountSerializer},
651+
)
652+
@action(detail=False, methods=["get"])
653+
def count_reviews(self, request):
654+
count = (
655+
get_observations().filter(current_status=Status.STATUS_IN_REVIEW).count()
656+
)
657+
return Response(status=HTTP_200_OK, data={"count": count})
658+
646659

647660
class ObservationTitleViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin):
648661
serializer_class = ObservationTitleSerializer
@@ -701,11 +714,11 @@ def approval(self, request, pk=None):
701714
return Response()
702715

703716
@extend_schema(
704-
methods=["PATCH"],
717+
methods=["POST"],
705718
request=ObservationLogBulkApprovalSerializer,
706719
responses={HTTP_204_NO_CONTENT: None},
707720
)
708-
@action(detail=False, methods=["patch"])
721+
@action(detail=False, methods=["post"])
709722
def bulk_approval(self, request):
710723
request_serializer = ObservationLogBulkApprovalSerializer(data=request.data)
711724
if not request_serializer.is_valid():
@@ -735,6 +748,22 @@ def bulk_delete(self, request):
735748
).delete()
736749
return Response(status=HTTP_204_NO_CONTENT)
737750

751+
@extend_schema(
752+
methods=["GET"],
753+
request=None,
754+
responses={HTTP_200_OK: CountSerializer},
755+
)
756+
@action(detail=False, methods=["get"])
757+
def count_approvals(self, request):
758+
count = (
759+
get_observation_logs()
760+
.filter(
761+
assessment_status=Assessment_Status.ASSESSMENT_STATUS_NEEDS_APPROVAL
762+
)
763+
.count()
764+
)
765+
return Response(status=HTTP_200_OK, data={"count": count})
766+
738767

739768
class EvidenceViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin):
740769
serializer_class = EvidenceSerializer

0 commit comments

Comments
 (0)