Skip to content

Commit c4ad812

Browse files
Add telemetry for auto-add package owner feature (#6374)
1 parent 16c912d commit c4ad812

11 files changed

Lines changed: 161 additions & 38 deletions

src/NuGetGallery/Security/MicrosoftTeamSubscription.cs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@ public class MicrosoftTeamSubscription : IUserSecurityPolicySubscription
1313

1414
internal const string MicrosoftUsername = "Microsoft";
1515
internal const string Name = "MicrosoftTeamSubscription";
16+
internal static readonly string[] AllowedCopyrightNotices = new string[]
17+
{
18+
"(c) Microsoft Corporation. All rights reserved.",
19+
"© Microsoft Corporation. All rights reserved.",
20+
"© Microsoft Corporation. All rights reserved.",
21+
"© Microsoft Corporation. Tüm hakları saklıdır.",
22+
"© Microsoft Corporation. Todos os direitos reservados.",
23+
"© Microsoft Corporation. Alle Rechte vorbehalten.",
24+
"© Microsoft Corporation. 保留所有权利.",
25+
"© Microsoft Corporation. Všechna práva vyhrazena.",
26+
"© Microsoft Corporation. Reservados todos los derechos.",
27+
"© Microsoft Corporation. Wszelkie prawa zastrzeżone.",
28+
"© Microsoft Corporation. Tous droits réservés.",
29+
"© Microsoft Corporation. 著作權所有,並保留一切權利。",
30+
"© Microsoft Corporation. Tutti i diritti sono riservati.",
31+
"© Корпорация Майкрософт (Microsoft Corporation). Все права защищены."
32+
};
1633

1734
public string SubscriptionName => Name;
1835

@@ -40,24 +57,9 @@ private static List<UserSecurityPolicy> InitializePoliciesList()
4057
return new List<UserSecurityPolicy>()
4158
{
4259
RequirePackageMetadataCompliancePolicy.CreatePolicy(
43-
Name,
60+
Name,
4461
MicrosoftUsername,
45-
allowedCopyrightNotices: new string[]
46-
{
47-
"(c) Microsoft Corporation. All rights reserved.",
48-
"© Microsoft Corporation. All rights reserved.",
49-
"© Microsoft Corporation. Tüm hakları saklıdır.",
50-
"© Microsoft Corporation. Todos os direitos reservados.",
51-
"© Microsoft Corporation. Alle Rechte vorbehalten.",
52-
"© Microsoft Corporation. 保留所有权利.",
53-
"© Microsoft Corporation. Všechna práva vyhrazena.",
54-
"© Microsoft Corporation. Reservados todos los derechos.",
55-
"© Microsoft Corporation. Wszelkie prawa zastrzeżone.",
56-
"© Microsoft Corporation. Tous droits réservés.",
57-
"© Microsoft Corporation. 著作權所有,並保留一切權利。",
58-
"© Microsoft Corporation. Tutti i diritti sono riservati.",
59-
"© Корпорация Майкрософт (Microsoft Corporation). Все права защищены."
60-
},
62+
allowedCopyrightNotices: AllowedCopyrightNotices,
6163
isLicenseUrlRequired: true,
6264
isProjectUrlRequired: true,
6365
errorMessageFormat: Strings.SecurityPolicy_RequireMicrosoftPackageMetadataComplianceForPush)

src/NuGetGallery/Security/PackageSecurityPolicyEvaluationContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@ public class PackageSecurityPolicyEvaluationContext : UserSecurityPolicyEvaluati
1212
public PackageSecurityPolicyEvaluationContext(
1313
IUserService userService,
1414
IPackageOwnershipManagementService packageOwnershipManagementService,
15+
ITelemetryService telemetryService,
1516
IEnumerable<UserSecurityPolicy> policies,
1617
Package package,
1718
HttpContextBase httpContext)
1819
: base(policies, httpContext)
1920
{
2021
Package = package ?? throw new ArgumentNullException(nameof(package));
2122
UserService = userService ?? throw new ArgumentNullException(nameof(userService));
23+
TelemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
2224
PackageOwnershipManagementService = packageOwnershipManagementService ?? throw new ArgumentNullException(nameof(packageOwnershipManagementService));
2325
}
2426

2527
public PackageSecurityPolicyEvaluationContext(
2628
IUserService userService,
2729
IPackageOwnershipManagementService packageOwnershipManagementService,
30+
ITelemetryService telemetryService,
2831
IEnumerable<UserSecurityPolicy> policies,
2932
Package package,
3033
User sourceAccount,
@@ -34,6 +37,7 @@ public PackageSecurityPolicyEvaluationContext(
3437
{
3538
Package = package ?? throw new ArgumentNullException(nameof(package));
3639
UserService = userService ?? throw new ArgumentNullException(nameof(userService));
40+
TelemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
3741
PackageOwnershipManagementService = packageOwnershipManagementService ?? throw new ArgumentNullException(nameof(packageOwnershipManagementService));
3842
}
3943

@@ -44,6 +48,8 @@ public PackageSecurityPolicyEvaluationContext(
4448

4549
public IUserService UserService { get; }
4650

51+
public ITelemetryService TelemetryService { get; }
52+
4753
public IPackageOwnershipManagementService PackageOwnershipManagementService { get; }
4854
}
4955
}

src/NuGetGallery/Security/RequirePackageMetadataCompliancePolicy.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ public override async Task<SecurityPolicyResult> EvaluateAsync(PackageSecurityPo
4949
// Evaluate package metadata validations
5050
if (!IsPackageMetadataCompliant(context.Package, state, out var complianceFailures))
5151
{
52+
context.TelemetryService.TrackPackageMetadataComplianceError(
53+
context.Package.Id,
54+
context.Package.NormalizedVersion,
55+
complianceFailures);
56+
5257
// Package policy not met.
5358
return SecurityPolicyResult.CreateErrorResult(
5459
string.Format(
@@ -63,12 +68,21 @@ public override async Task<SecurityPolicyResult> EvaluateAsync(PackageSecurityPo
6368
// This will also mark the package as verified if the prefix has been reserved by the co-owner.
6469
// The entities context is committed later as a single atomic transaction (see PackageUploadService).
6570
await context.PackageOwnershipManagementService.AddPackageOwnerAsync(context.Package.PackageRegistration, requiredCoOwner, commitChanges: false);
71+
72+
context.TelemetryService.TrackPackageOwnershipAutomaticallyAdded(
73+
context.Package.Id,
74+
context.Package.NormalizedVersion);
6675
}
6776

6877
// If the PackageRegistration is not marked as verified,
6978
// the account pushing the package has not registered the prefix yet.
7079
if (!context.Package.PackageRegistration.IsVerified)
7180
{
81+
context.TelemetryService.TrackPackageMetadataComplianceWarning(
82+
context.Package.Id,
83+
context.Package.NormalizedVersion,
84+
complianceWarnings: new[] { Strings.SecurityPolicy_RequirePackagePrefixReserved });
85+
7286
return SecurityPolicyResult.CreateWarningResult(Strings.SecurityPolicy_RequirePackagePrefixReserved);
7387
}
7488

@@ -77,8 +91,8 @@ public override async Task<SecurityPolicyResult> EvaluateAsync(PackageSecurityPo
7791
}
7892

7993
public static UserSecurityPolicy CreatePolicy(
80-
string subscription,
81-
string requiredCoOwnerUsername,
94+
string subscription,
95+
string requiredCoOwnerUsername,
8296
string[] allowedCopyrightNotices,
8397
bool isLicenseUrlRequired,
8498
bool isProjectUrlRequired,
@@ -153,7 +167,7 @@ public class State
153167

154168
[JsonProperty("licUrlReq")]
155169
public bool IsLicenseUrlRequired { get; set; }
156-
170+
157171
[JsonProperty("projUrlReq")]
158172
public bool IsProjectUrlRequired { get; set; }
159173

src/NuGetGallery/Security/SecurityPolicyService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ private static readonly RequireOrganizationTenantPolicy _organizationTenantPolic
3232

3333
private readonly Lazy<IUserService> _userService;
3434
private readonly Lazy<IPackageOwnershipManagementService> _packageOwnershipManagementService;
35+
private readonly ITelemetryService _telemetryService;
3536

3637
protected IEntitiesContext EntitiesContext { get; set; }
3738

@@ -56,6 +57,7 @@ public SecurityPolicyService(
5657
IAppConfiguration configuration,
5758
Lazy<IUserService> userService,
5859
Lazy<IPackageOwnershipManagementService> packageOwnershipManagementService,
60+
ITelemetryService telemetryService,
5961
MicrosoftTeamSubscription microsoftTeamSubscription = null)
6062
{
6163
EntitiesContext = entitiesContext ?? throw new ArgumentNullException(nameof(entitiesContext));
@@ -72,6 +74,7 @@ public SecurityPolicyService(
7274
MicrosoftTeamSubscription = microsoftTeamSubscription;
7375
_packageOwnershipManagementService = packageOwnershipManagementService ?? throw new ArgumentNullException(nameof(packageOwnershipManagementService));
7476
_userService = userService ?? throw new ArgumentNullException(nameof(userService));
77+
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
7578
}
7679

7780
/// <summary>
@@ -204,6 +207,7 @@ private async Task<SecurityPolicyResult> EvaluatePackagePoliciesInternalAsync(
204207
var context = new PackageSecurityPolicyEvaluationContext(
205208
_userService.Value,
206209
_packageOwnershipManagementService.Value,
210+
_telemetryService,
207211
foundPolicies,
208212
package,
209213
sourceAccount,

src/NuGetGallery/Services/ITelemetryService.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ public interface ITelemetryService
5151
/// </summary>
5252
void TrackUserPackageDeleteChecked(UserPackageDeleteEvent details, UserPackageDeleteOutcome outcome);
5353

54+
void TrackPackageMetadataComplianceError(string packageId, string packageVersion, IEnumerable<string> complianceFailures);
55+
56+
void TrackPackageMetadataComplianceWarning(string packageId, string packageVersion, IEnumerable<string> complianceWarnings);
57+
5458
/// <summary>
5559
/// A telemetry event emitted when a user package delete is executed.
5660
/// </summary>
@@ -64,6 +68,13 @@ public interface ITelemetryService
6468
/// or empty.</exception>
6569
void TrackCertificateAdded(string thumbprint);
6670

71+
/// <summary>
72+
/// A telemetry event emitted when a package owner was automatically added to a package registration.
73+
/// </summary>
74+
/// <param name="packageId">The package registration id.</param>
75+
/// <param name="packageVersion">The normalized package version.</param>
76+
void TrackPackageOwnershipAutomaticallyAdded(string packageId, string packageVersion);
77+
6778
/// <summary>
6879
/// A telemetry event emitted when a certificate is activated for an account.
6980
/// </summary>

src/NuGetGallery/Services/TelemetryService.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using System.Security.Principal;
88
using System.Web;
9+
using Newtonsoft.Json;
910
using NuGet.Versioning;
1011
using NuGetGallery.Authentication;
1112
using NuGetGallery.Diagnostics;
@@ -51,11 +52,20 @@ internal class Events
5152
public const string SymbolPackagePush = "SymbolPackagePush";
5253
public const string SymbolPackagePushFailure = "SymbolPackagePushFailure";
5354
public const string SymbolPackageGalleryValidation = "SymbolPackageGalleryValidation";
55+
public const string PackageMetadataComplianceError = "PackageMetadataComplianceError";
56+
public const string PackageMetadataComplianceWarning = "PackageMetadataComplianceWarning";
57+
public const string PackageOwnershipAutomaticallyAdded = "PackageOwnershipAutomaticallyAdded";
5458
}
5559

5660
private IDiagnosticsSource _diagnosticsSource;
5761
private ITelemetryClient _telemetryClient;
5862

63+
private readonly JsonSerializerSettings _defaultJsonSerializerSettings = new JsonSerializerSettings
64+
{
65+
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
66+
Formatting = Formatting.None
67+
};
68+
5969
// ODataQueryFilter properties
6070
public const string CallContext = "CallContext";
6171
public const string IsEnabled = "IsEnabled";
@@ -116,6 +126,10 @@ internal class Events
116126
public const string CreatedDateForAccountToBeDeleted = "CreatedDateForAccountToBeDeleted";
117127
public const string AccountDeleteSucceeded = "AccountDeleteSucceeded";
118128

129+
// Package metadata compliance properties
130+
public const string ComplianceFailures = "ComplianceFailures";
131+
public const string ComplianceWarnings = "ComplianceWarnings";
132+
119133
public const string ValueUnknown = "Unknown";
120134

121135
public TelemetryService(IDiagnosticsService diagnosticsService, ITelemetryClient telemetryClient = null)
@@ -313,6 +327,36 @@ public void TrackPackageRevalidate(Package package)
313327
TrackMetricForPackage(Events.PackageRevalidate, package);
314328
}
315329

330+
public void TrackPackageMetadataComplianceError(string packageId, string packageVersion, IEnumerable<string> complianceFailures)
331+
{
332+
TrackMetricForPackage(
333+
Events.PackageMetadataComplianceError,
334+
packageId,
335+
packageVersion,
336+
properties => {
337+
properties.Add(ComplianceFailures, JsonConvert.SerializeObject(complianceFailures, _defaultJsonSerializerSettings));
338+
});
339+
}
340+
341+
public void TrackPackageMetadataComplianceWarning(string packageId, string packageVersion, IEnumerable<string> complianceWarnings)
342+
{
343+
TrackMetricForPackage(
344+
Events.PackageMetadataComplianceWarning,
345+
packageId,
346+
packageVersion,
347+
properties => {
348+
properties.Add(ComplianceWarnings, JsonConvert.SerializeObject(complianceWarnings, _defaultJsonSerializerSettings));
349+
});
350+
}
351+
352+
public void TrackPackageOwnershipAutomaticallyAdded(string packageId, string packageVersion)
353+
{
354+
TrackMetricForPackage(
355+
Events.PackageOwnershipAutomaticallyAdded,
356+
packageId,
357+
packageVersion);
358+
}
359+
316360
public void TrackCertificateAdded(string thumbprint)
317361
{
318362
TrackMetricForCertificateActivity(Events.CertificateAdded, thumbprint);

tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1580,13 +1580,16 @@ private static ISecurityPolicyService CreateSecurityPolicyService(
15801580
var configurationMock = new Mock<IAppConfiguration>(MockBehavior.Strict);
15811581
configurationMock.SetupGet(m => m.EnforceDefaultSecurityPolicies).Returns(false);
15821582

1583+
var telemetryServiceMock = new Mock<ITelemetryService>();
1584+
15831585
return new SecurityPolicyService(
15841586
entitiesContext,
15851587
auditing,
15861588
diagnostics,
15871589
configurationMock.Object,
15881590
userServiceFactory,
1589-
packageOwnershipManagementServiceFactory);
1591+
packageOwnershipManagementServiceFactory,
1592+
telemetryServiceMock.Object);
15901593
}
15911594

15921595
private static Mock<TestPackageReader> CreatePackage(

tests/NuGetGallery.Facts/Security/MicrosoftTeamSubscriptionFacts.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public void Policies_ReturnsMicrosoftTeamSubscriptionPolicies()
2626
"\"copy\":" +
2727
"[" +
2828
"\"(c) Microsoft Corporation. All rights reserved.\"," +
29+
"\"&#169; Microsoft Corporation. All rights reserved.\"," +
2930
"\"© Microsoft Corporation. All rights reserved.\"," +
3031
"\"© Microsoft Corporation. Tüm hakları saklıdır.\"," +
3132
"\"© Microsoft Corporation. Todos os direitos reservados.\"," +

0 commit comments

Comments
 (0)