Skip to content

Commit

Permalink
Chore: add user agent headers [head-373] (#237)
Browse files Browse the repository at this point in the history
  • Loading branch information
Asaf Agami authored Jun 20, 2023
1 parent 1b3a4ac commit 1fdeb2d
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 65 deletions.
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@

# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none

dotnet_diagnostic.VSTHRD200.severity = suggestion
4 changes: 2 additions & 2 deletions Snyk.Code.Library/Api/SnykCodeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public class SnykCodeClient : ISnykCodeClient
/// <param name="token">User token.</param>
/// <param name="flowName">Context flow name.</param>
/// <param name="orgName">User organization name.</param>
public SnykCodeClient(string baseUrl, AuthenticationToken token, string flowName, string orgName)
public SnykCodeClient(string baseUrl, AuthenticationToken token, string flowName, string orgName, HttpClient httpClient = null)
{
this.httpClient = HttpClientFactory.NewHttpClient(token, baseUrl);
this.httpClient = httpClient ?? HttpClientFactory.NewHttpClient(token, baseUrl);

Logger.Information("Create http client with with url {BaseUrl}.", baseUrl);

Expand Down
6 changes: 4 additions & 2 deletions Snyk.Code.Library/Service/CodeServiceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Snyk.Code.Library.Api;
using Snyk.Common;
using Snyk.Common.Authentication;
using System.Net.Http;

/// <summary>
/// Factory to create SnykCode services. This class hide intance createtion.
Expand All @@ -23,9 +24,10 @@ public static ISnykCodeService CreateSnykCodeService(
string endpoint,
IFileProvider fileProvider,
string flowName,
string orgName)
string orgName,
HttpClient httpClient = null)
{
var codeClient = new SnykCodeClient(endpoint, apiToken, flowName, orgName);
var codeClient = new SnykCodeClient(endpoint, apiToken, flowName, orgName, httpClient);

var filterService = new FiltersService(codeClient);

Expand Down
20 changes: 19 additions & 1 deletion Snyk.Common/HttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using global::Sentry;
using Snyk.Common.Authentication;

/// <summary>
/// Factory for <see cref="HttpClient"/>.
/// </summary>
public class HttpClientFactory
public static class HttpClientFactory
{
/// <summary>
/// Create new <see cref="HttpClient"/> by base URL and API token.
Expand Down Expand Up @@ -49,4 +51,20 @@ public static HttpClient NewHttpClient(AuthenticationToken token, string baseUrl

return httpClient;
}

public static HttpClient WithUserAgent(this HttpClient httpClient, string ideVersion, string pluginVersionString)
{
if (string.IsNullOrEmpty(ideVersion) || string.IsNullOrEmpty(pluginVersionString))
{
return httpClient;
}

var os = RuntimeInformation.OSDescription;
var arch = RuntimeInformation.ProcessArchitecture.ToString();

var header = $"VISUAL_STUDIO/{ideVersion} ({os};{arch}) VISUAL_STUDIO/{pluginVersionString} (VISUAL_STUDIO/{ideVersion})";

httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(header);
return httpClient;
}
}
9 changes: 7 additions & 2 deletions Snyk.Common/Service/SnykApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@ public class SnykApiService : ISnykApiService
private const string SastSettingsApiName = "v1/cli-config/settings/sast";

private readonly ISnykOptions options;
private readonly string vsVersion;

/// <summary>
/// Initializes a new instance of the <see cref="SnykApiService"/> class.
/// </summary>
/// <param name="options">Options instance.</param>
public SnykApiService(ISnykOptions options)
/// <param name="vsVersion">The IDE major version (17 for vs22)</param>
/// <param name="pluginVersion">The full plugin version</param>
public SnykApiService(ISnykOptions options, string vsVersion = "", string pluginVersion = "")
{
this.options = options;
this.vsVersion = vsVersion ?? "";
}

private HttpClient HttpClient => HttpClientFactory.NewHttpClient(this.options.ApiToken);
private HttpClient HttpClient => HttpClientFactory.NewHttpClient(this.options.ApiToken)
.WithUserAgent(this.vsVersion, SnykExtension.Version);

/// <inheritdoc/>
public async Task<SnykUser> GetUserAsync()
Expand Down
49 changes: 22 additions & 27 deletions Snyk.Common/SnykExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ public class SnykExtension
private const string AppSettingsDevelopmentFileName = "appsettings.development.json";
private const string AppSettingsFileName = "appsettings.json";

private static string version = string.Empty;

private static string extensionDirectoryPath;

private static SnykAppSettings appSettings;
private static readonly Lazy<string> versionLazy = new(GetIntegrationVersion);

/// <summary>
/// Gets extension version.
/// </summary>
public static string Version => GetIntegrationVersion();
public static string Version => versionLazy.Value;

/// <summary>
/// Gets <see cref="SnykAppSettings"/> from the appsettings.json file.
Expand Down Expand Up @@ -81,36 +79,33 @@ public static string GetExtensionDirectoryPath()
/// <returns>String.</returns>
private static string GetIntegrationVersion()
{
if (string.IsNullOrEmpty(version))
try
{
try
{
string extensionPath = GetExtensionDirectoryPath();
var extensionPath = GetExtensionDirectoryPath();

string manifestPath = Path.Combine(extensionPath, "extension.vsixmanifest");

var xmlDocument = new XmlDocument();
xmlDocument.Load(manifestPath);

if (xmlDocument.DocumentElement == null || xmlDocument.DocumentElement.Name != "PackageManifest")
{
return "UNKNOWN";
}
var manifestPath = Path.Combine(extensionPath, "extension.vsixmanifest");

var metaData = xmlDocument.DocumentElement.ChildNodes.Cast<XmlElement>().First(x => x.Name == "Metadata");
var identity = metaData.ChildNodes.Cast<XmlElement>().First(x => x.Name == "Identity");
var xmlDocument = new XmlDocument();
xmlDocument.Load(manifestPath);

version = identity.GetAttribute("Version");
}
catch (Exception e)
if (xmlDocument.DocumentElement?.Name != "PackageManifest")
{
// The Exception.ToString() containing the exception type, message,
// stack trace, and all of these things again for nested/inner exceptions.
Console.Error.WriteLine(e.ToString());
return "UNKNOWN";
}
}

return version;
var metaData = xmlDocument.DocumentElement.ChildNodes.Cast<XmlElement>().First(x => x.Name == "Metadata");
var identity = metaData.ChildNodes.Cast<XmlElement>().First(x => x.Name == "Identity");

return identity.GetAttribute("Version");
}
catch (Exception e)
{
// The Exception.ToString() containing the exception type, message,
// stack trace, and all of these things again for nested/inner exceptions.
Console.Error.WriteLine(e.ToString());

return string.Empty;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions Snyk.VisualStudio.Extension.Shared/CLI/SnykCli.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public class SnykCli : ICli
/// <summary>
/// Initializes a new instance of the <see cref="SnykCli"/> class.
/// </summary>
public SnykCli(ISnykOptions options)
public SnykCli(ISnykOptions options, string ideVersion = "")
{
this.ConsoleRunner = new SnykConsoleRunner();
this.ConsoleRunner = new SnykConsoleRunner(ideVersion);
this.options = options;
}

Expand Down
8 changes: 8 additions & 0 deletions Snyk.VisualStudio.Extension.Shared/CLI/SnykConsoleRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public class SnykConsoleRunner
private static readonly ILogger Logger = LogManager.ForContext<SnykConsoleRunner>();

private bool isStopped = false;
private readonly string ideVersion;

public SnykConsoleRunner(string ideVersion = "")
{
this.ideVersion = ideVersion;
}

/// <summary>
/// Gets or sets a value indicating whether process.
Expand Down Expand Up @@ -70,6 +76,8 @@ public virtual Process CreateProcess(string fileName, string arguments, StringDi

processStartInfo.EnvironmentVariables["SNYK_INTEGRATION_NAME"] = SnykExtension.IntegrationName;
processStartInfo.EnvironmentVariables["SNYK_INTEGRATION_VERSION"] = SnykExtension.Version;
processStartInfo.EnvironmentVariables["SNYK_INTEGRATION_ENVIRONMENT_NAME"] = SnykExtension.IntegrationName;
processStartInfo.EnvironmentVariables["SNYK_INTEGRATION_ENVIRONMENT_VERSION"] = this.ideVersion;

processStartInfo.WorkingDirectory = workingDirectory;

Expand Down
34 changes: 20 additions & 14 deletions Snyk.VisualStudio.Extension.Shared/Service/SnykService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ namespace Snyk.VisualStudio.Extension.Shared.Service
{
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Settings;
using Newtonsoft.Json.Linq;
using Serilog;
using Snyk.Analytics;
using Snyk.Code.Library.Service;
Expand All @@ -34,7 +40,7 @@ public class SnykService : ISnykServiceProvider, ISnykService
private static readonly ILogger Logger = LogManager.ForContext<SnykService>();

private readonly IAsyncServiceProvider serviceProvider;

private readonly string vsVersion;
private SettingsManager settingsManager;

private SnykVsThemeService vsThemeService;
Expand All @@ -61,7 +67,12 @@ public class SnykService : ISnykServiceProvider, ISnykService
/// Initializes a new instance of the <see cref="SnykService"/> class.
/// </summary>
/// <param name="serviceProvider">Snyk service provider implementation.</param>
public SnykService(IAsyncServiceProvider serviceProvider) => this.serviceProvider = serviceProvider;
/// <param name="vsVersion">The version of the IDE</param>
public SnykService(IAsyncServiceProvider serviceProvider, string vsVersion = "")
{
this.serviceProvider = serviceProvider;
this.vsVersion = vsVersion;
}

/// <summary>
/// Gets Snyk options implementation.
Expand Down Expand Up @@ -172,7 +183,7 @@ public ISnykApiService ApiService
{
if (this.apiService == null)
{
this.apiService = new SnykApiService(this.Options);
this.apiService = new SnykApiService(this.Options, this.vsVersion, SnykExtension.Version);

this.Options.SettingsChanged += this.OnSettingsChanged;
}
Expand Down Expand Up @@ -221,13 +232,6 @@ public ISentryService SentryService
/// <returns>Result VS service instance</returns>
public async Task<object> GetServiceAsync(Type serviceType) => await this.serviceProvider.GetServiceAsync(serviceType);

/// <summary>
/// Get Visual Studio service by type (not async method).
/// </summary>
/// <param name="serviceType">Needed service type.</param>
/// <returns>Result VS service instance</returns>
public object GetService(Type serviceType) => null;

/// <summary>
/// Initialize service.
/// </summary>
Expand All @@ -238,7 +242,7 @@ public async Task InitializeAsync(CancellationToken cancellationToken)
try
{
Logger.Information("Initialize Snyk services");

Logger.Information("Plugin version is {Version}", SnykExtension.Version);
await this.Package.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

this.settingsManager = new ShellSettingsManager(this.Package);
Expand Down Expand Up @@ -269,7 +273,7 @@ public async Task InitializeAsync(CancellationToken cancellationToken)
/// Create new instance of SnykCli class with Options and Logger parameters.
/// </summary>
/// <returns>New SnykCli instance.</returns>
public ICli NewCli() => new SnykCli(this.Options);
public ICli NewCli() => new SnykCli(this.Options, this.vsVersion);

private void OnSettingsChanged(object sender, SnykSettingsChangedEventArgs e) => this.SetupSnykCodeService();

Expand All @@ -278,15 +282,17 @@ private void SetupSnykCodeService()
try
{
var options = this.Options;

string endpoint = new ApiEndpointResolver(this.Options).GetSnykCodeApiUrl();
var httpClient = HttpClientFactory.NewHttpClient(options.ApiToken, endpoint)
.WithUserAgent(this.vsVersion, SnykExtension.Version);

this.snykCodeService = CodeServiceFactory.CreateSnykCodeService(
options.ApiToken,
endpoint,
this.SolutionService.FileProvider,
SnykExtension.IntegrationName,
options.Organization ?? string.Empty);
options.Organization ?? string.Empty,
httpClient);

VsStatusBarNotificationService.Instance.InitializeEventListeners(this.snykCodeService, options);
}
Expand Down
Loading

0 comments on commit 1fdeb2d

Please sign in to comment.