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
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ public static ImageSource LoadSeverityIcon(SeverityLevel severity)
if (!_popupIconsLogged)
{
_popupIconsLogged = true;
System.Diagnostics.Debug.WriteLine($"[{CxAssistConstants.LogCategory}] {string.Format(CxAssistConstants.ICONS_LOADED_FOR_THEME, currentTheme)}");
CxAssistOutputPane.WriteToOutputPane(string.Format(CxAssistConstants.ICONS_LOADED_FOR_THEME, currentTheme));
}
return img;
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,25 @@ public static string GetRichSeverityName(SeverityLevel severity)
public const string CopilotOpenInstructionsMessage = "Prompt copied to clipboard! Paste it into GitHub Copilot Chat (Agent Mode).";
public const string CopilotGenericFallbackMessage = "Prompt copied to clipboard. Paste into GitHub Copilot Chat.";

/// <summary>Non-modal (info bar) when Copilot extension/commands are not registered.</summary>
public const string CopilotNotInstalledInfoBarMessage =
"GitHub Copilot is not installed. Your prompt was copied to the clipboard—install GitHub Copilot, open Copilot Chat, paste the prompt, switch to Agent mode, then submit.";

/// <summary>Non-modal when Copilot Chat UI could not be opened (commands may exist but host failed).</summary>
public const string CopilotChatOpenFailedInfoBarMessage =
"Could not open GitHub Copilot Chat. Your prompt was copied to the clipboard—open Copilot Chat manually, paste the prompt, switch to Agent mode, then submit.";

/// <summary>Non-modal when the user is not in Agent mode; prompt was pasted without sending.</summary>
public const string CopilotNotAgentModeInfoBarMessage =
"GitHub Copilot Chat is not in Agent mode. Your prompt is ready in Copilot Chat—switch to Agent mode, then submit.";

/// <summary>Non-modal for VS 2026+; mode detection is unavailable so prompt is pasted without auto-submit in any mode.</summary>
public const string CopilotPasteOnlyVs2026InfoBarMessage =
"Prompt pasted into GitHub Copilot Chat. Please switch to Agent mode (Ignore if already in Agent mode) and press Enter to submit.";

/// <summary>Non-modal when paste/focus into Copilot input failed.</summary>
public const string CopilotPromptPrepareFailedInfoBarMessage = "Unable to prepare prompt in Copilot.";

/// <summary>Context menu / Error List / Quick Info / Quick Fix: "Ignore this [finding type]" label based on scanner.</summary>
public static string GetIgnoreThisLabel(ScannerType scanner)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Windows;
using ast_visual_studio_extension.CxExtension.CxAssist.Core.Models;
using ast_visual_studio_extension.CxExtension.CxAssist.Core.Prompts;

Expand Down Expand Up @@ -84,9 +83,9 @@ public static void SendViewDetails(Vulnerability v, IReadOnlyList<Vulnerability>
private static void ShowNoPromptMessage(string detail, bool isFix)
{
string message = isFix
? "No fix prompt available for this finding.\n" + detail
: "View Details:\n" + detail;
MessageBox.Show(message, CxAssistConstants.DisplayName, MessageBoxButton.OK, MessageBoxImage.Information);
? "No fix prompt available for this finding. " + detail
: "View Details: " + detail;
CopilotIntegration.ShowAssistNotification(message, isError: false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -357,5 +357,54 @@ public static void RefreshProblemWindow(
findingsControl.SetAllFileNodes(fileNodes);
}, "Coordinator.RefreshProblemWindow");
}

/// <summary>
/// Clears all findings from the coordinator.
/// Used when user logs out or disables all scanners.
/// </summary>
public static void ClearAllFindings()
{
lock (_lock)
{
_fileToIssues.Clear();
}
IssuesUpdated?.Invoke(new Dictionary<string, List<Vulnerability>>());
}

/// <summary>
/// Clears findings from disabled scanners only.
/// Called when user toggles a scanner off via preferences.
/// Aligned with JetBrains ProblemHolderService.removeAllScanIssuesOfType().
/// </summary>
public static void ClearFindingsFromDisabledScanners()
Comment thread
cx-rahul-pidde marked this conversation as resolved.
{
var filesToRemove = new List<string>();

lock (_lock)
{
// Iterate all files and remove findings from disabled scanners
foreach (var filePath in _fileToIssues.Keys.ToList())
{
if (!_fileToIssues.TryGetValue(filePath, out var vulnerabilities) || vulnerabilities == null)
continue;

// Keep only findings from enabled scanners
_fileToIssues[filePath] = vulnerabilities
.Where(v => v != null && CxAssistConstants.IsScannerEnabled(v.Scanner))
.ToList();

// Mark file for removal if no vulnerabilities remain
if (_fileToIssues[filePath].Count == 0)
filesToRemove.Add(filePath);
}

// Remove files with no remaining vulnerabilities
foreach (var filePath in filesToRemove)
_fileToIssues.Remove(filePath);
}

// Broadcast update to all UI subscribers
IssuesUpdated?.Invoke(GetAllIssuesByFile());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="Padding" Value="2,2"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<EventSetter Event="MouseDoubleClick" Handler="TreeViewItem_MouseDoubleClick"/>
<EventSetter Event="PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
<Setter Property="Template">
Expand Down Expand Up @@ -80,7 +82,8 @@
<ContentPresenter x:Name="PART_Header"
Grid.Column="1"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
HorizontalAlignment="Left"
VerticalAlignment="Center"/>
</Grid>
</Border>
<!-- Children (reduced left margin so vulnerability rows don't have huge gap) -->
Expand Down Expand Up @@ -331,6 +334,8 @@
BorderThickness="0"
Background="Transparent"
Foreground="{DynamicResource {x:Static vsui:EnvironmentColors.ToolWindowTextBrushKey}}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Center"
SelectedItemChanged="FindingsTreeView_SelectedItemChanged"
ContextMenuOpening="FindingsTreeView_ContextMenuOpening">

Expand Down Expand Up @@ -385,8 +390,9 @@
</ContextMenu.Resources>
<MenuItem Header="Fix with Checkmarx One Assist" Click="FixWithCxOneAssist_Click"/>
<MenuItem Header="View details" Click="ViewDetails_Click"/>
<MenuItem x:Name="IgnoreThisMenuItem" Header="Ignore this vulnerability" Click="Ignore_Click"/>
<MenuItem x:Name="IgnoreAllMenuItem" Header="Ignore all of this type" Click="IgnoreAll_Click"/>
<!-- TODO: Ignore feature not yet implemented - hidden for now -->
<!--<MenuItem x:Name="IgnoreThisMenuItem" Header="Ignore this vulnerability" Click="Ignore_Click"/>
<MenuItem x:Name="IgnoreAllMenuItem" Header="Ignore all of this type" Click="IgnoreAll_Click"/>-->
<!-- Full-width HR: Border as item so it stretches to menu edges (theme-aware grey) -->
<Border Height="1" Margin="0,2,0,2" VerticalAlignment="Center"
Background="{DynamicResource {x:Static vsfx:VsBrushes.ToolWindowBorderKey}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
_allFileNodes = new ObservableCollection<FileNode>();
DataContext = this;

System.Diagnostics.Debug.WriteLine($"[{CxAssistConstants.LogCategory}] {CxAssistConstants.FINDINGS_WINDOW_INITIATED}");
CxAssistOutputPane.WriteToOutputPane(CxAssistConstants.FINDINGS_WINDOW_INITIATED);

Loaded += OnLoaded;
Unloaded += OnUnloaded;
Expand Down Expand Up @@ -150,8 +150,9 @@

private void OnIssuesUpdated(IReadOnlyDictionary<string, List<Core.Models.Vulnerability>> issuesByFile)
{
// Coordinator raises IssuesUpdated from UI thread (callers use SwitchToMainThreadAsync).
RefreshFromCoordinator();
// IssuesUpdated can fire from a background scanner thread — marshal to UI thread
// so ApplyFilters can safely read ToggleButton.IsChecked and update FileNodes.
Dispatcher.InvokeAsync(RefreshFromCoordinator);
}

/// <summary>
Expand Down Expand Up @@ -611,7 +612,10 @@
private void ApplyFilters()
{
if (_allFileNodes == null || _allFileNodes.Count == 0)
{
FileNodes = new ObservableCollection<FileNode>();
return;
}

// Get active filters
var activeFilters = new System.Collections.Generic.List<string>();
Expand Down Expand Up @@ -682,7 +686,7 @@
public void SetAllFileNodes(ObservableCollection<FileNode> allNodes)
{
_allFileNodes = allNodes;
FileNodes = new ObservableCollection<FileNode>(allNodes);
ApplyFilters();
}

#endregion
Expand Down Expand Up @@ -856,7 +860,7 @@
}
catch
{
System.Diagnostics.Debug.WriteLine($"[{CxAssistConstants.LogCategory}] {CxAssistConstants.FAILED_COPY_CLIPBOARD}");
CxAssistOutputPane.WriteToOutputPane(CxAssistConstants.FAILED_COPY_CLIPBOARD);
}
}

Expand Down Expand Up @@ -895,9 +899,9 @@
ShowStatusBarNotification("Message copied to clipboard.");
}
}
catch (Exception ex)
catch (Exception)
{
System.Diagnostics.Debug.WriteLine($"[{CxAssistConstants.LogCategory}] {CxAssistConstants.FAILED_COPY_CLIPBOARD}");
CxAssistOutputPane.WriteToOutputPane(CxAssistConstants.FAILED_COPY_CLIPBOARD);
}
}

Expand Down Expand Up @@ -925,11 +929,11 @@
{
Clipboard.SetText(prompt);
ShowStatusBarNotification("Fix prompt copied to clipboard. Paste into GitHub Copilot Chat to get remediation steps.");
System.Diagnostics.Debug.WriteLine($"[{CxAssistConstants.LogCategory}] {string.Format(CxAssistConstants.FIX_PROMPT_COPIED, v.Title ?? v.Description ?? "unknown")}");
CxAssistOutputPane.WriteToOutputPane(string.Format(CxAssistConstants.FIX_PROMPT_COPIED, v.Title ?? v.Description ?? "unknown"));
}
catch (Exception ex)

Check warning on line 934 in ast-visual-studio-extension/CxExtension/CxAssist/UI/FindingsWindow/CxAssistFindingsControl.xaml.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The variable 'ex' is declared but never used
{
System.Diagnostics.Debug.WriteLine($"[{CxAssistConstants.LogCategory}] {CxAssistConstants.FAILED_COPY_CLIPBOARD}");
CxAssistOutputPane.WriteToOutputPane(CxAssistConstants.FAILED_COPY_CLIPBOARD);
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions ast-visual-studio-extension/CxExtension/CxWindowPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
using ast_visual_studio_extension.CxExtension.CxAssist.Realtime;
using ast_visual_studio_extension.CxPreferences;
using ast_visual_studio_extension.CxPreferences.Configuration;
using ast_visual_studio_extension.CxExtension.Commands;

Check warning on line 6 in ast-visual-studio-extension/CxExtension/CxWindowPackage.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The using directive for 'ast_visual_studio_extension.CxExtension.Commands' appeared previously in this namespace

Check warning on line 6 in ast-visual-studio-extension/CxExtension/CxWindowPackage.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The using directive for 'ast_visual_studio_extension.CxExtension.Commands' appeared previously in this namespace
using ast_visual_studio_extension.CxPreferences.Configuration;

Check warning on line 7 in ast-visual-studio-extension/CxExtension/CxWindowPackage.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The using directive for 'ast_visual_studio_extension.CxPreferences.Configuration' appeared previously in this namespace

Check warning on line 7 in ast-visual-studio-extension/CxExtension/CxWindowPackage.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The using directive for 'ast_visual_studio_extension.CxPreferences.Configuration' appeared previously in this namespace
using ast_visual_studio_extension.CxExtension.Commands;

Check warning on line 8 in ast-visual-studio-extension/CxExtension/CxWindowPackage.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The using directive for 'ast_visual_studio_extension.CxExtension.Commands' appeared previously in this namespace
using ast_visual_studio_extension.CxExtension.CxAssist.Core;
using log4net;
using log4net.Appender;
Expand Down Expand Up @@ -99,6 +99,11 @@
Debug.WriteLine("CxWindowPackage: Solution event handler registered");
}

// Initialize Error List sync so findings appear in both Findings window and VS Error List
_CxAssistErrorListSync = new CxAssistErrorListSync();
_CxAssistErrorListSync.Start();
Debug.WriteLine("CxWindowPackage: Error List sync initialized");

// JetBrains GlobalScannerController.settingsApplied → syncAll: resync realtime when Assist prefs change (no tool window required).
CxOneAssistSettingsModule.RealtimeAssistSettingsChanged += OnRealtimeAssistSettingsApplied;

Expand Down Expand Up @@ -246,6 +251,10 @@
_ = JoinableTaskFactory.RunAsync(async () =>
{
await JoinableTaskFactory.SwitchToMainThreadAsync();

// Clear findings from disabled scanners only (user toggled scanner off)
CxAssistDisplayCoordinator.ClearFindingsFromDisabledScanners();

await RealtimeScannerHost.ResyncFromPersistedSettingsAsync(this, typeof(CxWindowPackage));
});
}
Expand All @@ -260,6 +269,9 @@
await JoinableTaskFactory.SwitchToMainThreadAsync();
try
{
// Clear ALL findings on logout (user logged out)
CxAssistDisplayCoordinator.ClearAllFindings();

await RealtimeScannerHost.UnregisterAsync();
}
catch (Exception ex)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions ast-visual-studio-extension/CxExtension/Utils/CxConstants.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
namespace ast_visual_studio_extension.CxExtension.Utils
namespace ast_visual_studio_extension.CxExtension.Utils
{
internal class CxConstants
{
/************ GENERAL ************/
public static string EXTENSION_TITLE => "Checkmarx One";
public static string EXTENSION_TITLE => "Checkmarx";

/// <summary>VS Output window pane title for Assist and scanner logs (single pane; not the tool window caption).</summary>
public static string OutputWindowPaneName => "Checkmarx";
public static string TREE_PARENT_NODE => "Scan {0}";
public static string TREE_PARENT_NODE_NO_RESULTS => "No results for scan {0}";
public static string DESC_TAB_LBL_ACTUAL_VALUE => "Actual value: ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ast_visual_studio_extension.CxExtension.CxAssist.Core;
using ast_visual_studio_extension.CxExtension.CxAssist.Core.Models;

namespace ast_visual_studio_extension.CxPreferences
{
Expand Down Expand Up @@ -89,10 +91,19 @@ protected override void OnApply(PageApplyEventArgs e)

/// <summary>
/// Explicitly persist all module settings to the registry and notify listeners to resync realtime scanners.
/// Also syncs scanner enable/disable state so ClearFindingsFromDisabledScanners() has correct state.
/// </summary>
public void PersistSettings()
{
SaveSettingsToStorage();

// Sync scanner enabled/disabled state BEFORE firing the event so ClearFindingsFromDisabledScanners() has current state
CxAssistConstants.SetScannerEnabled(ScannerType.ASCA, AscaCheckBox);
CxAssistConstants.SetScannerEnabled(ScannerType.OSS, OssRealtimeCheckBox);
CxAssistConstants.SetScannerEnabled(ScannerType.Secrets, SecretDetectionRealtimeCheckBox);
CxAssistConstants.SetScannerEnabled(ScannerType.Containers, ContainersRealtimeCheckBox);
CxAssistConstants.SetScannerEnabled(ScannerType.IaC, IacRealtimeCheckBox);

RealtimeAssistSettingsChanged?.Invoke(this, EventArgs.Empty);
}

Expand Down
43 changes: 38 additions & 5 deletions ast-visual-studio-extension/CxPreferences/CxOneAssistSettingsUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public partial class CxOneAssistSettingsUI : UserControl
private bool _isMcpInstallInProgress;
private CancellationTokenSource _mcpStatusDismissCts;
private bool _isAuthEventSubscribed;
private System.Threading.Timer _scannerCheckboxDebounceTimer;
private const int DebounceDelayMs = 300; // Batch checkbox changes within 300ms

private static readonly Color McpSuccessColor = Color.FromArgb(0, 120, 50);
private static readonly Color McpErrorColor = Color.FromArgb(160, 0, 0);
Expand Down Expand Up @@ -181,35 +183,66 @@ private void AscaCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (cxOneAssistSettingsModule == null || !CxPreferencesUI.IsAuthenticated())
return;
SyncAssistUiToModuleProperties();
DebounceSyncAssistUi();
}

private void OssCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (cxOneAssistSettingsModule == null || !CxPreferencesUI.IsAuthenticated())
return;
SyncAssistUiToModuleProperties();
DebounceSyncAssistUi();
}

private void SecretsCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (cxOneAssistSettingsModule == null || !CxPreferencesUI.IsAuthenticated())
return;
SyncAssistUiToModuleProperties();
DebounceSyncAssistUi();
}

private void ContainersCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (cxOneAssistSettingsModule == null || !CxPreferencesUI.IsAuthenticated())
return;
SyncAssistUiToModuleProperties();
DebounceSyncAssistUi();
}

private void IacCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (cxOneAssistSettingsModule == null || !CxPreferencesUI.IsAuthenticated())
return;
SyncAssistUiToModuleProperties();
DebounceSyncAssistUi();
}

/// <summary>
/// Debounces scanner checkbox changes so multiple checkbox clicks batch into a single sync operation.
/// This prevents enabling/disabling scanners one-by-one which causes delays.
/// </summary>
private void DebounceSyncAssistUi()
{
// Dispose existing timer
if (_scannerCheckboxDebounceTimer != null)
{
_scannerCheckboxDebounceTimer.Dispose();
}

// Start new timer to sync after user stops clicking
_scannerCheckboxDebounceTimer = new System.Threading.Timer(
callback: (_) =>
{
if (InvokeRequired)
{
BeginInvoke((Action)SyncAssistUiToModuleProperties);
}
else
{
SyncAssistUiToModuleProperties();
}
_scannerCheckboxDebounceTimer?.Dispose();
},
state: null,
dueTime: DebounceDelayMs,
period: Timeout.Infinite);
}

private void CmbContainersTool_SelectedIndexChanged(object sender, EventArgs e)
Expand Down
Loading
Loading