22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
44using System ;
5+ using System . Collections . Concurrent ;
56using System . Collections . Generic ;
6- using System . Threading ;
77using System . Threading . Tasks ;
8+ using System . Linq ;
89
910namespace NuGetGallery
1011{
1112 public class TyposquattingCheckService : ITyposquattingCheckService
1213 {
14+ // TODO: Length of checklist will be saved in the configuration file.
15+ // https://github.com/NuGet/Engineering/issues/1645
16+ private const int TyposquattingCheckListLength = 20000 ;
17+
1318 // TODO: Threshold parameters will be saved in the configuration file.
1419 // https://github.com/NuGet/Engineering/issues/1645
15- private static List < ThresholdInfo > _thresholdsList = new List < ThresholdInfo >
20+ private static readonly IReadOnlyList < ThresholdInfo > ThresholdsList = new List < ThresholdInfo >
1621 {
17- new ThresholdInfo { LowerBound = 0 , UpperBound = 30 , Threshold = 0 } ,
18- new ThresholdInfo { LowerBound = 30 , UpperBound = 50 , Threshold = 1 } ,
19- new ThresholdInfo { LowerBound = 50 , UpperBound = 120 , Threshold = 2 }
22+ new ThresholdInfo ( lowerBound : 0 , upperBound : 30 , threshold : 0 ) ,
23+ new ThresholdInfo ( lowerBound : 30 , upperBound : 50 , threshold : 1 ) ,
24+ new ThresholdInfo ( lowerBound : 50 , upperBound : 121 , threshold : 2 )
2025 } ;
21-
22- // TODO: popular packages checklist will be implemented
23- // https://github.com/NuGet/Engineering/issues/1624
24- public static List < PackageInfo > PackagesCheckList { get ; set ; }
2526
2627 private readonly ITyposquattingUserService _userTyposquattingService ;
28+ private readonly IEntityRepository < PackageRegistration > _packageRegistrationRepository ;
2729
28- public TyposquattingCheckService ( ITyposquattingUserService typosquattingUserService )
30+ public TyposquattingCheckService ( ITyposquattingUserService typosquattingUserService , IEntityRepository < PackageRegistration > packageRegistrationRepository )
2931 {
3032 _userTyposquattingService = typosquattingUserService ?? throw new ArgumentNullException ( nameof ( typosquattingUserService ) ) ;
33+ _packageRegistrationRepository = packageRegistrationRepository ?? throw new ArgumentNullException ( nameof ( packageRegistrationRepository ) ) ;
3134 }
32-
35+
3336 public bool IsUploadedPackageIdTyposquatting ( string uploadedPackageId , User uploadedPackageOwner )
3437 {
3538 if ( uploadedPackageId == null )
@@ -42,38 +45,42 @@ public bool IsUploadedPackageIdTyposquatting(string uploadedPackageId, User uplo
4245 throw new ArgumentNullException ( nameof ( uploadedPackageOwner ) ) ;
4346 }
4447
48+ var packagesCheckList = _packageRegistrationRepository . GetAll ( )
49+ . OrderByDescending ( pr => pr . IsVerified )
50+ . ThenByDescending ( pr => pr . DownloadCount )
51+ . Select ( pr => pr . Id )
52+ . Take ( TyposquattingCheckListLength )
53+ . ToList ( ) ;
54+
4555 var threshold = GetThreshold ( uploadedPackageId ) ;
4656 uploadedPackageId = TyposquattingStringNormalization . NormalizeString ( uploadedPackageId ) ;
4757
48- var countCollision = 0 ;
49- Parallel . ForEach ( PackagesCheckList , ( package , loopState ) =>
58+ var collisionPackageIds = new ConcurrentBag < string > ( ) ;
59+ Parallel . ForEach ( packagesCheckList , ( packageId , loopState ) =>
5060 {
5161 // TODO: handle the package which is owned by an organization.
5262 // https://github.com/NuGet/Engineering/issues/1656
53- if ( package . Owners . Contains ( uploadedPackageOwner . Username ) )
63+ string normalizedPackageId = TyposquattingStringNormalization . NormalizeString ( packageId ) ;
64+ if ( TyposquattingDistanceCalculation . IsDistanceLessThanThreshold ( uploadedPackageId , normalizedPackageId , threshold ) )
5465 {
55- return ;
66+ collisionPackageIds . Add ( packageId ) ;
5667 }
68+ } ) ;
5769
58- if ( TyposquattingDistanceCalculation . IsDistanceLessThanThreshold ( uploadedPackageId , package . Id , threshold ) )
70+ foreach ( var packageId in collisionPackageIds )
71+ {
72+ if ( ! _userTyposquattingService . CanUserTyposquat ( packageId , uploadedPackageOwner . Username ) )
5973 {
60- // Double check the owners list in the latest DB.
61- if ( _userTyposquattingService . CanUserTyposquat ( package . Id , uploadedPackageOwner . Username ) )
62- {
63- return ;
64- }
65-
66- Interlocked . Increment ( ref countCollision ) ;
67- loopState . Stop ( ) ;
74+ return true ;
6875 }
69- } ) ;
76+ }
7077
71- return countCollision != 0 ;
78+ return false ;
7279 }
7380
7481 private static int GetThreshold ( string packageId )
7582 {
76- foreach ( var thresholdInfo in _thresholdsList )
83+ foreach ( var thresholdInfo in ThresholdsList )
7784 {
7885 if ( packageId . Length >= thresholdInfo . LowerBound && packageId . Length < thresholdInfo . UpperBound )
7986 {
@@ -85,16 +92,16 @@ private static int GetThreshold(string packageId)
8592 }
8693 }
8794
88- public class PackageInfo
89- {
90- public string Id { get ; set ; }
91- public HashSet < string > Owners { get ; set ; }
92- }
93-
9495 public class ThresholdInfo
9596 {
96- public int LowerBound { get ; set ; }
97- public int UpperBound { get ; set ; }
98- public int Threshold { get ; set ; }
97+ public int LowerBound { get ; }
98+ public int UpperBound { get ; }
99+ public int Threshold { get ; }
100+ public ThresholdInfo ( int lowerBound , int upperBound , int threshold )
101+ {
102+ LowerBound = lowerBound ;
103+ UpperBound = upperBound ;
104+ Threshold = threshold ;
105+ }
99106 }
100107}
0 commit comments