- ${
- !projectName || !scan || !isLatestScan
- ? `
- ASPM data is only shown when the most recent scan of a project is selected
- in the Checkmarx One Results tab
-
`
- : ASPMResults.applicationNameIDMap.length === 0
- ? `
- This project is not associated with any application in the ASPM
-
`
- : ASPMResults.applicationNameIDMap.length > 0 &&
- ASPMResults.results.length === 0
- ? `
- ASPM does not hold result data for this project
-
`
- : `
-
-
Project:
- ${projectName ?? ""}
-
Scan ID: ${
- scan ?? ""
- }
-
-
-
-
-
-
-
-
-
+ private async getAspmWebviewContent(
+ projectName: string,
+ scan: string,
+ isLatestScan: boolean,
+ aspmResults: { results: CxResult[]; applicationNameIDMap: { name: string; id: string }[] }
+ ): Promise
{
+ const styleResetUri = this.setWebUri("media", "reset.css");
+ const styleVSCodeUri = this.setWebUri("media", "vscode.css");
+ const styleMainUri = this.setWebUri("media", "main.css");
+ const scriptUri = this.setWebUri("media", "riskManagement.js");
+ const styleUri = this.setWebUri("media", "riskManagement.css");
+ const codiconsUri = this.setWebUri("node_modules", "@vscode/codicons", "dist", "codicon.css");
+ const popperUri = this.setWebUri("node_modules", "@popperjs/core", "dist", "umd", "popper.min.js");
+ const styleBootStrap = this.setWebUri("media", "bootstrap", "bootstrap.min.css");
+ const scriptBootStrap = this.setWebUri("media", "bootstrap", "bootstrap.min.js");
+ const nonce = getNonce();
+ return `
+
+
+
+
+
+
+
+
+
+
+ Risks Management
+
+
+
+
ASPM Results Loading...
+
+
+ ${!projectName || !scan || !isLatestScan
+ ? `
ASPM data is only shown when the most recent scan of a project is selected in the Checkmarx One Results tab
`
+ : aspmResults.applicationNameIDMap.length === 0
+ ? `
This project is not associated with any application in the ASPM
`
+ : aspmResults.applicationNameIDMap.length > 0 && aspmResults.results.length === 0
+ ? `
ASPM does not hold result data for this project
`
+ : `
+
Project: ${projectName ?? ""}
+
Scan ID: ${scan ?? ""}
+
+
+
+
`}
+
+
+
+
+
+
`;
}
}
diff --git a/src/views/scaView/scaResultsProvider.ts b/src/views/scaView/scaResultsProvider.ts
index 00b4187d..8e07ee62 100644
--- a/src/views/scaView/scaResultsProvider.ts
+++ b/src/views/scaView/scaResultsProvider.ts
@@ -10,6 +10,7 @@ import { messages } from "../../utils/common/messages";
import { orderResults } from "../../utils/utils";
import { ResultsProvider } from "../resultsProviders";
import CxScaRealTimeErrors from "@checkmarxdev/ast-cli-javascript-wrapper/dist/main/scaRealtime/CxScaRealTimeErrors";
+import { cx } from "../../cx";
export class SCAResultsProvider extends ResultsProvider {
public process;
@@ -40,11 +41,17 @@ export class SCAResultsProvider extends ResultsProvider {
}
async refreshData(message?: string): Promise {
- this.showStatusBarItem(constants.scaScanRunningLog);
- this.message = message ? message : messages.scaStartScan;
- this.data = this.generateTree().children;
- this._onDidChangeTreeData.fire(undefined);
- this.hideStatusBarItem();
+ if (!await cx.isStandaloneEnabled(this.logs)) {
+ this.showStatusBarItem(constants.scaScanRunningLog);
+ this.message = message ? message : messages.scaStartScan;
+ this.data = this.generateTree().children;
+ this._onDidChangeTreeData.fire(undefined);
+ this.hideStatusBarItem();
+ }
+ else{
+ this.data = [];
+ this._onDidChangeTreeData.fire(undefined);
+ }
}
diff --git a/src/views/shared/PromotionalCardView.ts b/src/views/shared/PromotionalCardView.ts
new file mode 100644
index 00000000..edacdfe7
--- /dev/null
+++ b/src/views/shared/PromotionalCardView.ts
@@ -0,0 +1,166 @@
+import * as vscode from "vscode";
+
+export interface PromotionalCardConfig {
+ title: string;
+ description: string;
+ buttonText: string;
+ buttonUrl: string;
+ backgroundColor?: string;
+ textColor?: string;
+ buttonColor?: string;
+ showCard?: boolean;
+}
+
+export class PromotionalCardView {
+
+ public static generateStyles(): string {
+ return `
+ .promotional-card {
+ background-color: var(--vscode-background);
+ border-radius: 12px;
+ padding-top: 5px;
+ text-align: center;
+ }
+
+ .promotional-content {
+ margin: 0 auto;
+ }
+
+ .promotional-title {
+ font-size: 16px;
+ font-weight: 700;
+ margin-bottom: 12px;
+ color: var(--vscode-foreground);
+ line-height: 1.3;
+ }
+
+ .promotional-description {
+ font-size: 14px;
+ color: rgb(140,140,140);
+ margin-bottom: 20px;
+ line-height: 1.4;
+ }
+
+ .promotional-button {
+ background-color: rgb(0,129,225);
+ color: #ffffff;
+ border: none;
+ padding: 12px 32px;
+ font-size: 14px;
+ font-weight: 600;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ text-transform: none;
+ letter-spacing: 0.5px;
+ width: 100%;
+ }
+
+ .promotional-button:hover {
+ background-color: rgb(0,129,225);
+ }
+
+ .promotional-card.hidden {
+ display: none;
+ }
+ `;
+ }
+
+ public static generateHtml(config: PromotionalCardConfig): string {
+ if (!config.showCard) {
+ return '';
+ }
+
+ const cardStyle = config.backgroundColor ? `style="background-color: ${config.backgroundColor};"` : '';
+ const titleStyle = config.textColor ? `style="color: ${config.textColor};"` : '';
+ const descStyle = config.textColor ? `style="color: ${config.textColor};"` : '';
+ const buttonStyle = config.buttonColor ? `style="background-color: ${config.buttonColor};"` : '';
+
+ return `
+ `;
+ }
+
+ public static generateScript(): string {
+ return `
+ function handlePromotionalAction(url) {
+ if (typeof vscode !== 'undefined') {
+ vscode.postMessage({
+ command: 'openPromotionalLink',
+ url: url
+ });
+ }
+ }
+ `;
+ }
+
+ public static handleMessage(message: { command: string; url?: string }): void {
+ if (message.command === 'openPromotionalLink' && message.url) {
+ vscode.env.openExternal(vscode.Uri.parse(message.url));
+ }
+ }
+
+ public static getAspmConfig(): PromotionalCardConfig {
+ return {
+ title: "Unlock Full Security Coverage",
+ description: "This feature is part of the full Checkmarx platform. Upgrade to give your organization complete application security.",
+ buttonText: "Learn more",
+ buttonUrl: "https://docs.checkmarx.com/en/34965-68743-using-the-checkmarx-vs-code-extension---checkmarx-one-results.html#UUID-f6ae9b23-44c8-fcf3-bef2-7b136b9001a1_section-idm234938984608896",
+ showCard: true
+ };
+ }
+
+ public static getSastConfig(): PromotionalCardConfig {
+ return {
+ title: "Enhanced SAST Features Available",
+ description: "Get advanced static analysis with AI-powered remediation, custom rules, and enterprise-grade reporting.",
+ buttonText: "Explore SAST+",
+ buttonUrl: "https://checkmarx.com/product/static-application-security-testing/",
+ showCard: true
+ };
+ }
+
+ public static getScaConfig(): PromotionalCardConfig {
+ return {
+ title: "Enhanced SCA Features Available",
+ description: "Get advanced open source security with license compliance, policy enforcement, and remediation guidance.",
+ buttonText: "Explore SCA+",
+ buttonUrl: "https://checkmarx.com/product/software-composition-analysis/",
+ showCard: true
+ };
+ }
+
+ public static embedInWebview(
+ config: PromotionalCardConfig,
+ existingContent: string,
+ position: 'top' | 'bottom' = 'top'
+ ): string {
+ const promotionalHtml = this.generateHtml(config);
+ const script = this.generateScript();
+
+ if (position === 'top') {
+ return `
+ ${promotionalHtml}
+ ${existingContent}
+
+ `;
+ } else {
+ return `
+ ${existingContent}
+ ${promotionalHtml}
+
+ `;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/webview/authenticationWebview.ts b/src/webview/authenticationWebview.ts
index 7f5fd999..c6b79654 100644
--- a/src/webview/authenticationWebview.ts
+++ b/src/webview/authenticationWebview.ts
@@ -7,6 +7,8 @@ import { WelcomeWebview } from "../welcomePage/welcomeWebview";
import { WebViewCommand } from "../commands/webViewCommand";
import { cx } from "../cx";
import { initializeMcpConfiguration, uninstallMcp } from "../services/mcpSettingsInjector";
+import { CommonCommand } from "../commands/commonCommand";
+import { commands } from "../utils/common/commands";
export class AuthenticationWebview {
public static readonly viewType = "checkmarxAuth";
@@ -65,7 +67,7 @@ export class AuthenticationWebview {
return;
}
const panel = vscode.window.createWebviewPanel(
- AuthenticationWebview.viewType,
+ commands.astResultsPromo,
"Checkmarx One Authentication",
vscode.ViewColumn.One,
{
@@ -113,6 +115,34 @@ export class AuthenticationWebview {
await this.context.globalState.update("cxFirstWelcome", true);
}
+ private schedulePostAuth(isAiEnabled: boolean, options?: { apiKey?: string }) {
+ setTimeout(async () => {
+ try {
+ this._panel.dispose();
+ await this.markFirstWelcomeAsShown();
+ WelcomeWebview.show(this.context, isAiEnabled);
+ await vscode.commands.executeCommand(commands.updateCxOneAssist);
+ await vscode.commands.executeCommand(commands.refreshIgnoredStatusBar);
+ await vscode.commands.executeCommand(commands.refreshScaStatusBar);
+ await vscode.commands.executeCommand(commands.refreshKicsStatusBar);
+ await vscode.commands.executeCommand(commands.refreshRiskManagementView);
+ await vscode.commands.executeCommand(commands.clearKicsDiagnostics);
+ if (options?.apiKey) {
+ if (isAiEnabled) {
+ await initializeMcpConfiguration(options.apiKey);
+ } else {
+ await uninstallMcp();
+ }
+ setTimeout(() => {
+ this._panel.webview.postMessage({ type: "clear-message-api-validation" });
+ }, 500);
+ }
+ } catch (e) {
+ this.logs?.warn?.(`Post-auth refresh failed: ${e?.message ?? e}`);
+ }
+ }, 1000);
+ }
+
private _getWebviewContent(): string {
const styleBootStrap = this.setWebUri(
"media",
@@ -219,7 +249,7 @@ export class AuthenticationWebview {
"Yes",
"Cancel"
)
- .then((selection) => {
+ .then(async (selection) => {
if (selection === "Yes") {
const authService = AuthService.getInstance(this.context);
authService.logout();
@@ -233,6 +263,10 @@ export class AuthenticationWebview {
"Logged out successfully."
);
uninstallMcp();
+ await vscode.commands.executeCommand(commands.refreshIgnoredStatusBar);
+ await vscode.commands.executeCommand(commands.refreshScaStatusBar);
+ await vscode.commands.executeCommand(commands.refreshKicsStatusBar);
+ await vscode.commands.executeCommand(commands.refreshRiskManagementView);
}
});
} else if (message.command === "authenticate") {
@@ -252,12 +286,11 @@ export class AuthenticationWebview {
const authService = AuthService.getInstance(this.context);
const token = await authService.authenticate(baseUri, tenant);
const isAiEnabled = await cx.isAiMcpServerEnabled();
+ const commonCommand = new CommonCommand(this.context, this.logs);
+ await commonCommand.executeCheckStandaloneEnabled();
+ await commonCommand.executeCheckCxOneAssistEnabled();
if (token !== "") {
- setTimeout(async () => {
- this._panel.dispose();
- await this.markFirstWelcomeAsShown();
- WelcomeWebview.show(this.context, isAiEnabled);
- }, 1000);
+ this.schedulePostAuth(isAiEnabled);
}
else {
this._panel.webview.postMessage({ command: "enableAuthButton" });
@@ -281,30 +314,16 @@ export class AuthenticationWebview {
return;
}
- // If the API Key is valid, save it in the VSCode configuration (or wherever you prefer)
authService.saveToken(this.context, message.apiKey);
const isAiEnabled = await cx.isAiMcpServerEnabled();
- // Sending a success message to the window
+ const commonCommand = new CommonCommand(this.context, this.logs);
+ await commonCommand.executeCheckStandaloneEnabled();
+ await commonCommand.executeCheckCxOneAssistEnabled();
this._panel.webview.postMessage({
type: "validation-success",
message: "API Key validated successfully!",
});
- setTimeout(async () => {
-
- this._panel.dispose();
- await this.markFirstWelcomeAsShown();
- WelcomeWebview.show(this.context, isAiEnabled);
- if (isAiEnabled) {
- await initializeMcpConfiguration(message.apiKey);
- } else {
- await uninstallMcp();
- }
- setTimeout(() => {
- this._panel.webview.postMessage({
- type: "clear-message-api-validation",
- });
- }, 500);
- }, 1000);
+ this.schedulePostAuth(isAiEnabled, { apiKey: message.apiKey });
}
} catch (error) {
this._panel.webview.postMessage({ command: "enableAuthButton" });