Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,4 @@ docs/.hugo_build.lock

# claude etc
MEMORY.md
.claude/
30 changes: 0 additions & 30 deletions dojo/api_v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@
SLA_Configuration,
Sonarqube_Issue,
Sonarqube_Issue_Transition,
Stub_Finding,
System_Settings,
Test,
Test_Import,
Expand Down Expand Up @@ -2166,35 +2165,6 @@ class Meta:
fields = "__all__"


class StubFindingSerializer(serializers.ModelSerializer):
class Meta:
model = Stub_Finding
fields = "__all__"

def validate_severity(self, value: str) -> str:
if value not in SEVERITIES:
msg = f"Severity must be one of the following: {SEVERITIES}"
raise serializers.ValidationError(msg)
return value


class StubFindingCreateSerializer(serializers.ModelSerializer):
test = serializers.PrimaryKeyRelatedField(queryset=Test.objects.all())

class Meta:
model = Stub_Finding
fields = "__all__"
extra_kwargs = {
"reporter": {"default": serializers.CurrentUserDefault()},
}

def validate_severity(self, value: str) -> str:
if value not in SEVERITIES:
msg = f"Severity must be one of the following: {SEVERITIES}"
raise serializers.ValidationError(msg)
return value


class ProductSerializer(serializers.ModelSerializer):
findings_count = serializers.SerializerMethodField()
findings_list = serializers.SerializerMethodField()
Expand Down
73 changes: 0 additions & 73 deletions dojo/api_v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
)
from dojo.finding.queries import (
get_authorized_findings,
get_authorized_stub_findings,
)
from dojo.finding.views import (
duplicate_cluster,
Expand Down Expand Up @@ -133,7 +132,6 @@
SLA_Configuration,
Sonarqube_Issue,
Sonarqube_Issue_Transition,
Stub_Finding,
System_Settings,
Test,
Test_Import,
Expand Down Expand Up @@ -2218,77 +2216,6 @@ def partial_update(self, request, pk=None):
return Response(response, status=status.HTTP_405_METHOD_NOT_ALLOWED)


# Authorization: object-based
# @extend_schema_view(**schema_with_prefetch())
# Nested models with prefetch make the response schema too long for Swagger UI
class StubFindingsViewSet(
PrefetchDojoModelViewSet,
DeprecationNoticeMixin,
):
deprecated = True
end_of_life_date = datetime(2026, 6, 1)
serializer_class = serializers.StubFindingSerializer
queryset = Stub_Finding.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "title", "date", "severity", "description"]
permission_classes = (
IsAuthenticated,
permissions.UserHasFindingPermission,
)

def get_queryset(self):
return get_authorized_stub_findings(
Permissions.Finding_View,
).distinct()

def get_serializer_class(self):
if self.request and self.request.method == "POST":
return serializers.StubFindingCreateSerializer
return serializers.StubFindingSerializer

@extend_schema(
deprecated=True,
description="This endpoint is deprecated and will be removed on 2026-06-01.",
)
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)

@extend_schema(
deprecated=True,
description="This endpoint is deprecated and will be removed on 2026-06-01.",
)
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)

@extend_schema(
deprecated=True,
description="This endpoint is deprecated and will be removed on 2026-06-01.",
)
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)

@extend_schema(
deprecated=True,
description="This endpoint is deprecated and will be removed on 2026-06-01.",
)
def update(self, request, *args, **kwargs):
return super().update(request, *args, **kwargs)

@extend_schema(
deprecated=True,
description="This endpoint is deprecated and will be removed on 2026-06-01.",
)
def partial_update(self, request, *args, **kwargs):
return super().partial_update(request, *args, **kwargs)

@extend_schema(
deprecated=True,
description="This endpoint is deprecated and will be removed on 2026-06-01.",
)
def destroy(self, request, *args, **kwargs):
return super().destroy(request, *args, **kwargs)


# Authorization: authenticated, configuration
class DevelopmentEnvironmentViewSet(
DojoModelViewSet,
Expand Down
7 changes: 3 additions & 4 deletions dojo/authorization/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
Product_Type_Group,
Product_Type_Member,
Risk_Acceptance,
Stub_Finding,
Test,
)
from dojo.request_cache import cache_for_request
Expand Down Expand Up @@ -135,9 +134,9 @@ def user_has_permission(user: Dojo_User, obj: Model, permission: int) -> bool:
if obj.engagement is not None:
return user_has_permission(user, obj.engagement.product, permission)
return user_has_global_permission(user, permission)
if ((
isinstance(obj, Finding | Stub_Finding)
) and permission in Permissions.get_finding_permissions()) or (
if (
isinstance(obj, Finding) and permission in Permissions.get_finding_permissions()
) or (
isinstance(obj, Finding_Group)
and permission in Permissions.get_finding_group_permissions()
):
Expand Down
25 changes: 25 additions & 0 deletions dojo/db_migrations/0265_remove_stub_finding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Remove the Stub Findings feature.

Drops the ``Stub_Finding`` model. Stub Findings was deprecated in 2.57.0 and
is end-of-life in 2.59. The model has no inbound foreign keys, so the
deletion is self-contained.

Note: rebase the filename and the ``dependencies`` tuple to point at
whatever the latest migration is at merge time if another migration has
landed first.
"""

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("dojo", "0264_alter_url_identity_hash_alter_urlevent_identity_hash"),
]

operations = [
migrations.DeleteModel(
name="Stub_Finding",
),
]
43 changes: 0 additions & 43 deletions dojo/finding/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
Product_Member,
Product_Type_Group,
Product_Type_Member,
Stub_Finding,
Test_Import_Finding_Action,
Vulnerability_Id,
)
Expand Down Expand Up @@ -112,48 +111,6 @@ def get_authorized_findings_for_queryset(permission, queryset, user=None):
)


# Cached: all parameters are hashable, no dynamic queryset filtering
@cache_for_request
def get_authorized_stub_findings(permission):
user = get_current_user()

if user is None:
return Stub_Finding.objects.none()

if user.is_superuser:
return Stub_Finding.objects.all().order_by("id")

if user_has_global_permission(user, permission):
return Stub_Finding.objects.all().order_by("id")

roles = get_roles_for_permission(permission)

# Get authorized product/product_type IDs via subqueries
authorized_product_type_roles = Product_Type_Member.objects.filter(
user=user, role__in=roles,
).values("product_type_id")

authorized_product_roles = Product_Member.objects.filter(
user=user, role__in=roles,
).values("product_id")

authorized_product_type_groups = Product_Type_Group.objects.filter(
group__users=user, role__in=roles,
).values("product_type_id")

authorized_product_groups = Product_Group.objects.filter(
group__users=user, role__in=roles,
).values("product_id")

# Filter using IN with Subquery - no annotations needed
return Stub_Finding.objects.filter(
Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles))
| Q(test__engagement__product_id__in=Subquery(authorized_product_roles))
| Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups))
| Q(test__engagement__product_id__in=Subquery(authorized_product_groups)),
).order_by("id")


# Cached: all parameters are hashable, no dynamic queryset filtering
@cache_for_request
def get_authorized_vulnerability_ids(permission, user=None):
Expand Down
7 changes: 0 additions & 7 deletions dojo/finding/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,6 @@
views.set_finding_as_original, name="set_finding_as_original"),
re_path(r"^finding/(?P<fid>\d+)/remediation_date$", views.remediation_date,
name="remediation_date"),
# stub findings
re_path(r"^stub_finding/(?P<tid>\d+)/add$",
views.add_stub_finding, name="add_stub_finding"),
re_path(r"^stub_finding/(?P<fid>\d+)/promote$",
views.promote_to_finding, name="promote_to_finding"),
re_path(r"^stub_finding/(?P<fid>\d+)/delete$",
views.delete_stub_finding, name="delete_stub_finding"),

# template findings

Expand Down
Loading
Loading