Skip to content

Commit

Permalink
feat: only send Amplitude analytics events when connected to an MT US…
Browse files Browse the repository at this point in the history
… environment [IDE-24]

* fix: only send analytics when connected to US-based, non-FedRAMP MT environments

* fix: app hostname when custom endpoint is fedramp

This was driving the link in the settings page to enable Snyk Code, if the endpoint
was set to a FedRAMP environment, this still linked to app.snyk.io.

* fix: SnykCodeSettingsUrl edge cases and add tests

* chore: use parametrized tests for `IsAnalyticsPermitted`
  • Loading branch information
chdorner-snyk authored Feb 7, 2024
1 parent 3aca434 commit c6c82fb
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 24 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Snyk Changelog

## [1.1.46]
- Only send Amplitude analytics events when connected to an MT US environment

## [1.1.45]

### Changed
Expand Down
2 changes: 2 additions & 0 deletions Snyk.Common/Settings/ISnykOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public interface ISnykOptions

bool IsFedramp();

bool IsAnalyticsPermitted();

/// <summary>
/// Gets or sets a value indicating whether CLI custom endpoint parameter.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public virtual Process CreateProcess(string fileName, string arguments, StringDi
}
}

if (!this.options.UsageAnalyticsEnabled || this.options.IsFedramp())
if (!this.options.UsageAnalyticsEnabled || !this.options.IsAnalyticsPermitted())
{
processStartInfo.EnvironmentVariables["SNYK_CFG_DISABLE_ANALYTICS"] = "1";
}
Expand Down
2 changes: 1 addition & 1 deletion Snyk.VisualStudio.Extension.Shared/Service/SnykService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ private void InitializeAnalyticsService()

string anonymousId = this.Options.AnonymousId;

var enabled = this.Options.UsageAnalyticsEnabled && !this.Options.IsFedramp();
var enabled = this.Options.UsageAnalyticsEnabled && this.Options.IsAnalyticsPermitted();
var endpoint = this.ApiEndpointResolver.UserMeEndpoint;

Logger.Information("analytics enabled = {Enabled}, endpoint = {Endpoint}", enabled, endpoint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Threading.Tasks;
Expand Down Expand Up @@ -111,6 +112,18 @@ public bool IsFedramp()
return endpointUri.Host.ToLower().EndsWith("snykgov.io");
}

/// <summary>
/// Checks if the current endpoint permits sending external analytics events
/// </summary>
/// <returns></returns>
public bool IsAnalyticsPermitted()
{
var endpointUri = new Uri(this.GetBaseAppURL());

string[] permittedHosts = ["app.snyk.io", "app.us.snyk.io"];
return permittedHosts.Contains(endpointUri.Host.ToLower());
}

/// <summary>
/// Gets or sets a value indicating whether Custom endpoint.
/// </summary>
Expand Down Expand Up @@ -141,7 +154,7 @@ public string CustomEndpoint
}

/// <inheritdoc/>
public string SnykCodeSettingsUrl => $"{this.GetAppCustomEndpoint()}/manage/snyk-code";
public string SnykCodeSettingsUrl => $"{this.GetBaseAppURL()}/manage/snyk-code";

public SastSettings SastSettings
{
Expand Down Expand Up @@ -383,22 +396,22 @@ public bool Authenticate()

private void FireSettingsChangedEvent() => this.SettingsChanged?.Invoke(this, new SnykSettingsChangedEventArgs());

private string GetAppCustomEndpoint()
private string GetBaseAppURL()
{
var endpoint = this.customEndpoint.IsNullOrEmpty() ? "https://app.snyk.io" : this.customEndpoint.RemoveTrailingSlashes();
Uri uri = new Uri(endpoint);

if (!uri.Host.StartsWith("app") && uri.Host.EndsWith("snyk.io"))
if (!uri.Host.StartsWith("app") && (uri.Host.EndsWith("snyk.io") || uri.Host.EndsWith("snykgov.io")))
{
return endpoint.Replace("https://", "https://app.").RemoveFromEnd("api");
return endpoint.Replace("https://", "https://app.").RemoveFromEnd("/api");
}
else if (uri.Host.StartsWith("app") && uri.Host.EndsWith("snyk.io"))
else if (uri.Host.StartsWith("app") && (uri.Host.EndsWith("snyk.io") || uri.Host.EndsWith("snykgov.io")))
{
return endpoint.RemoveFromEnd("api");
return endpoint.RemoveFromEnd("/api");
}
else
{
return "https://app.snyk.io/";
return "https://app.snyk.io";
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ private void ShowWelcomeOrRunScanScreen()
{
var options = this.serviceProvider.Options;
this.serviceProvider.AnalyticsService.AnalyticsEnabledOption =
options.UsageAnalyticsEnabled && !options.IsFedramp();
options.UsageAnalyticsEnabled && options.IsAnalyticsPermitted();

if (options.ApiToken.IsValid())
{
Expand Down
34 changes: 34 additions & 0 deletions Snyk.VisualStudio.Extension.Tests/GeneralOptionsDialogPage.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace Snyk.VisualStudio.Extension.Tests
{
using System.Collections.Generic;
using System.Net;
using Moq;
using Snyk.VisualStudio.Extension.Shared.CLI;
using Snyk.VisualStudio.Extension.Shared.Service;
Expand All @@ -26,5 +28,37 @@ public void ApiEndpointChanged_InvalidatesCliToken()
// Assert
cliMock.Verify(mock => mock.UnsetApiToken());
}

[Theory]
[InlineData("https://app.snyk.io/api", true)]
[InlineData("https://snyk.io/api", true)]
[InlineData("https://app.us.snyk.io/api", true)]
[InlineData("https://app.eu.snyk.io/api", false)]
[InlineData("https://app.au.snyk.io/api", false)]
[InlineData("https://app.snykgov.io/api", false)]
public void IsAnalyticsPermitted(string endpoint, bool expected)
{
var optionsDialogPage = new SnykGeneralOptionsDialogPage();
optionsDialogPage.CustomEndpoint = endpoint;
Assert.Equal(expected, optionsDialogPage.IsAnalyticsPermitted());
}

[Theory]
[InlineData(null, "https://app.snyk.io/manage/snyk-code")]
[InlineData("", "https://app.snyk.io/manage/snyk-code")]
[InlineData("https://snyk.io", "https://app.snyk.io/manage/snyk-code")]
[InlineData("https://snyk.io/api", "https://app.snyk.io/manage/snyk-code")]
[InlineData("https://snyk.io/api/", "https://app.snyk.io/manage/snyk-code")]
[InlineData("https://snykgov.io/api", "https://app.snykgov.io/manage/snyk-code")]
[InlineData("https://app.snyk.io/api", "https://app.snyk.io/manage/snyk-code")]
[InlineData("https://app.eu.snyk.io/api", "https://app.eu.snyk.io/manage/snyk-code")]
[InlineData("https://app.snykgov.io/api", "https://app.snykgov.io/manage/snyk-code")]
[InlineData("https://example.org", "https://app.snyk.io/manage/snyk-code")]
public void SnykCodeSettingsUrl(string endpoint, string expected)
{
var optionsDialogPage = new SnykGeneralOptionsDialogPage();
optionsDialogPage.CustomEndpoint = endpoint;
Assert.Equal(expected, optionsDialogPage.SnykCodeSettingsUrl);
}
}
}
4 changes: 4 additions & 0 deletions Snyk.VisualStudio.Extension.Tests/SnykCliTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ public SnykCliTest()
.Setup(options => options.IsFedramp())
.Returns(false);

this.optionsMock
.Setup(options => options.IsAnalyticsPermitted())
.Returns(true);

this.optionsMock
.Setup(options => options.ApiToken)
.Returns(AuthenticationToken.EmptyToken);
Expand Down
28 changes: 14 additions & 14 deletions Snyk.VisualStudio.Extension.Tests/SnykConsoleRunnerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ public class SnykConsoleRunnerTest
{

[Fact]
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_Off_IsFedramp()
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_Off_IsAnalyticsNotPermitted()
{
var optionsMock = new Mock<ISnykOptions>();
optionsMock
.Setup(options => options.UsageAnalyticsEnabled)
.Returns(false);

optionsMock
.Setup(options => options.IsFedramp())
.Returns(true);
.Setup(options => options.IsAnalyticsPermitted())
.Returns(false);

var cut = new SnykConsoleRunner(optionsMock.Object);

Expand All @@ -31,60 +31,60 @@ public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_Off_IsFedramp
}

[Fact]
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_Off_IsNotFedramp()
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_Off_IsAnalyticsPermitted()
{
var optionsMock = new Mock<ISnykOptions>();
optionsMock
.Setup(options => options.UsageAnalyticsEnabled)
.Returns(false);

optionsMock
.Setup(options => options.IsFedramp())
.Returns(false);
.Setup(options => options.IsAnalyticsPermitted())
.Returns(true);

var cut = new SnykConsoleRunner(optionsMock.Object);

var process = cut.CreateProcess("snyk", "test");

Assert.Equal("1", process.StartInfo.EnvironmentVariables["SNYK_CFG_DISABLE_ANALYTICS"]);
}

[Fact]
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_On_IsFedramp()
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_On_IsAnalyticsPermitted()
{
var optionsMock = new Mock<ISnykOptions>();
optionsMock
.Setup(options => options.UsageAnalyticsEnabled)
.Returns(true);

optionsMock
.Setup(options => options.IsFedramp())
.Setup(options => options.IsAnalyticsPermitted())
.Returns(true);

var cut = new SnykConsoleRunner(optionsMock.Object);

var process = cut.CreateProcess("snyk", "test");

Assert.Equal("1", process.StartInfo.EnvironmentVariables["SNYK_CFG_DISABLE_ANALYTICS"]);
Assert.Null(process.StartInfo.EnvironmentVariables["SNYK_CFG_DISABLE_ANALYTICS"]);
}

[Fact]
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_On_IsNotFedramp()
public void SnykConsoleRunnerTest_CreateProcess_Respects_Analytics_On_IsAnalyticsNotPermitted()
{
var optionsMock = new Mock<ISnykOptions>();
optionsMock
.Setup(options => options.UsageAnalyticsEnabled)
.Returns(true);

optionsMock
.Setup(options => options.IsFedramp())
.Setup(options => options.IsAnalyticsPermitted())
.Returns(false);

var cut = new SnykConsoleRunner(optionsMock.Object);

var process = cut.CreateProcess("snyk", "test");

Assert.Null(process.StartInfo.EnvironmentVariables["SNYK_CFG_DISABLE_ANALYTICS"]);
Assert.Equal("1", process.StartInfo.EnvironmentVariables["SNYK_CFG_DISABLE_ANALYTICS"]);
}
}
}

0 comments on commit c6c82fb

Please sign in to comment.