6464from vulnerabilities .models import VulnerabilityAnalysis
6565from vulnerabilities .tests import make_vulnerability
6666from vulnerabilities .tests import make_vulnerability_analysis
67+ from vulnerabilities .triage .models import AnalysisPreset
6768from vulnerabilities .triage .models import ProductTriageRuleset
6869from vulnerabilities .triage .models import TriageAction
6970from vulnerabilities .triage .models import TriageRuleset
@@ -1994,6 +1995,12 @@ def test_product_portfolio_product_update_save_as_new(self):
19941995 CodebaseResource .objects .create (
19951996 path = "/path1/" , product = self .product1 , dataspace = self .dataspace
19961997 )
1998+ ruleset = TriageRuleset .objects .create (
1999+ name = "Upgrade Ruleset" , precedence = 100 , dataspace = self .dataspace
2000+ )
2001+ ProductTriageRuleset .objects .create (
2002+ product = self .product1 , ruleset = ruleset , dataspace = self .dataspace
2003+ )
19972004 initial_product_count = Product .objects .get_queryset (self .super_user ).count ()
19982005
19992006 data = {
@@ -2015,6 +2022,7 @@ def test_product_portfolio_product_update_save_as_new(self):
20152022 self .assertEqual (1 , cloned_product .productcomponents .count ())
20162023 self .assertEqual (1 , cloned_product .productpackages .count ())
20172024 self .assertEqual (1 , cloned_product .codebaseresources .count ())
2025+ self .assertEqual (1 , cloned_product .product_triage_rulesets .count ())
20182026
20192027 def test_product_portfolio_product_delete_view (self ):
20202028 delete_url = self .product1 .get_delete_url ()
@@ -3423,6 +3431,54 @@ def test_product_portfolio_vulnerability_analysis_form_view(self):
34233431 self .assertEqual (vulnerability1 , analysis .vulnerability )
34243432 self .assertEqual ("resolved" , analysis .state )
34253433
3434+ def test_product_portfolio_apply_analysis_preset_view (self ):
3435+ self .client .login (username = self .super_user .username , password = "secret" )
3436+
3437+ package1 = make_package (self .dataspace )
3438+ vulnerability1 = make_vulnerability (self .dataspace , affecting = [package1 ])
3439+ product1 = make_product (self .dataspace , inventory = [package1 ])
3440+ product_package = ProductPackage .objects .get (product = product1 , package = package1 )
3441+ preset = AnalysisPreset .objects .create (
3442+ name = "Preset1" ,
3443+ state = "not_affected" ,
3444+ detail = "Not deployed" ,
3445+ dataspace = self .dataspace ,
3446+ )
3447+
3448+ url = reverse (
3449+ "product_portfolio:apply_analysis_preset" ,
3450+ args = [product_package .uuid , preset .pk , vulnerability1 .advisory_uid ],
3451+ )
3452+ response = self .client .post (url )
3453+ self .assertEqual (200 , response .status_code )
3454+ self .assertEqual (b'{"success": "applied"}' , response .content )
3455+ analysis = VulnerabilityAnalysis .objects .get ()
3456+ self .assertEqual (product_package , analysis .product_package )
3457+ self .assertEqual (vulnerability1 , analysis .vulnerability )
3458+ self .assertEqual ("not_affected" , analysis .state )
3459+ self .assertEqual (preset , analysis .applied_by_preset )
3460+
3461+ def test_product_portfolio_apply_analysis_preset_view_existing_analysis (self ):
3462+ self .client .login (username = self .super_user .username , password = "secret" )
3463+
3464+ package1 = make_package (self .dataspace )
3465+ vulnerability1 = make_vulnerability (self .dataspace , affecting = [package1 ])
3466+ product1 = make_product (self .dataspace , inventory = [package1 ])
3467+ product_package = ProductPackage .objects .get (product = product1 , package = package1 )
3468+ make_vulnerability_analysis (product_package , vulnerability1 , state = "exploitable" )
3469+ preset = AnalysisPreset .objects .create (
3470+ name = "Preset1" , state = "not_affected" , dataspace = self .dataspace
3471+ )
3472+
3473+ url = reverse (
3474+ "product_portfolio:apply_analysis_preset" ,
3475+ args = [product_package .uuid , preset .pk , vulnerability1 .advisory_uid ],
3476+ )
3477+ response = self .client .post (url )
3478+ self .assertEqual (400 , response .status_code )
3479+ self .assertEqual (1 , VulnerabilityAnalysis .objects .count ())
3480+ self .assertEqual ("exploitable" , VulnerabilityAnalysis .objects .get ().state )
3481+
34263482 def test_product_portfolio_tab_compliance_view_empty (self ):
34273483 self .client .login (username = "nexb_user" , password = "secret" )
34283484 url = self .product1 .get_url ("tab_compliance" )
@@ -4295,6 +4351,85 @@ def test_post_with_change_perm_evaluates_and_returns_hx_refresh(self, mock_evalu
42954351 mock_evaluate .assert_called_once_with (self .product1 )
42964352
42974353
4354+ class ManageTriageRulesetsViewTestCase (TestCase ):
4355+ def setUp (self ):
4356+ self .dataspace = Dataspace .objects .create (name = "nexB" )
4357+ self .super_user = create_superuser ("nexb_user" , self .dataspace )
4358+ self .basic_user = create_user ("basic_user" , self .dataspace )
4359+ self .product1 = Product .objects .create (
4360+ name = "Product1" , version = "1.0" , dataspace = self .dataspace
4361+ )
4362+ self .ruleset = TriageRuleset .objects .create (
4363+ name = "Upgrade Ruleset" ,
4364+ action = TriageAction .UPGRADE ,
4365+ precedence = 100 ,
4366+ dataspace = self .dataspace ,
4367+ )
4368+
4369+ def test_get_without_login_redirects (self ):
4370+ url = self .product1 .get_manage_triage_rulesets_url ()
4371+ response = self .client .get (url )
4372+ self .assertEqual (302 , response .status_code )
4373+
4374+ def test_get_without_change_perm_returns_404 (self ):
4375+ self .client .login (username = "basic_user" , password = "secret" )
4376+ url = self .product1 .get_manage_triage_rulesets_url ()
4377+ response = self .client .get (url )
4378+ self .assertEqual (404 , response .status_code )
4379+
4380+ def test_get_with_change_perm_renders_available_rulesets (self ):
4381+ self .client .login (username = "nexb_user" , password = "secret" )
4382+ url = self .product1 .get_manage_triage_rulesets_url ()
4383+ response = self .client .get (url )
4384+ self .assertEqual (200 , response .status_code )
4385+ self .assertContains (response , self .ruleset .name )
4386+ self .assertNotContains (response , "checked" )
4387+
4388+ def test_get_marks_the_assigned_rulesets_as_checked (self ):
4389+ ProductTriageRuleset .objects .create (
4390+ product = self .product1 , ruleset = self .ruleset , dataspace = self .dataspace
4391+ )
4392+ self .client .login (username = "nexb_user" , password = "secret" )
4393+ url = self .product1 .get_manage_triage_rulesets_url ()
4394+ response = self .client .get (url )
4395+ self .assertContains (response , "checked" )
4396+
4397+ def test_post_without_change_perm_returns_404 (self ):
4398+ self .client .login (username = "basic_user" , password = "secret" )
4399+ url = self .product1 .get_manage_triage_rulesets_url ()
4400+ response = self .client .post (url )
4401+ self .assertEqual (404 , response .status_code )
4402+
4403+ @patch ("product_portfolio.views.evaluate_ruleset" )
4404+ def test_post_assigns_the_submitted_rulesets (self , mock_evaluate ):
4405+ self .client .login (username = "nexb_user" , password = "secret" )
4406+ url = self .product1 .get_manage_triage_rulesets_url ()
4407+ response = self .client .post (url , {"ruleset_uuids" : [str (self .ruleset .uuid )]})
4408+ self .assertEqual (b'{"success": true}' , response .content )
4409+ self .assertTrue (
4410+ ProductTriageRuleset .objects .filter (
4411+ product = self .product1 , ruleset = self .ruleset
4412+ ).exists ()
4413+ )
4414+ mock_evaluate .assert_called_once_with (ruleset = self .ruleset , product = self .product1 )
4415+
4416+ @patch ("product_portfolio.views.evaluate_ruleset" )
4417+ def test_post_unassigns_the_deselected_rulesets (self , mock_evaluate ):
4418+ ProductTriageRuleset .objects .create (
4419+ product = self .product1 , ruleset = self .ruleset , dataspace = self .dataspace
4420+ )
4421+ self .client .login (username = "nexb_user" , password = "secret" )
4422+ url = self .product1 .get_manage_triage_rulesets_url ()
4423+ response = self .client .post (url , {"ruleset_uuids" : []})
4424+ self .assertEqual (b'{"success": true}' , response .content )
4425+ self .assertFalse (
4426+ ProductTriageRuleset .objects .filter (
4427+ product = self .product1 , ruleset = self .ruleset
4428+ ).exists ()
4429+ )
4430+ mock_evaluate .assert_not_called ()
4431+
4432+
42984433class TabCompliancePolicyContextTestCase (TestCase ):
42994434 def setUp (self ):
43004435 self .dataspace = Dataspace .objects .create (name = "nexB" )
0 commit comments