Skip to content

Commit 54df13f

Browse files
authored
Merge pull request #16 from GitGuardian/garancegourdel/scrt-4590-display-custom-remediation-infos
Add sidebar to display custom remediation message
2 parents d2e34b5 + c5ecf62 commit 54df13f

File tree

5 files changed

+242
-311
lines changed

5 files changed

+242
-311
lines changed

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@
8787
"id": "gitguardianView",
8888
"name": "gitguardian"
8989
},
90+
{
91+
"type": "webview",
92+
"id": "gitguardianRemediationMessageView",
93+
"name": "remediation message"
94+
},
9095
{
9196
"type": "webview",
9297
"id": "gitguardianQuotaView",
@@ -133,5 +138,7 @@
133138
"mocha": "^10.2.0",
134139
"typescript": "^5.1.3"
135140
},
136-
"dependencies": {}
141+
"dependencies": {
142+
"axios": "^1.7.7"
143+
}
137144
}

src/extension.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
GitGuardianSecretHoverProvider,
3535
} from "./giguardian-interface/gitguardian-hover-provider";
3636
import { GitGuardianQuotaWebviewProvider } from "./ggshield-webview/gitguardian-quota-webview";
37+
import { GitGuardianRemediationMessageWebviewProvider } from "./ggshield-webview/gitguardian-remediation-message-view";
3738

3839
/**
3940
* Extension diagnostic collection
@@ -135,16 +136,24 @@ export function activate(context: ExtensionContext) {
135136
context.extensionUri
136137
);
137138

139+
const ggshieldRemediationMessageViewProvider = new GitGuardianRemediationMessageWebviewProvider(
140+
configuration,
141+
context.extensionUri
142+
);
138143
const ggshieldQuotaViewProvider = new GitGuardianQuotaWebviewProvider(
139144
configuration,
140145
context.extensionUri
141146
);
142147
window.registerWebviewViewProvider("gitguardianView", ggshieldViewProvider);
148+
window.registerWebviewViewProvider(
149+
"gitguardianRemediationMessageView",
150+
ggshieldRemediationMessageViewProvider
151+
);
143152
window.registerWebviewViewProvider(
144153
"gitguardianQuotaView",
145154
ggshieldQuotaViewProvider
146155
);
147-
context.subscriptions.push(ggshieldViewProvider, ggshieldQuotaViewProvider);
156+
context.subscriptions.push(ggshieldViewProvider, ggshieldRemediationMessageViewProvider, ggshieldQuotaViewProvider);
148157

149158
statusBar = window.createStatusBarItem(StatusBarAlignment.Left, 0);
150159
updateStatusBarItem(StatusBarStatus.initialization, statusBar);
@@ -241,6 +250,7 @@ export function activate(context: ExtensionContext) {
241250
const ggshieldApi = ggshieldApiKey(configuration);
242251
setApiKey(configuration, ggshieldApi);
243252
ggshieldViewProvider.refresh();
253+
ggshieldRemediationMessageViewProvider.refresh();
244254
ggshieldQuotaViewProvider.refresh();
245255
} else {
246256
updateStatusBarItem(StatusBarStatus.unauthenticated, statusBar);
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { getRemediationMessage, ggshieldAuthStatus } from "../lib/ggshield-api";
2+
import { GGShieldConfiguration } from "../lib/ggshield-configuration";
3+
import * as vscode from "vscode";
4+
5+
export class GitGuardianRemediationMessageWebviewProvider
6+
implements vscode.WebviewViewProvider
7+
{
8+
public static readonly viewType = "gitguardian.gitguardianRemediationMessageView";
9+
private _view?: vscode.WebviewView;
10+
private isAuthenticated: boolean = false;
11+
private remediationMessage: string = "";
12+
private isLoading: boolean = false;
13+
14+
constructor(
15+
private ggshieldConfiguration: GGShieldConfiguration,
16+
private readonly _extensionUri: vscode.Uri
17+
) {
18+
this.checkAuthenticationStatus();
19+
this.updateRemediationMessage();
20+
}
21+
22+
public async resolveWebviewView(
23+
webviewView: vscode.WebviewView,
24+
context: vscode.WebviewViewResolveContext,
25+
_token: vscode.CancellationToken
26+
) {
27+
this._view = webviewView;
28+
this.refresh();
29+
30+
webviewView.onDidChangeVisibility(() => {
31+
if (webviewView.visible) {
32+
// Refresh when the view becomes visible (e.g., after being collapsed and reopened)
33+
this.refresh();
34+
}
35+
});
36+
}
37+
38+
private async checkAuthenticationStatus() {
39+
this.isAuthenticated = ggshieldAuthStatus(this.ggshieldConfiguration);
40+
}
41+
42+
private async updateRemediationMessage() {
43+
if (this.isAuthenticated) {
44+
this.remediationMessage = await getRemediationMessage(this.ggshieldConfiguration);
45+
}
46+
}
47+
48+
private updateWebViewContent(webviewView?: vscode.WebviewView) {
49+
if (webviewView) {
50+
webviewView.webview.html = this.getHtmlForWebview();
51+
}
52+
}
53+
54+
private escapeHtml(unsafe: string):string {
55+
return unsafe
56+
.replace(/&/g, "&")
57+
.replace(/</g, "&lt;")
58+
.replace(/>/g, "&gt;")
59+
.replace(/"/g, "&quot;")
60+
.replace(/'/g, "&#039;");
61+
}
62+
63+
private getHtmlForWebview(): string {
64+
if (this.isLoading) {
65+
return `
66+
<!DOCTYPE html>
67+
<html lang="en">
68+
<body>
69+
<p>Loading...</p>
70+
</body>
71+
</html>`;
72+
}
73+
74+
if (this.isAuthenticated) {
75+
return `
76+
<!DOCTYPE html>
77+
<html lang="en">
78+
<body>
79+
<pre style="white-space:pre-wrap">
80+
${this.escapeHtml(this.remediationMessage)}
81+
</pre>
82+
</body>
83+
</html>`;
84+
} else {
85+
return `
86+
<!DOCTYPE html>
87+
<html lang="en">
88+
<body>
89+
<p>Please authenticate to see your personalized remediation message.</p>
90+
</body>
91+
</html>`;
92+
}
93+
}
94+
95+
public async refresh() {
96+
this.isLoading = true;
97+
this.updateWebViewContent(this._view);
98+
99+
await this.checkAuthenticationStatus();
100+
console.log("Well authentificated");
101+
await this.updateRemediationMessage();
102+
103+
this.isLoading = false;
104+
this.updateWebViewContent(this._view);
105+
}
106+
107+
dispose(): void {
108+
if (this._view) {
109+
this._view.webview.onDidReceiveMessage(() => {});
110+
this._view.webview.html = "";
111+
this._view = undefined;
112+
}
113+
}
114+
}

src/lib/ggshield-api.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
spawnSync,
88
} from "child_process";
99
import { workspace, window } from "vscode";
10+
import axios from 'axios';
1011
import { GGShieldConfiguration } from "./ggshield-configuration";
1112
import { GGShieldScanResults } from "./api-types";
1213
import * as os from "os";
@@ -89,6 +90,23 @@ export async function getAPIquota(
8990
}
9091
}
9192

93+
94+
export async function getRemediationMessage(
95+
configuration: GGShieldConfiguration
96+
): Promise<string> {
97+
const path = require('node:path');
98+
try {
99+
const response = await axios.get(path.join(configuration.apiUrl,'v1/metadata'), {
100+
headers: {
101+
'authorization': `Token ${configuration.apiKey}`
102+
}
103+
});
104+
return response.data.remediation_messages.pre_commit;
105+
} catch (error) {
106+
return "An error occured.";
107+
}
108+
}
109+
92110
/**
93111
* Ignore last found secrets
94112
*

0 commit comments

Comments
 (0)