Skip to content

Commit 8294768

Browse files
Merge pull request #14 from Corgea/COR-767
COR-767: Enable & Disable Scanning in IDE
2 parents a60ff78 + 685c3b6 commit 8294768

File tree

9 files changed

+174
-29
lines changed

9 files changed

+174
-29
lines changed

src/providers/vulnerabilitiesWebviewProvider.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import ViewsManager, { Views } from "../utils/ViewsManager";
88
import Vulnerability from "../types/vulnerability";
99
import SCAVulnerability from "../types/scaVulnerability";
1010
import scanningService, { ScanState } from "../services/scanningService";
11+
import ConfigService from "../services/configService";
1112

1213
export default class VulnerabilitiesWebviewProvider implements vscode.WebviewViewProvider {
1314
public static readonly viewType = "vulnerabilitiesWebview";
@@ -22,6 +23,7 @@ export default class VulnerabilitiesWebviewProvider implements vscode.WebviewVie
2223
private _autoRefreshEnabled = false;
2324
private _autoRefreshInterval?: NodeJS.Timeout;
2425
private _isInScanningMode = false;
26+
private _ideScanningEnabled = true;
2527

2628
constructor(private readonly _extensionUri: vscode.Uri) {
2729
VulnerabilitiesWebviewProvider._instance = this;
@@ -251,7 +253,7 @@ export default class VulnerabilitiesWebviewProvider implements vscode.WebviewVie
251253
return;
252254
}
253255

254-
// Fetch vulnerabilities
256+
// Fetch company configs and vulnerabilities in parallel
255257
const [response, scaResponse] = await Promise.all([
256258
APIManager.getProjectVulnerabilities(potentialNames).catch((error: any) => ({
257259
status: error.status,
@@ -260,7 +262,15 @@ export default class VulnerabilitiesWebviewProvider implements vscode.WebviewVie
260262
APIManager.getProjectSCAVulnerabilities(potentialNames).catch((error: any) => ({
261263
status: error.status,
262264
data: { status: "no_project_found", issues: [] },
263-
}))
265+
})),
266+
// Fetch and store configs on refresh
267+
ConfigService.fetchAndStoreConfigs().then(() =>
268+
ConfigService.isIdeScanningEnabled()
269+
).then(enabled => {
270+
this._ideScanningEnabled = enabled;
271+
}).catch(() => {
272+
// If config fetch fails, keep current state
273+
})
264274
]);
265275

266276
// Check project status
@@ -336,6 +346,7 @@ export default class VulnerabilitiesWebviewProvider implements vscode.WebviewVie
336346
scanState: this._scanState,
337347
isInScanningMode: this._isInScanningMode,
338348
autoRefreshEnabled: this._autoRefreshEnabled,
349+
ideScanningEnabled: this._ideScanningEnabled,
339350
});
340351
}
341352

@@ -377,7 +388,8 @@ export default class VulnerabilitiesWebviewProvider implements vscode.WebviewVie
377388
hasSCAVulnerabilities: ${data.hasSCAVulnerabilities},
378389
scanState: ${JSON.stringify(data.scanState)},
379390
isInScanningMode: ${data.isInScanningMode},
380-
autoRefreshEnabled: ${data.autoRefreshEnabled}
391+
autoRefreshEnabled: ${data.autoRefreshEnabled},
392+
ideScanningEnabled: ${data.ideScanningEnabled}
381393
};
382394
</script>
383395
</body>

src/services/authService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import APIManager from "../utils/apiManager";
77
import EventsManager from "../utils/eventsManager";
88
import { OnCommand } from "../utils/commandsManager";
99
import { withErrorHandling } from "../utils/ErrorHandlingManager";
10+
import ConfigService from "./configService";
1011

1112
export default class AuthService {
1213
@OnCommand("corgea.logout")
1314
public static logout(): void {
1415
StorageManager.setValue(StorageKeys.isLoggedIn, false);
1516
StorageManager.setValue(StorageKeys.corgeaUrl, undefined);
1617
StorageManager.setSecretValue(StorageSecretKeys.corgeaApiKey, "");
18+
ConfigService.clearStoredConfigs(); // Clear configs on logout
1719
EventsManager.emit("internal.logout");
1820
vscode.window.showInformationMessage(
1921
"You have been logged out successfully.",
@@ -64,6 +66,7 @@ export default class AuthService {
6466
if (isValid) {
6567
StorageManager.setValue(StorageKeys.isLoggedIn, true);
6668
StorageManager.setSecretValue(StorageSecretKeys.corgeaApiKey, apiKey);
69+
await ConfigService.fetchAndStoreConfigs();
6770
EventsManager.emit("internal.login");
6871
vscode.window.showInformationMessage(
6972
"API Key verified successfully. View the Corgea extension to start fixing.",

src/services/configService.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import APIManager from "../utils/apiManager";
2+
import StorageManager, { StorageKeys } from "../utils/storageManager";
3+
import { withErrorHandling } from "../utils/ErrorHandlingManager";
4+
5+
export interface CompanyConfigs {
6+
ide: {
7+
ide_scanning_enabled: boolean;
8+
};
9+
}
10+
11+
export default class ConfigService {
12+
private static defaultConfigs: CompanyConfigs = {
13+
ide: {
14+
ide_scanning_enabled: true,
15+
},
16+
};
17+
18+
@withErrorHandling()
19+
public static async fetchAndStoreConfigs(): Promise<CompanyConfigs> {
20+
try {
21+
const response = await APIManager.getCompanyConfigs();
22+
if (response.status === "ok" && response.configs) {
23+
await StorageManager.setValue(StorageKeys.companyConfigs, response.configs);
24+
return response.configs;
25+
} else {
26+
return await this.getStoredConfigs();
27+
}
28+
} catch (error) {
29+
console.warn("Failed to fetch company configs, using stored or default configs:", error);
30+
return await this.getStoredConfigs();
31+
}
32+
}
33+
34+
35+
public static async getStoredConfigs(): Promise<CompanyConfigs> {
36+
try {
37+
const storedConfigs = await StorageManager.getValue<CompanyConfigs>(StorageKeys.companyConfigs);
38+
return storedConfigs || this.defaultConfigs;
39+
} catch (error) {
40+
console.warn("Failed to get stored configs, using defaults:", error);
41+
return this.defaultConfigs;
42+
}
43+
}
44+
45+
46+
public static async isIdeScanningEnabled(): Promise<boolean> {
47+
const configs = await this.getStoredConfigs();
48+
return configs.ide.ide_scanning_enabled;
49+
}
50+
51+
52+
public static async clearStoredConfigs(): Promise<void> {
53+
try {
54+
await StorageManager.setValue(StorageKeys.companyConfigs, undefined);
55+
} catch (error) {
56+
console.warn("Failed to clear stored configs:", error);
57+
}
58+
}
59+
60+
public static activate() {}
61+
}

src/services/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import AuthService from "./authService";
22
import VulnerabilityService from "./vulnerabilityService";
33
import VulnerabilityHighlightingService from "./vulnerabilityHighlightingService";
44
import scanningService from "./scanningService";
5+
import ConfigService from "./configService";
56

67
export default [
78
AuthService,
89
VulnerabilityService,
910
VulnerabilityHighlightingService,
1011
scanningService,
12+
ConfigService,
1113
];

src/services/scanningService.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import StorageManager, {
1212
import ErrorHandlingManager, { withErrorHandling } from "../utils/ErrorHandlingManager";
1313
import EventsManager from "../utils/eventsManager";
1414
import WorkspaceManager from "../utils/workspaceManager";
15+
import ConfigService from "./configService";
1516
const kill = require('tree-kill');
1617

1718
export interface ScanProgress {
@@ -54,7 +55,17 @@ export default class scanningService {
5455
};
5556

5657
@OnCommand("corgea.scan-uncommitted")
58+
@withErrorHandling()
5759
public static async scanUncommittedFiles() {
60+
// Check if IDE scanning is enabled
61+
const isIdeScanningEnabled = await ConfigService.isIdeScanningEnabled();
62+
if (!isIdeScanningEnabled) {
63+
vscode.window.showInformationMessage(
64+
"IDE scanning is disabled for your organization. Please contact your administrator to enable this feature."
65+
);
66+
return;
67+
}
68+
5869
await scanningService.scanProject(false);
5970
}
6071

@@ -66,6 +77,15 @@ export default class scanningService {
6677
@OnCommand("corgea.scan-full")
6778
@withErrorHandling()
6879
public static async scanProject(isFullScan: boolean = true) {
80+
// Check if IDE scanning is enabled
81+
const isIdeScanningEnabled = await ConfigService.isIdeScanningEnabled();
82+
if (!isIdeScanningEnabled) {
83+
vscode.window.showInformationMessage(
84+
"IDE scanning is disabled for your organization. Please contact your administrator to enable this feature."
85+
);
86+
return;
87+
}
88+
6989
// Reset scan state
7090
scanningService._scanCancelledByUser = false;
7191
scanningService._scanState = {

src/utils/apiManager.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,4 +563,27 @@ export default class APIManager {
563563
throw error;
564564
}
565565
}
566+
567+
public static async getCompanyConfigs(): Promise<{
568+
status: string;
569+
configs: {
570+
ide: {
571+
ide_scanning_enabled: boolean;
572+
};
573+
};
574+
}> {
575+
const corgeaUrl = await APIManager.getBaseUrl();
576+
try {
577+
const client = await this.getBaseClient();
578+
const url = `${corgeaUrl}/api/${this.apiVersion}/configs`;
579+
const response = await client.get(url);
580+
this.checkForWarnings(response.headers, response.status);
581+
return response.data;
582+
} catch (error) {
583+
console.error(error);
584+
throw error;
585+
} finally {
586+
APIManager.hideLoadingStatus();
587+
}
588+
}
566589
}

src/utils/storageManager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export enum StorageKeys {
55
corgeaUrl = "corgeaUrl",
66
highlightingEnabled = "highlightingEnabled",
77
debugModeEnabled = "debugModeEnabled",
8+
companyConfigs = "companyConfigs",
89
}
910

1011
export enum StorageSecretKeys {

src/views/components/TabNavigation/TabNavigation.tsx

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ const TabNavigation: React.FC = () => {
1010
const [showRightIndicator, setShowRightIndicator] = useState(false);
1111

1212
const handleTabClick = (tab: 'code' | 'sca' | 'scanning') => {
13+
// Don't allow switching to scanning tab if it's disabled
14+
if (tab === 'scanning' && !state.ideScanningEnabled) {
15+
return;
16+
}
17+
1318
dispatch({ type: 'SET_ACTIVE_TAB', payload: tab });
1419

1520
// Scroll to active tab if it's not fully visible
@@ -88,28 +93,30 @@ const TabNavigation: React.FC = () => {
8893

8994
event.preventDefault();
9095

91-
const tabs = ['code', 'sca', 'scanning'] as const;
92-
const currentIndex = tabs.indexOf(state.activeTab);
96+
// Filter available tabs based on IDE scanning config
97+
const allTabs = ['code', 'sca', 'scanning'] as const;
98+
const availableTabs = state.ideScanningEnabled ? allTabs : (['code', 'sca'] as const);
99+
const currentIndex = availableTabs.indexOf(state.activeTab as any);
93100

94101
let newIndex = currentIndex;
95102

96103
switch (event.key) {
97104
case 'ArrowLeft':
98-
newIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
105+
newIndex = currentIndex > 0 ? currentIndex - 1 : availableTabs.length - 1;
99106
break;
100107
case 'ArrowRight':
101-
newIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
108+
newIndex = currentIndex < availableTabs.length - 1 ? currentIndex + 1 : 0;
102109
break;
103110
case 'Home':
104111
newIndex = 0;
105112
break;
106113
case 'End':
107-
newIndex = tabs.length - 1;
114+
newIndex = availableTabs.length - 1;
108115
break;
109116
}
110117

111118
if (newIndex !== currentIndex) {
112-
handleTabClick(tabs[newIndex]);
119+
handleTabClick(availableTabs[newIndex]);
113120
}
114121
};
115122

@@ -145,6 +152,14 @@ const TabNavigation: React.FC = () => {
145152
setTimeout(() => updateScrollIndicators(), 100);
146153
}, [state.vulnerabilities.length, state.scaVulnerabilities.length, state.scanState.isScanning]);
147154

155+
// Handle tab switching when IDE scanning is disabled
156+
useEffect(() => {
157+
if (!state.ideScanningEnabled && state.activeTab === 'scanning') {
158+
// Switch to code tab if scanning tab is disabled and currently active
159+
dispatch({ type: 'SET_ACTIVE_TAB', payload: 'code' });
160+
}
161+
}, [state.ideScanningEnabled, state.activeTab, dispatch]);
162+
148163
return (
149164
<div className="tab-navigation-container">
150165
<div className="nav-tabs-wrapper">
@@ -206,23 +221,25 @@ const TabNavigation: React.FC = () => {
206221
)}
207222
</button>
208223
</li>
209-
<li className="nav-item" role="presentation">
210-
<button
211-
className={`nav-link ${state.activeTab === 'scanning' ? 'active' : ''}`}
212-
onClick={() => handleTabClick('scanning')}
213-
type="button"
214-
role="tab"
215-
aria-selected={state.activeTab === 'scanning'}
216-
>
217-
<i className="fas fa-search"></i>
218-
&nbsp;Scanning
219-
{state.scanState.isScanning && (
220-
<span className="count-badge scanning-indicator">
221-
<i className="fas fa-spinner fa-spin"></i>
222-
</span>
223-
)}
224-
</button>
225-
</li>
224+
{state.ideScanningEnabled && (
225+
<li className="nav-item" role="presentation">
226+
<button
227+
className={`nav-link ${state.activeTab === 'scanning' ? 'active' : ''}`}
228+
onClick={() => handleTabClick('scanning')}
229+
type="button"
230+
role="tab"
231+
aria-selected={state.activeTab === 'scanning'}
232+
>
233+
<i className="fas fa-search"></i>
234+
&nbsp;Scanning
235+
{state.scanState.isScanning && (
236+
<span className="count-badge scanning-indicator">
237+
<i className="fas fa-spinner fa-spin"></i>
238+
</span>
239+
)}
240+
</button>
241+
</li>
242+
)}
226243
</ul>
227244
</div>
228245
</div>

src/views/context/VulnerabilitiesContext.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export interface VulnerabilitiesState {
7474
isInScanningMode: boolean;
7575
autoRefreshEnabled: boolean;
7676
activeTab: 'code' | 'sca' | 'scanning';
77+
ideScanningEnabled: boolean;
7778
}
7879

7980
type VulnerabilitiesAction =
@@ -86,8 +87,9 @@ type VulnerabilitiesAction =
8687
| { type: 'SET_SCANNING_MODE'; payload: boolean }
8788
| { type: 'SET_AUTO_REFRESH'; payload: boolean }
8889
| { type: 'SET_ACTIVE_TAB'; payload: 'code' | 'sca' | 'scanning' }
89-
| { type: 'UPDATE_VULNERABILITY_LISTS'; payload: {
90-
vulnerabilities: Vulnerability[];
90+
| { type: 'SET_IDE_SCANNING_ENABLED'; payload: boolean }
91+
| { type: 'UPDATE_VULNERABILITY_LISTS'; payload: {
92+
vulnerabilities: Vulnerability[];
9193
scaVulnerabilities: SCAVulnerability[];
9294
fileGroups: FileGroup[];
9395
packageGroups: PackageGroup[];
@@ -118,7 +120,8 @@ const initialState: VulnerabilitiesState = {
118120
},
119121
isInScanningMode: false,
120122
autoRefreshEnabled: false,
121-
activeTab: 'code'
123+
activeTab: 'code',
124+
ideScanningEnabled: true
122125
};
123126

124127
function vulnerabilitiesReducer(state: VulnerabilitiesState, action: VulnerabilitiesAction): VulnerabilitiesState {
@@ -151,6 +154,8 @@ function vulnerabilitiesReducer(state: VulnerabilitiesState, action: Vulnerabili
151154
return { ...state, autoRefreshEnabled: action.payload };
152155
case 'SET_ACTIVE_TAB':
153156
return { ...state, activeTab: action.payload };
157+
case 'SET_IDE_SCANNING_ENABLED':
158+
return { ...state, ideScanningEnabled: action.payload };
154159
case 'UPDATE_VULNERABILITY_LISTS':
155160
return {
156161
...state,
@@ -263,6 +268,7 @@ export function VulnerabilitiesProvider({ children }: { children: ReactNode }) {
263268
dispatch({ type: 'SET_SCAN_STATE', payload: data.scanState || initialState.scanState });
264269
dispatch({ type: 'SET_SCANNING_MODE', payload: data.isInScanningMode });
265270
dispatch({ type: 'SET_AUTO_REFRESH', payload: data.autoRefreshEnabled });
271+
dispatch({ type: 'SET_IDE_SCANNING_ENABLED', payload: data.ideScanningEnabled });
266272
}
267273

268274
const handleMessage = (event: MessageEvent) => {

0 commit comments

Comments
 (0)