Skip to content

Commit dfbd7c0

Browse files
Maffoochclaude
andcommitted
remove: Stub Findings (announced 2.57, EOL in 2.59)
Per the 2.59 release notes, retires the Stub Findings feature in its entirety: UI, API, model, and DB table. Stub_Finding has no inbound foreign keys, so the deletion is self-contained. Endpoint removed (now `404`): - /api/v2/stub_findings/ UI removed: - /finding/<id>/promote, /stub_finding/<id>/add, /stub_finding/<id>/delete - "Potential Findings" table on the test detail page (view_test.html) - The quick-add-form JS handler that powered it - The promote_to_finding.html template Code deleted: - `StubFindingsViewSet`, `StubFindingSerializer`, `StubFindingCreateSerializer` - `add_stub_finding`, `delete_stub_finding`, `promote_to_finding` views - `StubFindingForm`, `DeleteStubFindingForm` - `get_authorized_stub_findings` query helper - `get_stub_findings` method and call site in `dojo/test/views.py` - `Stub_Finding` admin registration and model class - The `Stub_Finding` branch in `dojo/authorization/authorization.py` (now just `Finding` instead of `Finding | Stub_Finding`) - The `Stub_Finding` early-return and union check in `dojo/jira/helper.py` - Unit tests: `StubFindingsTest` (REST), `TestGetAuthorizedStubFindings`, the two `test_user_has_permission_stub_finding_*` tests, and the three Selenium tests in `tests/test_test.py` - Dead `#stub_findings` JS in `view_objects.html` / `view_objects_eng.html` Schema dropped via 0265_remove_stub_finding: - `DeleteModel('Stub_Finding')` The 2.59 upgrade doc already documents the removal; no doc update. Note: PR 2 also adds a 0265_* migration. Whichever PR merges second must rebase the migration filename and `dependencies` tuple accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5deca76 commit dfbd7c0

20 files changed

Lines changed: 29 additions & 955 deletions

dojo/api_v2/serializers.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@
9494
SLA_Configuration,
9595
Sonarqube_Issue,
9696
Sonarqube_Issue_Transition,
97-
Stub_Finding,
9897
System_Settings,
9998
Test,
10099
Test_Import,
@@ -2166,35 +2165,6 @@ class Meta:
21662165
fields = "__all__"
21672166

21682167

2169-
class StubFindingSerializer(serializers.ModelSerializer):
2170-
class Meta:
2171-
model = Stub_Finding
2172-
fields = "__all__"
2173-
2174-
def validate_severity(self, value: str) -> str:
2175-
if value not in SEVERITIES:
2176-
msg = f"Severity must be one of the following: {SEVERITIES}"
2177-
raise serializers.ValidationError(msg)
2178-
return value
2179-
2180-
2181-
class StubFindingCreateSerializer(serializers.ModelSerializer):
2182-
test = serializers.PrimaryKeyRelatedField(queryset=Test.objects.all())
2183-
2184-
class Meta:
2185-
model = Stub_Finding
2186-
fields = "__all__"
2187-
extra_kwargs = {
2188-
"reporter": {"default": serializers.CurrentUserDefault()},
2189-
}
2190-
2191-
def validate_severity(self, value: str) -> str:
2192-
if value not in SEVERITIES:
2193-
msg = f"Severity must be one of the following: {SEVERITIES}"
2194-
raise serializers.ValidationError(msg)
2195-
return value
2196-
2197-
21982168
class ProductSerializer(serializers.ModelSerializer):
21992169
findings_count = serializers.SerializerMethodField()
22002170
findings_list = serializers.SerializerMethodField()

dojo/api_v2/views.py

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@
7575
)
7676
from dojo.finding.queries import (
7777
get_authorized_findings,
78-
get_authorized_stub_findings,
7978
)
8079
from dojo.finding.views import (
8180
duplicate_cluster,
@@ -133,7 +132,6 @@
133132
SLA_Configuration,
134133
Sonarqube_Issue,
135134
Sonarqube_Issue_Transition,
136-
Stub_Finding,
137135
System_Settings,
138136
Test,
139137
Test_Import,
@@ -2218,77 +2216,6 @@ def partial_update(self, request, pk=None):
22182216
return Response(response, status=status.HTTP_405_METHOD_NOT_ALLOWED)
22192217

22202218

2221-
# Authorization: object-based
2222-
# @extend_schema_view(**schema_with_prefetch())
2223-
# Nested models with prefetch make the response schema too long for Swagger UI
2224-
class StubFindingsViewSet(
2225-
PrefetchDojoModelViewSet,
2226-
DeprecationNoticeMixin,
2227-
):
2228-
deprecated = True
2229-
end_of_life_date = datetime(2026, 6, 1)
2230-
serializer_class = serializers.StubFindingSerializer
2231-
queryset = Stub_Finding.objects.none()
2232-
filter_backends = (DjangoFilterBackend,)
2233-
filterset_fields = ["id", "title", "date", "severity", "description"]
2234-
permission_classes = (
2235-
IsAuthenticated,
2236-
permissions.UserHasFindingPermission,
2237-
)
2238-
2239-
def get_queryset(self):
2240-
return get_authorized_stub_findings(
2241-
Permissions.Finding_View,
2242-
).distinct()
2243-
2244-
def get_serializer_class(self):
2245-
if self.request and self.request.method == "POST":
2246-
return serializers.StubFindingCreateSerializer
2247-
return serializers.StubFindingSerializer
2248-
2249-
@extend_schema(
2250-
deprecated=True,
2251-
description="This endpoint is deprecated and will be removed on 2026-06-01.",
2252-
)
2253-
def list(self, request, *args, **kwargs):
2254-
return super().list(request, *args, **kwargs)
2255-
2256-
@extend_schema(
2257-
deprecated=True,
2258-
description="This endpoint is deprecated and will be removed on 2026-06-01.",
2259-
)
2260-
def retrieve(self, request, *args, **kwargs):
2261-
return super().retrieve(request, *args, **kwargs)
2262-
2263-
@extend_schema(
2264-
deprecated=True,
2265-
description="This endpoint is deprecated and will be removed on 2026-06-01.",
2266-
)
2267-
def create(self, request, *args, **kwargs):
2268-
return super().create(request, *args, **kwargs)
2269-
2270-
@extend_schema(
2271-
deprecated=True,
2272-
description="This endpoint is deprecated and will be removed on 2026-06-01.",
2273-
)
2274-
def update(self, request, *args, **kwargs):
2275-
return super().update(request, *args, **kwargs)
2276-
2277-
@extend_schema(
2278-
deprecated=True,
2279-
description="This endpoint is deprecated and will be removed on 2026-06-01.",
2280-
)
2281-
def partial_update(self, request, *args, **kwargs):
2282-
return super().partial_update(request, *args, **kwargs)
2283-
2284-
@extend_schema(
2285-
deprecated=True,
2286-
description="This endpoint is deprecated and will be removed on 2026-06-01.",
2287-
)
2288-
def destroy(self, request, *args, **kwargs):
2289-
return super().destroy(request, *args, **kwargs)
2290-
2291-
22922219
# Authorization: authenticated, configuration
22932220
class DevelopmentEnvironmentViewSet(
22942221
DojoModelViewSet,

dojo/authorization/authorization.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
Product_Type_Group,
2828
Product_Type_Member,
2929
Risk_Acceptance,
30-
Stub_Finding,
3130
Test,
3231
)
3332
from dojo.request_cache import cache_for_request
@@ -135,9 +134,9 @@ def user_has_permission(user: Dojo_User, obj: Model, permission: int) -> bool:
135134
if obj.engagement is not None:
136135
return user_has_permission(user, obj.engagement.product, permission)
137136
return user_has_global_permission(user, permission)
138-
if ((
139-
isinstance(obj, Finding | Stub_Finding)
140-
) and permission in Permissions.get_finding_permissions()) or (
137+
if (
138+
isinstance(obj, Finding) and permission in Permissions.get_finding_permissions()
139+
) or (
141140
isinstance(obj, Finding_Group)
142141
and permission in Permissions.get_finding_group_permissions()
143142
):
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Remove the Stub Findings feature.
2+
3+
Drops the ``Stub_Finding`` model. Stub Findings was deprecated in 2.57.0 and
4+
is end-of-life in 2.59. The model has no inbound foreign keys, so the
5+
deletion is self-contained.
6+
7+
Note: rebase the filename and the ``dependencies`` tuple to point at
8+
whatever the latest migration is at merge time if another migration has
9+
landed first.
10+
"""
11+
12+
from django.db import migrations
13+
14+
15+
class Migration(migrations.Migration):
16+
17+
dependencies = [
18+
("dojo", "0264_alter_url_identity_hash_alter_urlevent_identity_hash"),
19+
]
20+
21+
operations = [
22+
migrations.DeleteModel(
23+
name="Stub_Finding",
24+
),
25+
]

dojo/finding/queries.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
Product_Member,
1818
Product_Type_Group,
1919
Product_Type_Member,
20-
Stub_Finding,
2120
Test_Import_Finding_Action,
2221
Vulnerability_Id,
2322
)
@@ -112,48 +111,6 @@ def get_authorized_findings_for_queryset(permission, queryset, user=None):
112111
)
113112

114113

115-
# Cached: all parameters are hashable, no dynamic queryset filtering
116-
@cache_for_request
117-
def get_authorized_stub_findings(permission):
118-
user = get_current_user()
119-
120-
if user is None:
121-
return Stub_Finding.objects.none()
122-
123-
if user.is_superuser:
124-
return Stub_Finding.objects.all().order_by("id")
125-
126-
if user_has_global_permission(user, permission):
127-
return Stub_Finding.objects.all().order_by("id")
128-
129-
roles = get_roles_for_permission(permission)
130-
131-
# Get authorized product/product_type IDs via subqueries
132-
authorized_product_type_roles = Product_Type_Member.objects.filter(
133-
user=user, role__in=roles,
134-
).values("product_type_id")
135-
136-
authorized_product_roles = Product_Member.objects.filter(
137-
user=user, role__in=roles,
138-
).values("product_id")
139-
140-
authorized_product_type_groups = Product_Type_Group.objects.filter(
141-
group__users=user, role__in=roles,
142-
).values("product_type_id")
143-
144-
authorized_product_groups = Product_Group.objects.filter(
145-
group__users=user, role__in=roles,
146-
).values("product_id")
147-
148-
# Filter using IN with Subquery - no annotations needed
149-
return Stub_Finding.objects.filter(
150-
Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles))
151-
| Q(test__engagement__product_id__in=Subquery(authorized_product_roles))
152-
| Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups))
153-
| Q(test__engagement__product_id__in=Subquery(authorized_product_groups)),
154-
).order_by("id")
155-
156-
157114
# Cached: all parameters are hashable, no dynamic queryset filtering
158115
@cache_for_request
159116
def get_authorized_vulnerability_ids(permission, user=None):

dojo/finding/urls.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,6 @@
164164
views.set_finding_as_original, name="set_finding_as_original"),
165165
re_path(r"^finding/(?P<fid>\d+)/remediation_date$", views.remediation_date,
166166
name="remediation_date"),
167-
# stub findings
168-
re_path(r"^stub_finding/(?P<tid>\d+)/add$",
169-
views.add_stub_finding, name="add_stub_finding"),
170-
re_path(r"^stub_finding/(?P<fid>\d+)/promote$",
171-
views.promote_to_finding, name="promote_to_finding"),
172-
re_path(r"^stub_finding/(?P<fid>\d+)/delete$",
173-
views.delete_stub_finding, name="delete_stub_finding"),
174167

175168
# template findings
176169

0 commit comments

Comments
 (0)