Skip to content

Commit 3db2dff

Browse files
committed
re-evaluate or clean up on change
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent 326f7ff commit 3db2dff

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

vulnerabilities/triage/api.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from dje.api import DataspacedSerializer
1515
from dje.api import ExtraPermissionsViewSetMixin
1616
from dje.api_custom import TabPermission
17+
from vulnerabilities.triage.engine import evaluate_ruleset
1718
from vulnerabilities.triage.models import AnalysisPreset
1819
from vulnerabilities.triage.models import TriageRuleset
1920

@@ -137,3 +138,14 @@ class TriageRulesetViewSet(
137138

138139
def get_queryset(self):
139140
return super().get_queryset().select_related("analysis_preset", "request_template")
141+
142+
def perform_update(self, serializer):
143+
"""Mirror TriageRulesetAdmin.save_model(): re-evaluate or clean up on change."""
144+
ruleset = serializer.save()
145+
146+
if not ruleset.enabled:
147+
ruleset.triage_records.filter(request__isnull=True).delete()
148+
return
149+
150+
for assignment in ruleset.product_triage_rulesets.select_related("product"):
151+
evaluate_ruleset(ruleset=ruleset, product=assignment.product)

vulnerabilities/triage/tests/test_api.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,20 @@
1212

1313
from rest_framework import status
1414

15+
from component_catalog.tests import make_package
1516
from dje.models import Dataspace
1617
from dje.tests import create_superuser
1718
from product_portfolio.models import Product
19+
from product_portfolio.tests import make_product
20+
from product_portfolio.tests import make_product_package
21+
from vulnerabilities.tests import make_vulnerability
22+
from vulnerabilities.triage.engine import evaluate_ruleset
1823
from vulnerabilities.triage.models import AnalysisPreset
1924
from vulnerabilities.triage.models import TriageAction
25+
from vulnerabilities.triage.models import TriageRecord
2026
from vulnerabilities.triage.models import TriageRuleset
2127
from vulnerabilities.triage.tests import make_analysis_preset
28+
from vulnerabilities.triage.tests import make_product_triage_ruleset
2229
from vulnerabilities.triage.tests import make_triage_ruleset
2330
from workflow.models import RequestTemplate
2431

@@ -165,6 +172,42 @@ def test_api_triageruleset_endpoint_update(self):
165172
self.ruleset.refresh_from_db()
166173
self.assertFalse(self.ruleset.enabled)
167174

175+
def test_api_triageruleset_endpoint_update_reevaluates_assigned_products(self):
176+
# Regression: editing a ruleset via the API must re-evaluate its assigned
177+
# products, matching what already happens when editing it in the Admin.
178+
self.client.login(username="super_user", password="secret")
179+
product = make_product(self.dataspace)
180+
package = make_package(self.dataspace)
181+
make_product_package(product, package=package)
182+
vulnerability = make_vulnerability(self.dataspace, affecting=package, risk_score=9.0)
183+
make_product_triage_ruleset(product, ruleset=self.ruleset)
184+
self.assertFalse(TriageRecord.objects.exists())
185+
186+
data = {"rules_config": {"risk_score": {"is_active": True, "min_risk_score": 8.0}}}
187+
response = self.client.patch(self.detail_url, data=data, content_type="application/json")
188+
189+
self.assertEqual(status.HTTP_200_OK, response.status_code)
190+
record = TriageRecord.objects.get()
191+
self.assertEqual(vulnerability, record.vulnerability)
192+
193+
def test_api_triageruleset_endpoint_disabling_deletes_its_triage_records(self):
194+
self.client.login(username="super_user", password="secret")
195+
product = make_product(self.dataspace)
196+
package = make_package(self.dataspace)
197+
make_product_package(product, package=package)
198+
make_vulnerability(self.dataspace, affecting=package, risk_score=9.0)
199+
self.ruleset.rules_config = {"risk_score": {"is_active": True, "min_risk_score": 8.0}}
200+
self.ruleset.save()
201+
make_product_triage_ruleset(product, ruleset=self.ruleset)
202+
evaluate_ruleset(self.ruleset, product)
203+
self.assertTrue(TriageRecord.objects.exists())
204+
205+
data = {"enabled": False}
206+
response = self.client.patch(self.detail_url, data=data, content_type="application/json")
207+
208+
self.assertEqual(status.HTTP_200_OK, response.status_code)
209+
self.assertFalse(TriageRecord.objects.exists())
210+
168211
def test_api_triageruleset_endpoint_delete(self):
169212
self.client.login(username="super_user", password="secret")
170213
response = self.client.delete(self.detail_url)

0 commit comments

Comments
 (0)