Skip to content

Commit 1e6b4c7

Browse files
Plugin will display an error message when no valid projects are found in the open solution (AST-116483) (#291)
* Improve Visual Studio Plugin Behavior When No Projects Are Detected in Solution (AST-AST-116483) * fix bug #AST-120978 --------- Co-authored-by: cx-Margarita-LevitM <cx-margarita-levitm>
1 parent f7ae7d1 commit 1e6b4c7

3 files changed

Lines changed: 179 additions & 16 deletions

File tree

ast-visual-studio-extension/CxExtension/Toolbar/CxToolbar.cs

Lines changed: 155 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,61 @@ internal class CxToolbar
5050
public Func<List<State>, Dictionary<MenuItem, State>> CreateStateMenuItems { get; set; }
5151

5252
private static bool initPolling = false;
53+
54+
public static bool IsValidSourceProject(string sourcePath)
55+
{
56+
if (string.IsNullOrEmpty(sourcePath))
57+
{
58+
return false;
59+
}
60+
61+
try
62+
{
63+
string searchPath;
64+
if (System.IO.File.Exists(sourcePath))
65+
{
66+
searchPath = System.IO.Path.GetDirectoryName(sourcePath);
67+
}
68+
else if (System.IO.Directory.Exists(sourcePath))
69+
{
70+
searchPath = sourcePath;
71+
}
72+
else
73+
{
74+
return false;
75+
}
76+
77+
string[] projectExtensions = { "*.sln", "*.csproj" };
78+
79+
foreach (string extension in projectExtensions)
80+
{
81+
var files = System.IO.Directory.GetFiles(searchPath, extension, System.IO.SearchOption.AllDirectories);
82+
if (files.Any(file => IsValidProjectFile(file)))
83+
return true;
84+
}
85+
86+
return false;
87+
}
88+
catch (Exception ex)
89+
{
90+
UpdateStatusBar("Checkmarx: Error validating project directory" + ex.Message);
91+
return false;
92+
}
93+
}
94+
95+
96+
private static bool IsValidProjectFile(string filePath)
97+
{
98+
try
99+
{
100+
var fileInfo = new System.IO.FileInfo(filePath);
101+
return fileInfo.Exists && fileInfo.Length > 0;
102+
}
103+
catch
104+
{
105+
return false;
106+
}
107+
}
53108
private const string DevOrTestFilterName = "SCA Dev & Test Dependencies";
54109

55110

@@ -286,6 +341,13 @@ public async Task ScanStart_ClickAsync()
286341
return;
287342
}
288343

344+
if (!IsValidSourceProject(dte.Solution.FullName))
345+
{
346+
CxUtils.DisplayMessageInInfoWithLinkBar(Package, CxConstants.NOT_A_VALID_PROJECT, KnownMonikers.StatusError, "Project Error", "", false);
347+
ScanStartButton.IsEnabled = true;
348+
return;
349+
}
350+
289351
var currentGitBranch = await GetCurrentGitBranchAsync(dte);
290352
var checkmarxBranch = SettingsUtils.GetToolbarValue(Package, SettingsUtils.branchProperty);
291353
var matchProject = await ASTProjectMatchesWorkspaceProjectAsync(dte);
@@ -322,6 +384,9 @@ private static async Task<string> GetCurrentGitBranchAsync(EnvDTE.DTE dte)
322384
try
323385
{
324386
string workingDir = System.IO.Path.GetDirectoryName(dte.Solution.FullName);
387+
if (string.IsNullOrEmpty(workingDir) || !System.IO.Directory.Exists(workingDir))
388+
return null;
389+
325390
RepositoryInformation repository = RepositoryInformation.GetRepositoryInformation(workingDir);
326391

327392
if (repository == null)
@@ -341,9 +406,9 @@ private static async Task<string> GetCurrentGitBranchAsync(EnvDTE.DTE dte)
341406

342407
private static async Task<bool> ASTProjectMatchesWorkspaceProjectAsync(EnvDTE.DTE dte)
343408
{
344-
if (ResultsTreePanel.currentResults == null || !ResultsTreePanel.currentResults.results.Any())
409+
if (ResultsTreePanel.currentResults == null | ResultsTreePanel.currentResults.results == null || ResultsTreePanel.currentResults.results.Any())
345410
{
346-
return true;
411+
return false;
347412
}
348413

349414
List<Result> astResults = ResultsTreePanel.currentResults.results;
@@ -434,6 +499,13 @@ private async Task StartScanAsync()
434499

435500
string currentPath = await GetCurrentWorkingDirAsync();
436501

502+
// Check if a valid project/solution was found
503+
if (string.IsNullOrEmpty(currentPath))
504+
{
505+
UpdateStatusBar(CxConstants.NOT_A_VALID_PROJECT);
506+
return;
507+
}
508+
437509
Dictionary<string, string> parameters = new Dictionary<string, string>
438510
{
439511
{ CxCLI.CxConstants.FLAG_SOURCE, currentPath },
@@ -462,14 +534,91 @@ private static async Task<string> GetCurrentWorkingDirAsync()
462534
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
463535

464536
DTE2 dte = (DTE2)ServiceProvider.GlobalProvider.GetService(typeof(DTE));
537+
var solutionExplorer = dte?.ToolWindows?.SolutionExplorer;
538+
539+
// Try to get directory from solution or active projects
540+
string directory = null;
541+
542+
if (!string.IsNullOrEmpty(dte?.Solution?.FullName))
543+
{
544+
// Solution is loaded - get its directory or use the path itself if it's a directory
545+
if (System.IO.File.Exists(dte.Solution.FullName))
546+
{
547+
directory = System.IO.Path.GetDirectoryName(dte.Solution.FullName);
548+
}
549+
else if (System.IO.Directory.Exists(dte.Solution.FullName))
550+
{
551+
directory = dte.Solution.FullName;
552+
}
553+
}
554+
else if (solutionExplorer?.DTE?.ActiveSolutionProjects is Array projects && projects.Length > 0)
555+
{
556+
// Try to get directory from first active project
557+
var firstProject = projects.GetValue(0) as EnvDTE.Project;
558+
if (firstProject != null && !string.IsNullOrEmpty(firstProject.FullName))
559+
{
560+
directory = System.IO.Path.GetDirectoryName(firstProject.FullName);
561+
}
562+
}
563+
564+
// If we still don't have a directory, try current directory
565+
if (string.IsNullOrEmpty(directory))
566+
{
567+
try
568+
{
569+
directory = System.IO.Directory.GetCurrentDirectory();
570+
}
571+
catch
572+
{
573+
return null;
574+
}
575+
}
465576

466-
var solutionExplorer = dte.ToolWindows.SolutionExplorer;
577+
// Now search for .sln or .csproj in the directory
578+
if (!string.IsNullOrEmpty(directory) && System.IO.Directory.Exists(directory))
579+
{
580+
return FindSolutionFileOrDirectory(directory);
581+
}
582+
583+
return null;
584+
}
585+
586+
private static string FindSolutionFileOrDirectory(string directory)
587+
{
588+
if (string.IsNullOrEmpty(directory) || !System.IO.Directory.Exists(directory))
589+
{
590+
return null;
591+
}
592+
593+
// Look for .sln file in the directory
594+
var slnFiles = System.IO.Directory.GetFiles(directory, "*.sln", System.IO.SearchOption.TopDirectoryOnly);
595+
if (slnFiles.Length > 0)
596+
{
597+
// Return the first valid .sln file
598+
foreach (var slnFile in slnFiles)
599+
{
600+
if (IsValidProjectFile(slnFile))
601+
{
602+
return directory;
603+
}
604+
}
605+
}
467606

468-
if ((solutionExplorer.DTE.ActiveSolutionProjects as Array)?.Length > 0)
607+
// If no .sln found, look for .csproj files
608+
var csprojFiles = System.IO.Directory.GetFiles(directory, "*.csproj", System.IO.SearchOption.TopDirectoryOnly);
609+
if (csprojFiles.Length > 0)
469610
{
470-
return System.IO.Path.GetDirectoryName(dte.Solution.FullName);
611+
foreach (var csprojFile in csprojFiles)
612+
{
613+
if (IsValidProjectFile(csprojFile))
614+
{
615+
return directory;
616+
}
617+
}
471618
}
472-
return ".";
619+
620+
// No valid .sln or .csproj found - return null to trigger error
621+
return null;
473622
}
474623

475624
private async Task PollScanStartedAsync()

ast-visual-studio-extension/CxExtension/Utils/CxConstants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ internal class CxConstants
9393
public static string PROJECT_AND_BRANCH_DO_NOT_MATCH => "The Git branch and files open in your workspace don't match the branch and project that were previously scanned in this Checkmarx project. Do you want to scan anyway?";
9494
public static string RUN_SCAN => "Run scan";
9595
public static string RUN_SCAN_ACTION => "RUN_SCAN_ACTION";
96+
public static string NOT_A_VALID_PROJECT => "No .NET project files were found in the source directory.";
97+
9698

9799
/** LEARN MORE AND REMEDIATION **/
98100
public static string CODE_SAMPLE_TITLE => "{0} using {1}";

ast-visual-studio-extension/CxExtension/Utils/SolutionExplorerUtils.cs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,22 @@ internal static Task<List<string>> SearchAllFilesAsync(string partialFileLocatio
100100

101101
List<string> allFiles = new List<string>();
102102

103-
allFiles.AddRange(await
104-
GetAllProjectFilesAsync(Path.GetDirectoryName(dte.Solution.FullName), partialFileLocation, new string[] { "bin", "obj", "packages", "node_modules", ".git", ".vs" }));
103+
if (!string.IsNullOrEmpty(dte.Solution.FullName))
104+
{
105+
allFiles.AddRange(await
106+
GetAllProjectFilesAsync(dte.Solution.FullName, partialFileLocation, new string[] { "bin", "obj", "packages", "node_modules", ".git", ".vs" }));
107+
}
105108

106109
if (allFiles.Count == 0) {
107110
foreach (EnvDTE.Project project in dte.Solution.Projects)
108111
{
109112
if (!await IsProjectLoadedAsync(project)) continue;
110113

111-
FileInfo projectFileInfo = new FileInfo(project.FullName);
112-
string projectPath = Directory.GetParent(projectFileInfo.Directory.FullName).FullName;
113-
114-
string[] files = await GetAllProjectFilesAsync(projectPath, partialFileLocation, new string[] { "bin", "obj", "packages", "node_modules", ".git", ".vs" });
115-
116-
allFiles.AddRange(files);
114+
if (!string.IsNullOrEmpty(dte.Solution.FullName))
115+
{
116+
string[] files = await GetAllProjectFilesAsync(dte.Solution.FullName, partialFileLocation, new string[] { "bin", "obj", "packages", "node_modules", ".git", ".vs" });
117+
allFiles.AddRange(files);
118+
}
117119
}
118120
}
119121

@@ -151,15 +153,25 @@ static async Task<string[]> GetAllProjectFilesAsync(string path, string pathStri
151153
{
152154
List<string> files = new List<string>();
153155

156+
// Validate the input path
157+
if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
158+
{
159+
return Array.Empty<string>();
160+
}
161+
154162
string[] topLevelFiles = await Task.Run(() => Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly));
155163
files.AddRange(topLevelFiles.Where(file => file.EndsWith(pathString)));
156164

157165
foreach (string directory in Directory.GetDirectories(path))
158166
{
159167
if (!excludedDirectories.Contains(Path.GetFileName(directory)))
160168
{
161-
string[] subdirectoryFiles = await GetAllProjectFilesAsync(directory, pathString, excludedDirectories);
162-
files.AddRange(subdirectoryFiles);
169+
// Validate subdirectory exists before recursive call
170+
if (Directory.Exists(directory))
171+
{
172+
string[] subdirectoryFiles = await GetAllProjectFilesAsync(directory, pathString, excludedDirectories);
173+
files.AddRange(subdirectoryFiles);
174+
}
163175
}
164176
}
165177

0 commit comments

Comments
 (0)