@@ -3429,6 +3429,95 @@ def test_create_not_authorized_product_name_engagement_name_scan_type_title(self
34293429 importer_mock .assert_not_called ()
34303430 reimporter_mock .assert_not_called ()
34313431
3432+ # Security tests: verify that conflicting ID-based and name-based identifiers are rejected
3433+
3434+ @patch ("dojo.importers.default_reimporter.DefaultReImporter.process_scan" )
3435+ @patch ("dojo.importers.default_importer.DefaultImporter.process_scan" )
3436+ @patch ("dojo.api_v2.permissions.user_has_permission" )
3437+ def test_reimport_engagement_param_ignored_permission_checked_on_name_resolved_target (self , mock , importer_mock , reimporter_mock ):
3438+ """
3439+ Engagement is not a declared field on ReImportScanSerializer — verify
3440+ the permission check uses the name-resolved target, not the engagement param.
3441+ """
3442+ mock .return_value = False
3443+ importer_mock .return_value = IMPORTER_MOCK_RETURN_VALUE
3444+ reimporter_mock .return_value = REIMPORTER_MOCK_RETURN_VALUE
3445+
3446+ with Path ("tests/zap_sample.xml" ).open (encoding = "utf-8" ) as testfile :
3447+ payload = {
3448+ "minimum_severity" : "Low" ,
3449+ "active" : True ,
3450+ "verified" : True ,
3451+ "scan_type" : "ZAP Scan" ,
3452+ "file" : testfile ,
3453+ # engagement=1 belongs to Product 2 Engagement 1, but it should be ignored
3454+ "engagement" : 1 ,
3455+ # These names resolve to Product 2's Engagement 4 -> Test 4
3456+ "product_name" : "Security How-to" ,
3457+ "engagement_name" : "April monthly engagement" ,
3458+ "version" : "1.0.0" ,
3459+ }
3460+ response = self .client .post (self .url , payload )
3461+ self .assertEqual (403 , response .status_code , response .content [:1000 ])
3462+ # Permission must be checked on name-resolved Test 4 (in Engagement 4),
3463+ # NOT on Test 3 (which belongs to the engagement=1 param)
3464+ mock .assert_called_with (User .objects .get (username = "admin" ),
3465+ Test .objects .get (id = 4 ),
3466+ Permissions .Import_Scan_Result )
3467+ importer_mock .assert_not_called ()
3468+ reimporter_mock .assert_not_called ()
3469+
3470+ @patch ("dojo.importers.default_reimporter.DefaultReImporter.process_scan" )
3471+ @patch ("dojo.importers.default_importer.DefaultImporter.process_scan" )
3472+ def test_reimport_with_test_id_mismatched_product_name_is_rejected (self , importer_mock , reimporter_mock ):
3473+ """Sending test ID from one product with product_name from another must be rejected."""
3474+ importer_mock .return_value = IMPORTER_MOCK_RETURN_VALUE
3475+ reimporter_mock .return_value = REIMPORTER_MOCK_RETURN_VALUE
3476+
3477+ with Path ("tests/zap_sample.xml" ).open (encoding = "utf-8" ) as testfile :
3478+ payload = {
3479+ "minimum_severity" : "Low" ,
3480+ "active" : True ,
3481+ "verified" : True ,
3482+ "scan_type" : "ZAP Scan" ,
3483+ "file" : testfile ,
3484+ # Test 3 belongs to Engagement 1 -> Product 2 ("Security How-to")
3485+ "test" : 3 ,
3486+ # But product_name points to Product 1 ("Python How-to")
3487+ "product_name" : "Python How-to" ,
3488+ "version" : "1.0.0" ,
3489+ }
3490+ response = self .client .post (self .url , payload )
3491+ self .assertEqual (400 , response .status_code , response .content [:1000 ])
3492+ importer_mock .assert_not_called ()
3493+ reimporter_mock .assert_not_called ()
3494+
3495+ @patch ("dojo.importers.default_reimporter.DefaultReImporter.process_scan" )
3496+ @patch ("dojo.importers.default_importer.DefaultImporter.process_scan" )
3497+ def test_reimport_with_test_id_mismatched_engagement_name_is_rejected (self , importer_mock , reimporter_mock ):
3498+ """Sending test ID from one engagement with engagement_name from another must be rejected."""
3499+ importer_mock .return_value = IMPORTER_MOCK_RETURN_VALUE
3500+ reimporter_mock .return_value = REIMPORTER_MOCK_RETURN_VALUE
3501+
3502+ with Path ("tests/zap_sample.xml" ).open (encoding = "utf-8" ) as testfile :
3503+ payload = {
3504+ "minimum_severity" : "Low" ,
3505+ "active" : True ,
3506+ "verified" : True ,
3507+ "scan_type" : "ZAP Scan" ,
3508+ "file" : testfile ,
3509+ # Test 3 belongs to Engagement 1 ("1st Quarter Engagement")
3510+ "test" : 3 ,
3511+ # But engagement_name points to a different engagement
3512+ "product_name" : "Security How-to" ,
3513+ "engagement_name" : "April monthly engagement" ,
3514+ "version" : "1.0.0" ,
3515+ }
3516+ response = self .client .post (self .url , payload )
3517+ self .assertEqual (400 , response .status_code , response .content [:1000 ])
3518+ importer_mock .assert_not_called ()
3519+ reimporter_mock .assert_not_called ()
3520+
34323521
34333522@versioned_fixtures
34343523class ProductTypeTest (BaseClass .BaseClassTest ):
0 commit comments