Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CM-38194, CM-38195 - Add Open-source Threats (SCA) support #18

Merged
merged 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

## [Unreleased]

## [1.2.0] - 2024-08-XX

- Add Open-source Threats (SCA) support

## [1.1.4] - 2024-07-25

- Disable Sentry for on-premise installations
Expand Down Expand Up @@ -36,6 +40,8 @@

The first public release of the extension.

[1.2.0]: https://github.com/cycodehq/visual-studio-extension/releases/tag/v1.2.0

[1.1.4]: https://github.com/cycodehq/visual-studio-extension/releases/tag/v1.1.4

[1.1.3]: https://github.com/cycodehq/visual-studio-extension/releases/tag/v1.1.3
Expand All @@ -50,4 +56,4 @@ The first public release of the extension.

[1.0.0]: https://github.com/cycodehq/visual-studio-extension/releases/tag/v1.0.0

[Unreleased]: https://github.com/cycodehq/visual-studio-extension/compare/v1.1.4...HEAD
[Unreleased]: https://github.com/cycodehq/visual-studio-extension/compare/v1.2.0...HEAD
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="Cycode.7e1a0714-9b3b-4e0e-9c0a-d23fb20ab86e" Version="1.1.4" Language="en-US" Publisher="cycodehq" />
<Identity Id="Cycode.7e1a0714-9b3b-4e0e-9c0a-d23fb20ab86e" Version="1.2.0" Language="en-US" Publisher="cycodehq" />
<DisplayName>Cycode</DisplayName>
<Description xml:space="preserve">Cycode for Visual Studio IDE</Description>
<MoreInfo>https://github.com/cycodehq/visual-studio-extension</MoreInfo>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="Cycode.f2c5020e-67a2-46f8-a888-609412fd59db" Version="1.1.4" Language="en-US" Publisher="cycodehq" />
<Identity Id="Cycode.f2c5020e-67a2-46f8-a888-609412fd59db" Version="1.2.0" Language="en-US" Publisher="cycodehq" />
<DisplayName>Cycode</DisplayName>
<Description xml:space="preserve">Cycode for Visual Studio IDE</Description>
<MoreInfo>https://github.com/cycodehq/visual-studio-extension</MoreInfo>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
namespace Cycode.VisualStudio.Extension.Shared.Cli.DTO;

using System.Collections.Generic;
using System.Linq;

public static class ScaHelper {
// keep in lowercase.
// source: https://github.com/cycodehq/cycode-cli/blob/ec8333707ab2590518fd0f36454c8636ccbf1061/cycode/cli/consts.py#L50-L82
private static readonly List<string> _scaConfigurationScanSupportedFiles = [
"cargo.lock",
"cargo.toml",
"composer.json",
"composer.lock",
"go.sum",
"go.mod",
// "gopkg.toml", // FIXME(MarshalX): missed in CLI?
"gopkg.lock",
"pom.xml",
"build.gradle",
"gradle.lockfile",
"build.gradle.kts",
"package.json",
"package-lock.json",
"yarn.lock",
"npm-shrinkwrap.json",
"packages.config",
"project.assets.json",
"packages.lock.json",
"nuget.config",
".csproj",
"gemfile",
"gemfile.lock",
"build.sbt",
"build.scala",
"build.sbt.lock",
"pyproject.toml",
"poetry.lock",
"pipfile",
"pipfile.lock",
"requirements.txt",
"setup.py",
"mix.exs",
"mix.lock",
];

private static readonly Dictionary<string, string> _scaConfigurationScanLockFileToPackageFile =
new() {
{ "cargo.lock", "cargo.toml" },
{ "composer.lock", "composer.json" },
{ "go.sum", "go.mod" },
{ "gopkg.lock", "gopkg.toml" },
{ "gradle.lockfile", "build.gradle" },
{ "package-lock.json", "package.json" },
{ "yarn.lock", "package.json" },
{ "packages.lock.json", "nuget.config" },
{ "gemfile.lock", "gemfile" },
{ "build.sbt.lock", "build.sbt" }, // and build.scala?
{ "poetry.lock", "pyproject.toml" },
{ "pipfile.lock", "pipfile" },
{ "mix.lock", "mix.exs" },
};

private static readonly List<string> _scaConfigurationScanSupportedLockFiles =
_scaConfigurationScanLockFileToPackageFile.Keys.ToList();

public static bool IsSupportedPackageFile(string filename) {
string lowercaseFilename = filename.ToLowerInvariant();
return _scaConfigurationScanSupportedFiles.Any(file => lowercaseFilename.EndsWith(file));
}

public static bool IsSupportedLockFile(string filename) {
string lowercaseFilename = filename.ToLowerInvariant();
return _scaConfigurationScanSupportedLockFiles.Any(file => lowercaseFilename.EndsWith(file));
}

public static string GetPackageFileForLockFile(string filename) {
string lowercaseFilename = filename.ToLowerInvariant();
return _scaConfigurationScanLockFileToPackageFile.TryGetValue(lowercaseFilename, out string packageFile)
? packageFile
: "package";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public abstract class DetectionBase {
public string Severity { get; set; }
public DetectionDetailsBase DetectionDetailsBase { get; set; }
public DetectionDetailsBase DetectionDetails { get; set; }

public abstract string GetFormattedMessage();
public abstract string GetFormattedTitle();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Newtonsoft.Json;

namespace Cycode.VisualStudio.Extension.Shared.Cli.DTO.ScanResult.Sca;

public class ScaDetection : DetectionBase {
[JsonProperty(Required = Required.Always)]
public string Message { get; set; }

[JsonProperty(Required = Required.Always)]
public ScaDetectionDetails DetectionDetails { get; set; }

[JsonProperty(Required = Required.Always)]
public string Severity { get; set; }

[JsonProperty(Required = Required.Always)]
public string Type { get; set; }

[JsonProperty(Required = Required.Always)]
public string DetectionRuleId { get; set; }

[JsonProperty(Required = Required.Always)]
public string DetectionTypeId { get; set; }

public override string GetFormattedMessage() {
return Message;
}

public override string GetFormattedTitle() {
string message = DetectionDetails.VulnerabilityDescription ?? GetFormattedMessage();
return $"{DetectionDetails.PackageName}@{DetectionDetails.PackageVersion} - {message}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Newtonsoft.Json;

namespace Cycode.VisualStudio.Extension.Shared.Cli.DTO.ScanResult.Sca;

public class ScaDetectionDetails : DetectionDetailsBase {
[JsonProperty(Required = Required.Always)]
public string FileName { get; set; }

[JsonProperty(Required = Required.Always)]
public int StartPosition { get; set; }

[JsonProperty(Required = Required.Always)]
public int EndPosition { get; set; }

[JsonProperty(Required = Required.Always)]
public int Line { get; set; }

[JsonProperty(Required = Required.Always)]
public int LineInFile { get; set; }

[JsonProperty(Required = Required.Always)]
public string DependencyPaths { get; set; }

[JsonProperty(Required = Required.Always)]
public string PackageName { get; set; }

[JsonProperty(Required = Required.Always)]
public string PackageVersion { get; set; }

public string License { get; set; }
public string VulnerabilityDescription { get; set; }
public string VulnerabilityId { get; set; }
public ScaDetectionDetailsAlert Alert { get; set; }

public override string GetFilePath() {
return FileName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Newtonsoft.Json;

namespace Cycode.VisualStudio.Extension.Shared.Cli.DTO.ScanResult.Sca;

public class ScaDetectionDetailsAlert {
[JsonProperty(Required = Required.Always)]
public string Severity { get; set; }

[JsonProperty(Required = Required.Always)]
public string Summary { get; set; }

[JsonProperty(Required = Required.Always)]
public string Description { get; set; }

public string VulnerableRequirements { get; set; }
public string FirstPatchedVersion { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System.Collections.Generic;

namespace Cycode.VisualStudio.Extension.Shared.Cli.DTO.ScanResult.Sca;

public class ScaScanResult : ScanResultBase {
public List<ScaDetection> Detections { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class SecretDetection: DetectionBase {
public SecretDetectionDetails DetectionDetails { get; set; }

[JsonProperty(Required = Required.Always)]
public new string Severity { get; set; }
public string Severity { get; set; }

[JsonProperty(Required = Required.Always)]
public string Type { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) {
}

ICycodeService cycode = ServiceLocator.GetService<ICycodeService>();
await cycode.StartSecretScanForCurrentProjectAsync();
cycode.StartSecretScanForCurrentProjectAsync().FireAndForget();
cycode.StartScaScanForCurrentProjectAsync().FireAndForget();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\CliResult.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\CliScanType.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\IdeUserAgent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScaHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\DetectionBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\DetectionDetailsBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\ScanResultBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\Sca\ScaDetection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\Sca\ScaDetectionDetails.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\Sca\ScaDetectionDetailsAlert.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\Sca\ScaScanResult.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\Secret\SecretDetection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\Secret\SecretDetectionDetails.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Cli\DTO\ScanResult\Secret\SecretScanResult.cs" />
Expand Down Expand Up @@ -49,14 +54,18 @@
<Compile Include="$(MSBuildThisFileDirectory)Services\CycodeService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\DocTableEventsHandlerService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\DownloadService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\TaskCreators\ScaErrorTaskCreator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\TaskCreators\SecretsErrorTaskCreator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\ErrorCategoryUtilities.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\ErrorListService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\ErrorTagger.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\ErrorTaggerProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\IErrorListService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\IErrorTaskCreatorService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\SecretsErrorTaskCreator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorList\ErrorTaskCreatorService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorTagger\ErrorTagger.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorTagger\ErrorTaggerProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorTagger\ErrorTaggerUtilities.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorTagger\TagSpansCreators\ScaTagSpansCreator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ErrorTagger\TagSpansCreators\SecretsTagSpansCreator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\GithubReleasesService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ICliDownloadService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ICliService.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Cycode.VisualStudio.Extension.Shared.Cli;
using Cycode.VisualStudio.Extension.Shared.Cli.DTO;
using Cycode.VisualStudio.Extension.Shared.Cli.DTO.ScanResult;
using Cycode.VisualStudio.Extension.Shared.Cli.DTO.ScanResult.Sca;
using Cycode.VisualStudio.Extension.Shared.Cli.DTO.ScanResult.Secret;
using Cycode.VisualStudio.Extension.Shared.DTO;
using Cycode.VisualStudio.Extension.Shared.Helpers;
Expand Down Expand Up @@ -139,8 +139,15 @@ public async Task<bool> DoAuthAsync(TaskCancelledCallback cancelledCallback = nu
}

private static string[] GetCliScanOptions(CliScanType scanType) {
// TODO(MarshalX): for Sca
return [];
List<string> options = [];

if (scanType != CliScanType.Sca) return options.ToArray();

// SCA specific options to performs it faster
options.Add("--sync");
options.Add("--no-restore");

return options.ToArray();
}

private async Task<CliResult<T>> ScanPathsAsync<T>(
Expand All @@ -163,7 +170,7 @@ public async Task ScanPathsSecretsAsync(
CliResult<SecretScanResult> results =
await ScanPathsAsync<SecretScanResult>(paths, CliScanType.Secret, cancelledCallback);
if (results == null) {
logger.Warn("Failed to scan paths: {0}", string.Join(", ", paths));
logger.Warn("Failed to scan Secret paths: {0}", string.Join(", ", paths));
return;
}

Expand All @@ -176,4 +183,24 @@ public async Task ScanPathsSecretsAsync(

ShowScanFileResultNotification(CliScanType.Secret, detectionsCount, onDemand);
}

public async Task ScanPathsScaAsync(
List<string> paths, bool onDemand = true, TaskCancelledCallback cancelledCallback = null
) {
CliResult<ScaScanResult> results =
await ScanPathsAsync<ScaScanResult>(paths, CliScanType.Sca, cancelledCallback);
if (results == null) {
logger.Warn("Failed to scan SCA paths: {0}", string.Join(", ", paths));
return;
}

int detectionsCount = 0;
if (results is CliResult<ScaScanResult>.Success successResult) {
detectionsCount = successResult.Result.Detections.Count;
scanResultsService.SetScaResults(successResult.Result);
errorTaskCreatorService.RecreateAsync().FireAndForget();
}

ShowScanFileResultNotification(CliScanType.Sca, detectionsCount, onDemand);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,37 @@ private async Task StartPathSecretScanInternalAsync(List<string> pathsToScan, bo
await cliService.ScanPathsSecretsAsync(pathsToScan, onDemand);
logger.Debug("[Secret] Finish scanning paths: {0}", string.Join(", ", pathsToScan));
}

public async Task StartScaScanForCurrentProjectAsync() {
string projectRoot = SolutionHelper.GetSolutionRootDirectory();
if (projectRoot == null) {
logger.Warn("Failed to get current project root. Aborting scan...");
return;
}

await StartPathScaScanAsync(projectRoot, onDemand: true);
}

public async Task StartPathScaScanAsync(string pathToScan, bool onDemand = false) {
await StartPathScaScanAsync([pathToScan], onDemand);
}

public async Task StartPathScaScanAsync(List<string> pathsToScan, bool onDemand = false) {
await WrapWithStatusCenterAsync(
taskFunction: () => StartPathScaScanInternalAsync(pathsToScan, onDemand),
label: "Cycode is scanning files for package vulnerabilities...",
canBeCanceled: false // TODO(MarshalX): Should be cancellable. Not implemented yet
);
}

private async Task StartPathScaScanInternalAsync(List<string> pathsToScan, bool onDemand = false) {
if (!_pluginState.CliAuthed) {
logger.Debug("Not authenticated with Cycode CLI. Aborting scan...");
return;
}

logger.Debug("[SCA] Start scanning paths: {0}", string.Join(", ", pathsToScan));
await cliService.ScanPathsScaAsync(pathsToScan, onDemand);
logger.Debug("[SCA] Finish scanning paths: {0}", string.Join(", ", pathsToScan));
}
}
Loading
Loading