Skip to content

Commit c3127aa

Browse files
authored
Typo-squatting: Log metrics (#6428)
* Finish log metrics * Update * update again * update * Refactor different metrics for different time costs * Retry pushing * Final update
1 parent 4fafe75 commit c3127aa

5 files changed

Lines changed: 155 additions & 18 deletions

File tree

src/NuGetGallery/Services/ITelemetryService.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,41 @@ public interface ITelemetryService
178178
/// <param name="packageId">The id of the package that has the symbols uploaded.</param>
179179
/// <param name="packageVersion">The version of the package that has the symbols uploaded.</param>
180180
void TrackSymbolPackageFailedGalleryValidationEvent(string packageId, string packageVersion);
181+
182+
/// <summary>
183+
/// The typosquatting check result and total time for the uploaded package.
184+
/// </summary>
185+
/// <param name="packageId">The Id of the uploaded package.</param>
186+
/// <param name="totalTime">The total time for the typosquatting check logic</param>
187+
/// <param name="wasUploadBlocked">Whether the uploaded package is blocked because of typosquatting check.</param>
188+
/// <param name="collisionPackageIds">The list of collision package Ids for this uploaded package.</param>
189+
/// <param name="checklistLength">The length of the checklist for typosquatting check</param>
190+
void TrackMetricForTyposquattingCheckResultAndTotalTime(
191+
string packageId,
192+
TimeSpan totalTime,
193+
bool wasUploadBlocked,
194+
List<string> collisionPackageIds,
195+
int checklistLength);
196+
197+
/// <summary>
198+
/// The retrieval time to get the checklist for typosquatting check.
199+
/// /// </summary>
200+
/// <param name="packageId">The Id of the uploaded package.</param>
201+
/// <param name="checklistRetrievalTime">The time used to retrieval the checklist from the database.</param>
202+
void TrackMetricForTyposquattingChecklistRetrievalTime(string packageId, TimeSpan checklistRetrievalTime);
203+
204+
/// <summary>
205+
/// The algorithm processing time for typosquatting check.
206+
/// /// </summary>
207+
/// <param name="packageId">The Id of the uploaded package.</param>
208+
/// <param name="algorithmProcessingTime">The time used to finish the algorithm of typosquatting check.</param>
209+
void TrackMetricForTyposquattingAlgorithmProcessingTime(string packageId, TimeSpan algorithmProcessingTime);
210+
211+
/// <summary>
212+
/// The owners double check time for typosquatting check.
213+
/// /// </summary>
214+
/// <param name="packageId">The Id of the uploaded package.</param>
215+
/// <param name ="ownersCheckTime">The time used to double check the owners of collision Ids</param>
216+
void TrackMetricForTyposquattingOwnersCheckTime(string packageId, TimeSpan ownersCheckTime);
181217
}
182218
}

src/NuGetGallery/Services/TelemetryService.cs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ internal class Events
5555
public const string PackageMetadataComplianceError = "PackageMetadataComplianceError";
5656
public const string PackageMetadataComplianceWarning = "PackageMetadataComplianceWarning";
5757
public const string PackageOwnershipAutomaticallyAdded = "PackageOwnershipAutomaticallyAdded";
58+
public const string TyposquattingCheckResultAndTotalTimeInMs = "TyposquattingCheckResultAndTotalTimeInMs";
59+
public const string TyposquattingChecklistRetrievalTimeInMs = "TyposquattingChecklistRetrievalTimeInMs";
60+
public const string TyposquattingAlgorithmProcessingTimeInMs = "TyposquattingAlgorithmProcessingTimeInMs";
61+
public const string TyposquattingOwnersCheckTimeInMs = "TyposquattingOwnersCheckTimeInMs";
5862
}
5963

6064
private IDiagnosticsSource _diagnosticsSource;
@@ -65,7 +69,7 @@ internal class Events
6569
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
6670
Formatting = Formatting.None
6771
};
68-
72+
6973
// ODataQueryFilter properties
7074
public const string CallContext = "CallContext";
7175
public const string IsEnabled = "IsEnabled";
@@ -132,6 +136,14 @@ internal class Events
132136

133137
public const string ValueUnknown = "Unknown";
134138

139+
// Typosquatting check properties
140+
private const int TyposquattingCollisionIdsMaxPropertyValue = 10;
141+
public const string WasUploadBlocked = "WasUploadBlocked";
142+
public const string CollisionPackageIds = "CollisionPackageIds";
143+
public const string CollisionPackageIdsCount = "CollisionPackageIdsCount";
144+
public const string CheckListLength = "CheckListLength";
145+
public const string HasExtraCollisionPackageIds = "HasExtraCollisionPackageIds";
146+
135147
public TelemetryService(IDiagnosticsService diagnosticsService, ITelemetryClient telemetryClient = null)
136148
{
137149
if (diagnosticsService == null)
@@ -681,6 +693,44 @@ public void TrackSendEmail(string smtpUri, DateTimeOffset startTime, TimeSpan du
681693
_telemetryClient.TrackDependency("SMTP", smtpUri, "SendMessage", null, startTime, duration, null, success, properties);
682694
}
683695

696+
public void TrackMetricForTyposquattingCheckResultAndTotalTime(
697+
string packageId,
698+
TimeSpan totalTime,
699+
bool wasUploadBlocked,
700+
List<string> collisionPackageIds,
701+
int checklistLength)
702+
{
703+
TrackMetric(Events.TyposquattingCheckResultAndTotalTimeInMs, totalTime.TotalMilliseconds, properties => {
704+
properties.Add(PackageId, packageId);
705+
properties.Add(WasUploadBlocked, wasUploadBlocked.ToString());
706+
properties.Add(CollisionPackageIds, string.Join(",", collisionPackageIds.Take(TyposquattingCollisionIdsMaxPropertyValue)));
707+
properties.Add(CollisionPackageIdsCount, collisionPackageIds.Count.ToString());
708+
properties.Add(CheckListLength, checklistLength.ToString());
709+
properties.Add(HasExtraCollisionPackageIds, (collisionPackageIds.Count > TyposquattingCollisionIdsMaxPropertyValue).ToString());
710+
});
711+
}
712+
713+
public void TrackMetricForTyposquattingChecklistRetrievalTime(string packageId, TimeSpan checklistRetrievalTime)
714+
{
715+
TrackMetric(Events.TyposquattingChecklistRetrievalTimeInMs, checklistRetrievalTime.TotalMilliseconds, properties => {
716+
properties.Add(PackageId, packageId);
717+
});
718+
}
719+
720+
public void TrackMetricForTyposquattingAlgorithmProcessingTime(string packageId, TimeSpan algorithmProcessingTime)
721+
{
722+
TrackMetric(Events.TyposquattingAlgorithmProcessingTimeInMs, algorithmProcessingTime.TotalMilliseconds, properties => {
723+
properties.Add(PackageId, packageId);
724+
});
725+
}
726+
727+
public void TrackMetricForTyposquattingOwnersCheckTime(string packageId, TimeSpan ownersCheckTime)
728+
{
729+
TrackMetric(Events.TyposquattingOwnersCheckTimeInMs, ownersCheckTime.TotalMilliseconds, properties => {
730+
properties.Add(PackageId, packageId);
731+
});
732+
}
733+
684734
/// <summary>
685735
/// We use <see cref="ITelemetryClient.TrackMetric(string, double, IDictionary{string, string})"/> instead of
686736
/// <see cref="ITelemetryClient.TrackEvent(string, IDictionary{string, string}, IDictionary{string, double})"/>

src/NuGetGallery/Services/TyposquattingService.cs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.Threading.Tasks;
88
using System.Linq;
9+
using System.Diagnostics;
910

1011
namespace NuGetGallery
1112
{
@@ -23,22 +24,25 @@ public class TyposquattingService : ITyposquattingService
2324
private readonly IContentObjectService _contentObjectService;
2425
private readonly IPackageService _packageService;
2526
private readonly IReservedNamespaceService _reservedNamespaceService;
27+
private readonly ITelemetryService _telemetryService;
2628

27-
public TyposquattingService(IContentObjectService contentObjectService, IPackageService packageService, IReservedNamespaceService reservedNamespaceService)
29+
public TyposquattingService(IContentObjectService contentObjectService, IPackageService packageService, IReservedNamespaceService reservedNamespaceService, ITelemetryService telemetryService)
2830
{
2931
_contentObjectService = contentObjectService ?? throw new ArgumentNullException(nameof(contentObjectService));
3032
_packageService = packageService ?? throw new ArgumentNullException(nameof(packageService));
3133
_reservedNamespaceService = reservedNamespaceService ?? throw new ArgumentNullException(nameof(reservedNamespaceService));
34+
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
3235

3336
TyposquattingCheckListLength = _contentObjectService.TyposquattingConfiguration.PackageIdChecklistLength;
3437
}
3538

3639
public bool IsUploadedPackageIdTyposquatting(string uploadedPackageId, User uploadedPackageOwner, out List<string> typosquattingCheckCollisionIds)
3740
{
3841
typosquattingCheckCollisionIds = new List<string>();
42+
var wasUploadBlocked = false;
3943
if (!_contentObjectService.TyposquattingConfiguration.IsCheckEnabled || _reservedNamespaceService.GetReservedNamespacesForId(uploadedPackageId).Any())
4044
{
41-
return false;
45+
return wasUploadBlocked;
4246
}
4347

4448
if (uploadedPackageId == null)
@@ -51,14 +55,17 @@ public bool IsUploadedPackageIdTyposquatting(string uploadedPackageId, User uplo
5155
throw new ArgumentNullException(nameof(uploadedPackageOwner));
5256
}
5357

58+
var checklistRetrievalStopwatch = Stopwatch.StartNew();
5459
var packageRegistrations = _packageService.GetAllPackageRegistrations();
5560
var packagesCheckList = packageRegistrations
5661
.OrderByDescending(pr => pr.IsVerified)
5762
.ThenByDescending(pr => pr.DownloadCount)
5863
.Select(pr => pr.Id)
5964
.Take(TyposquattingCheckListLength)
6065
.ToList();
66+
checklistRetrievalStopwatch.Stop();
6167

68+
var algorithmProcessingStopwatch = Stopwatch.StartNew();
6269
var threshold = GetThreshold(uploadedPackageId);
6370
uploadedPackageId = TyposquattingStringNormalization.NormalizeString(uploadedPackageId);
6471

@@ -72,11 +79,25 @@ public bool IsUploadedPackageIdTyposquatting(string uploadedPackageId, User uplo
7279
}
7380
});
7481

82+
algorithmProcessingStopwatch.Stop();
83+
84+
var totalTime = checklistRetrievalStopwatch.Elapsed.Add(algorithmProcessingStopwatch.Elapsed);
85+
_telemetryService.TrackMetricForTyposquattingChecklistRetrievalTime(uploadedPackageId, checklistRetrievalStopwatch.Elapsed);
86+
_telemetryService.TrackMetricForTyposquattingAlgorithmProcessingTime(uploadedPackageId, algorithmProcessingStopwatch.Elapsed);
87+
7588
if (collisionIds.Count == 0)
7689
{
90+
_telemetryService.TrackMetricForTyposquattingCheckResultAndTotalTime(
91+
uploadedPackageId,
92+
totalTime,
93+
wasUploadBlocked,
94+
typosquattingCheckCollisionIds,
95+
TyposquattingCheckListLength);
96+
7797
return false;
7898
}
7999

100+
var ownersCheckStopwatch = Stopwatch.StartNew();
80101
var collisionPackagesIdAndOwners = packageRegistrations
81102
.Where(pr => collisionIds.Contains(pr.Id))
82103
.Select(pr => new { Id = pr.Id, Owners = pr.Owners.Select(x => x.Key).ToList() })
@@ -99,7 +120,20 @@ public bool IsUploadedPackageIdTyposquatting(string uploadedPackageId, User uplo
99120
var isUserAllowedTyposquatting = collisionPackagesIdAndOwners
100121
.Any(pio => pio.Owners.Any(k => k == uploadedPackageOwner.Key));
101122

102-
return _contentObjectService.TyposquattingConfiguration.IsBlockUsersEnabled && !isUserAllowedTyposquatting;
123+
wasUploadBlocked = _contentObjectService.TyposquattingConfiguration.IsBlockUsersEnabled && !isUserAllowedTyposquatting;
124+
125+
ownersCheckStopwatch.Stop();
126+
127+
totalTime = totalTime.Add(ownersCheckStopwatch.Elapsed);
128+
_telemetryService.TrackMetricForTyposquattingOwnersCheckTime(uploadedPackageId, ownersCheckStopwatch.Elapsed);
129+
_telemetryService.TrackMetricForTyposquattingCheckResultAndTotalTime(
130+
uploadedPackageId,
131+
totalTime,
132+
wasUploadBlocked,
133+
typosquattingCheckCollisionIds,
134+
TyposquattingCheckListLength);
135+
136+
return wasUploadBlocked;
103137
}
104138

105139
private static int GetThreshold(string packageId)
@@ -112,7 +146,7 @@ private static int GetThreshold(string packageId)
112146
}
113147
}
114148

115-
throw new ArgumentException("There is no predefined typo-squatting threshold for this package Id: " + packageId);
149+
throw new ArgumentException(String.Format("There is no predefined typo-squatting threshold for this package Id: {0}", packageId));
116150
}
117151
}
118152

tests/NuGetGallery.Facts/Services/TelemetryServiceFacts.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,22 @@ public static IEnumerable<object[]> TrackMetricNames_Data
203203
yield return new object[] { "PackageOwnershipAutomaticallyAdded",
204204
(TrackAction)(s => s.TrackPackageOwnershipAutomaticallyAdded(fakes.Package.Id, package.NormalizedVersion))
205205
};
206+
207+
yield return new object[] { "TyposquattingCheckResultAndTotalTimeInMs",
208+
(TrackAction)(s => s.TrackMetricForTyposquattingCheckResultAndTotalTime(fakes.Package.Id, TimeSpan.FromMilliseconds(100), true, new List<string>{"newtonsoft-json" }, 10000))
209+
};
210+
211+
yield return new object[] { "TyposquattingChecklistRetrievalTimeInMs",
212+
(TrackAction)(s => s.TrackMetricForTyposquattingChecklistRetrievalTime(fakes.Package.Id, TimeSpan.FromMilliseconds(100)))
213+
};
214+
215+
yield return new object[] { "TyposquattingAlgorithmProcessingTimeInMs",
216+
(TrackAction)(s => s.TrackMetricForTyposquattingAlgorithmProcessingTime(fakes.Package.Id, TimeSpan.FromMilliseconds(100)))
217+
};
218+
219+
yield return new object[] { "TyposquattingOwnersCheckTimeInMs",
220+
(TrackAction)(s => s.TrackMetricForTyposquattingOwnersCheckTime(fakes.Package.Id, TimeSpan.FromMilliseconds(100)))
221+
};
206222
}
207223
}
208224

0 commit comments

Comments
 (0)