@@ -1636,3 +1636,60 @@ func TestSendWaiverRequests(t *testing.T) {
16361636 })
16371637 }
16381638}
1639+
1640+ // TestFetchNodesStatusConcurrentMapWrite reproduces crash
1641+ // reported when many packages are blocked by curation simultaneously.
1642+ func TestFetchNodesStatusConcurrentMapWrite (t * testing.T ) {
1643+ const numNodes = 50
1644+
1645+ // Mock server: HEAD returns 403 for all packages, GET returns curation block JSON
1646+ blockResponse := `{"errors":[{"status":403,"message":"Package download was blocked by JFrog Packages Curation service due to the following policies violated {testPolicy, testCondition, testExplanation, testRecommendation}"}]}`
1647+ serverMock , _ , rtManager := coreCommonTests .CreateRtRestsMockServer (t , func (w http.ResponseWriter , r * http.Request ) {
1648+ if r .Method == http .MethodHead {
1649+ w .WriteHeader (http .StatusForbidden )
1650+ return
1651+ }
1652+ if r .Method == http .MethodGet {
1653+ w .WriteHeader (http .StatusForbidden )
1654+ _ , _ = w .Write ([]byte (blockResponse ))
1655+ return
1656+ }
1657+ })
1658+ defer serverMock .Close ()
1659+
1660+ rtAuth := rtManager .GetConfig ().GetServiceDetails ()
1661+ httpClientDetails := rtAuth .CreateHttpClientDetails ()
1662+
1663+ root := & xrayUtils.GraphNode {Id : "npm://root:1.0.0" }
1664+ for i := 0 ; i < numNodes ; i ++ {
1665+ root .Nodes = append (root .Nodes , & xrayUtils.GraphNode {
1666+ Id : fmt .Sprintf ("npm://pkg-%d:%d.0.0" , i , i ),
1667+ })
1668+ }
1669+
1670+ analyzer := treeAnalyzer {
1671+ rtManager : rtManager ,
1672+ extractPoliciesRegex : regexp .MustCompile (extractPoliciesRegexTemplate ),
1673+ rtAuth : rtAuth ,
1674+ httpClientDetails : httpClientDetails ,
1675+ url : rtAuth .GetUrl (),
1676+ repo : "npm-remote" ,
1677+ tech : techutils .Npm ,
1678+ parallelRequests : 10 ,
1679+ }
1680+
1681+ packagesStatusMap := sync.Map {}
1682+ rootNodes := map [string ]struct {}{root .Id : {}}
1683+
1684+ // This will crash with "concurrent map writes" without the fix
1685+ err := analyzer .fetchNodesStatus (root , & packagesStatusMap , rootNodes )
1686+ assert .NoError (t , err )
1687+
1688+ // Verify all blocked packages were recorded
1689+ count := 0
1690+ packagesStatusMap .Range (func (_ , _ any ) bool {
1691+ count ++
1692+ return true
1693+ })
1694+ assert .Equal (t , numNodes , count , "expected all %d packages to be recorded as blocked" , numNodes )
1695+ }
0 commit comments