Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
** general **
always be concise, direct and don't try to appease me.
DOUBLE CHECK THAT YOUR CHANGES ARE REALLY NEEDED. ALWAYS STICK TO THE GIVEN GOAL, NOT MORE.
I repeat: don't optimize, don't refactor if not needed.
Adhere to the rules, fix linting & test issues that are newly introduced.
don't change old entries in the changelog. it's a history of changes. the current version is the last semantic version tag on origin/main in git

** how to implement **
always write and update test cases. iterate until they pass.
use existing mocks, don't write new ones.
if you use mocks, use mockk to generate them.
always run the tests after editing.
always use the linter, use npm lint:fix. only fix linting issues in changed files.
don't change code that does not need to be changed. only do the minimum changes.
add a summary of the changes to the changelog for the next version. usually it's a minor semantic version bump.
this is not a library. if files are not used or needed anymore, delete them instead of deprecating them.
if a tool call fails, analyze why it failed and correct your approach. don't prompt the user for help.
if you don't know something, read the code instead of assuming it.
commenting out code to fix errors is not a solution. instead, fix the error.

** security **
determine the absolute path of the project directory. you can do that e.g. by executing pwd on the shell within the directory.
always use snyk for sca and code scanning. scan with both tools. you need to pass the absolute path of the directory we are scanning.
run snyk code tests after each edit. pass the absolute path of the project directory as a parameter
run snyk sca tests after updating package.json and pass the absolute path of the project directory as a parameter.
run snyk sca and code test before committing. if not test data, fix issues before committing.

** fixing issues **
fix security issues if they are fixable. take the snyk scan results and the test results as input.

** committing **
use atomic commits
when asked to commit, always use conventional commit messages (Conventional Commit Style (Subject + Body)). be descriptive in the body. if you find a JIRA issue (IDE-XXXX) in the branch name, use it as a postfix to the subject line in the format [IDE-XXXX]
consider all commits in the current branch when committing, to have the context of the current changes.
when asked to push, always use 'git push --set-upstream origin $(git_current_branch)' with git_current_branch being the current branch we are on
never force push
never push without asking
regularly fetch main branch and offer to merge it into git_current_branch
don't touch the copyright header
after pushing offer to create a PR on github. analyze the changes by comparing the current branch ($(git_current_branch)) with origin/main, and craft a PR description and title.
use the github pr template in this repository
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,6 @@ ModelManifest.xml
Thumbs.db
.directory
.DS_Store

# JetBrains IDEs
.idea/
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
# Snyk Security Changelog

## [2.3.0]
### Added
- SSL certificate validation bypass for downloads:
- Added support for `--insecure` flag via `IgnoreUnknownCA` setting in both WebClient and HttpClient
- Added SSL certificate validation bypass when `IgnoreUnknownCA` is enabled
- Added comprehensive test coverage for SSL certificate handling

### Changed
- PAT support.
- remove Snyk Code Quality
- Enhanced `SnykWebClient` to respect SSL settings from extension options
- Enhanced `SnykCliDownloader` to use properly configured `HttpClient` with SSL support

### Fixed
- Fixed SSL certificate validation not respecting the `IgnoreUnknownCA` setting during CLI downloads

## [2.2.1]
### Fixed
Expand Down
28 changes: 23 additions & 5 deletions Snyk.VisualStudio.Extension.2022/Download/SnykCliDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public LatestReleaseInfo GetLatestReleaseInfo()
{
Logger.Information("Enter GetLatestReleaseInfo method");

using (var webClient = new SnykWebClient())
using (var webClient = new SnykWebClient(this.SnykOptions))
{
Logger.Information("Get latest CLI release info");

Expand Down Expand Up @@ -79,14 +79,15 @@ public bool IsNewVersionAvailable(string currentVersionStr, string newVersionStr
}

/// <summary>
/// Request last cli sha.
/// Request last cli sha256 checksum from server.
/// </summary>
/// <returns>CLI sha string.</returns>
/// <param name="cliDownloadUrl">CLI download URL.</param>
/// <returns>CLI sha256 checksum.</returns>
public string GetLatestCliSha(string cliDownloadUrl)
{
Logger.Information("Enter GetLatestCliSha method");

using (var webClient = new SnykWebClient())
using (var webClient = new SnykWebClient(this.SnykOptions))
{
Logger.Information("Get latest CLI sha");
var shaDownloadUrl = string.Format(Sha256DownloadUrl, cliDownloadUrl);
Expand Down Expand Up @@ -262,7 +263,8 @@ private async Task DownloadFileAsync(
{
const int bufferSize = 81920;

using (var client = new HttpClient())
using (var handler = CreateHttpClientHandler())
using (var client = new HttpClient(handler))
{
client.Timeout = TimeSpan.FromMinutes(5);

Expand Down Expand Up @@ -336,6 +338,22 @@ private async Task DownloadFileAsync(
}
}

private HttpClientHandler CreateHttpClientHandler()
{
var handler = new HttpClientHandler();

// Configure SSL/TLS settings - bypass certificate validation when IgnoreUnknownCA is enabled
if (this.SnykOptions?.IgnoreUnknownCA == true)
{
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
}

// Note: Proxy configuration is not needed - HttpClient uses system proxy by default
// (HttpClientHandler.UseProxy = true by default, which means it will use system proxy)

return handler;
}

private void FinishDownload(ISnykProgressWorker progressWorker, List<CliDownloadFinishedCallback> downloadFinishedCallbacks)
{
Logger.Information("Fire DownloadFinished event");
Expand Down
26 changes: 25 additions & 1 deletion Snyk.VisualStudio.Extension.2022/SnykWebClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Net;
using System.Security.Cryptography.X509Certificates;
using Snyk.VisualStudio.Extension.Settings;

namespace Snyk.VisualStudio.Extension
{
Expand All @@ -8,15 +10,37 @@ namespace Snyk.VisualStudio.Extension
[System.ComponentModel.DesignerCategory("Code")] // To prevent VS from changing this file subtype to "Component" in the .csproj
public class SnykWebClient : WebClient
{
private readonly ISnykOptions options;

/// <summary>
/// Initializes a new instance of the <see cref="SnykWebClient"/> class.
/// </summary>
public SnykWebClient()
public SnykWebClient(ISnykOptions options = null)
: base()
{
this.options = options;
this.Headers.Add("User-Agent", "Snyk.VisualStudio.Extension");

ServicePointManager.Expect100Continue = true;

// Configure SSL/TLS settings - bypass certificate validation when IgnoreUnknownCA is enabled
if (options?.IgnoreUnknownCA == true)
{
ServicePointManager.ServerCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) => true;
}

// Note: Proxy configuration is not needed - WebClient uses system proxy by default
}

protected override void Dispose(bool disposing)
{
if (disposing && options?.IgnoreUnknownCA == true)
{
// Reset certificate validation callback to default
ServicePointManager.ServerCertificateValidationCallback = null;
}
base.Dispose(disposing);
}
}
}
93 changes: 92 additions & 1 deletion Snyk.VisualStudio.Extension.Tests/SnykCliDownloaderTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Sdk.TestFramework;
using Moq;
using Snyk.VisualStudio.Extension.CLI;
using Snyk.VisualStudio.Extension.Download;
Expand All @@ -10,17 +13,23 @@

namespace Snyk.VisualStudio.Extension.Tests
{
[Collection(MockedVS.Collection)]
public class SnykCliDownloaderTest
{
private Mock<ISnykProgressWorker> progressWorkerMock;
private Mock<ISnykOptions> optionsMock;
private readonly Mock<ISnykOptions> mockOptions;
private readonly SnykCliDownloader downloader;

public SnykCliDownloaderTest()
public SnykCliDownloaderTest(GlobalServiceProvider sp)
{
sp.Reset();
this.progressWorkerMock = new Mock<ISnykProgressWorker>();
this.optionsMock = new Mock<ISnykOptions>();
this.optionsMock.Setup(x => x.CliDownloadUrl).Returns(SnykCliDownloader.DefaultBaseDownloadUrl);
this.optionsMock.Setup(x => x.CliReleaseChannel).Returns("preview");
mockOptions = new Mock<ISnykOptions>();
downloader = new SnykCliDownloader(mockOptions.Object);
}

[Fact]
Expand Down Expand Up @@ -191,5 +200,87 @@ public async Task SnykCliDownloader_PreviousVersionOlderProvided_CliDownloadSucc
File.Delete(tempCliPath);
Assert.True(cliDownloader.IsCliDownloadNeeded(tempCliPath));
}

[Fact]
public void SnykWebClient_Should_Respect_IgnoreUnknownCA_Setting()
{
// Arrange
mockOptions.Setup(o => o.IgnoreUnknownCA).Returns(true);

// Act
using (var webClient = new SnykWebClient(mockOptions.Object))
{
// Assert
Assert.NotNull(webClient);
// The ServerCertificateValidationCallback should be set when IgnoreUnknownCA is true
Assert.NotNull(ServicePointManager.ServerCertificateValidationCallback);
}
}

[Fact]
public void SnykWebClient_Should_Handle_Null_Options()
{
// Act & Assert - should not throw
using (var webClient = new SnykWebClient(null))
{
Assert.NotNull(webClient);
}
}

[Fact]
public void CreateHttpClientHandler_Should_Respect_IgnoreUnknownCA_Setting()
{
// Arrange
mockOptions.Setup(o => o.IgnoreUnknownCA).Returns(true);
var downloader = new SnykCliDownloader(mockOptions.Object);

// Act
var handler = GetHttpClientHandler(downloader);

// Assert
Assert.NotNull(handler);
Assert.NotNull(handler.ServerCertificateCustomValidationCallback);

// Test that the callback returns true (accepts all certificates)
var result = handler.ServerCertificateCustomValidationCallback(
null, null, null, System.Net.Security.SslPolicyErrors.None);
Assert.True(result);
}

[Fact]
public void CreateHttpClientHandler_Should_Handle_Secure_Connection()
{
// Arrange
mockOptions.Setup(o => o.IgnoreUnknownCA).Returns(false);
var downloader = new SnykCliDownloader(mockOptions.Object);

// Act
var handler = GetHttpClientHandler(downloader);

// Assert
Assert.NotNull(handler);
Assert.Null(handler.ServerCertificateCustomValidationCallback);
// UseProxy should be true by default (system proxy)
Assert.True(handler.UseProxy);
}

[Fact]
public void HttpClient_Should_Use_System_Proxy_By_Default()
{
// Test to verify HttpClient uses system proxy by default
using (var defaultHandler = new HttpClientHandler())
{
// Assert default behavior - HttpClient should use system proxy by default
Assert.True(defaultHandler.UseProxy);
}
}

// Helper method to access the private CreateHttpClientHandler method
private HttpClientHandler GetHttpClientHandler(SnykCliDownloader downloader)
{
var method = typeof(SnykCliDownloader).GetMethod("CreateHttpClientHandler",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return (HttpClientHandler)method.Invoke(downloader, null);
}
}
}
Loading