Skip to content

Commit 1987311

Browse files
authored
Merge pull request #8 from GitGuardian/salomevoltz/scrt-4847-declare-fp-update-gitguardianyaml
feat(ignore_fp): Add option to ignore one secret
2 parents e8c4f4e + 689dd2a commit 1987311

File tree

5 files changed

+138
-1
lines changed

5 files changed

+138
-1
lines changed

src/extension.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
ggshieldAuthStatus,
33
ggshieldScanFile,
44
ignoreLastFound,
5+
ignoreSecret,
56
loginGGShield,
67
showAPIQuota,
78
} from "./lib/ggshield-api";
@@ -23,9 +24,13 @@ import {
2324
StatusBarAlignment,
2425
} from "vscode";
2526
import { GGShieldResolver } from "./lib/ggshield-resolver";
26-
import { isGitInstalled } from "./utils";
27+
import { getCurrentFile, isGitInstalled } from "./utils";
2728
import { GitGuardianWebviewProvider } from "./ggshield-webview/gitguardian-webview-view";
2829
import { StatusBarStatus, updateStatusBarItem } from "./status-bar-utils";
30+
import {
31+
generateSecretName,
32+
GitGuardianSecretHoverProvider,
33+
} from "./gitguardian-hover-provider";
2934

3035
/**
3136
* Extension diagnostic collection
@@ -129,6 +134,10 @@ export function activate(context: ExtensionContext) {
129134
registerOpenViewsCommands(context, outputChannel);
130135
context.subscriptions.push(statusBar);
131136

137+
context.subscriptions.push(
138+
languages.registerHoverProvider("*", new GitGuardianSecretHoverProvider())
139+
);
140+
132141
ggshieldResolver
133142
.checkGGShieldConfiguration()
134143
.then(() => {
@@ -176,6 +185,24 @@ export function activate(context: ExtensionContext) {
176185
cleanUpFileDiagnostics(window.activeTextEditor?.document.uri);
177186
}
178187
}),
188+
commands.registerCommand(
189+
"gitguardian.ignoreSecret",
190+
(diagnosticData) => {
191+
let currentFile = getCurrentFile();
192+
let secretName = generateSecretName(currentFile, diagnosticData);
193+
194+
ignoreSecret(
195+
ggshieldResolver.configuration,
196+
diagnosticData.secretSha,
197+
secretName
198+
);
199+
scanFile(
200+
currentFile,
201+
Uri.file(currentFile),
202+
ggshieldResolver.configuration
203+
);
204+
}
205+
),
179206
commands.registerCommand("gitguardian.authenticate", async () => {
180207
const isAuthenticated = await loginGGShield(
181208
ggshieldResolver.configuration,

src/gitguardian-hover-provider.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import * as vscode from "vscode";
2+
3+
export class GitGuardianSecretHoverProvider implements vscode.HoverProvider {
4+
public provideHover(
5+
document: vscode.TextDocument,
6+
position: vscode.Position,
7+
token: vscode.CancellationToken
8+
): vscode.ProviderResult<vscode.Hover> {
9+
const diagnostics = vscode.languages.getDiagnostics(document.uri);
10+
11+
for (const diagnostic of diagnostics) {
12+
if (
13+
diagnostic.range.contains(position) &&
14+
diagnostic.source === "gitguardian"
15+
) {
16+
const hoverMessage = new vscode.MarkdownString();
17+
hoverMessage.isTrusted = true;
18+
19+
const diagnosticData = diagnosticToJSON(diagnostic);
20+
const encodedDiagnosticData = encodeURIComponent(diagnosticData);
21+
22+
hoverMessage.appendMarkdown(
23+
`[GitGuardian: Ignore Secret](command:gitguardian.ignoreSecret?${encodedDiagnosticData} "Click to ignore this incident")`
24+
);
25+
return new vscode.Hover(hoverMessage, diagnostic.range);
26+
}
27+
}
28+
29+
return null;
30+
}
31+
}
32+
33+
function diagnosticToJSON(diagnostic: vscode.Diagnostic) {
34+
// Extract the infos useful to generate the secret name
35+
const { detector, secretSha } = extractInfosFromMessage(diagnostic.message);
36+
37+
const diagnosticObject = {
38+
startLine: diagnostic.range.start.line,
39+
detector: detector,
40+
secretSha: secretSha,
41+
};
42+
43+
// Convert the object to a JSON string
44+
return JSON.stringify(diagnosticObject);
45+
}
46+
47+
function extractInfosFromMessage(message: string): {
48+
detector: string;
49+
secretSha: string;
50+
} {
51+
const regexDetectorPattern = /Secret detected: ([a-zA-Z ]+)/;
52+
const regexShaPattern = /Secret SHA: ([a-zA-Z0-9]+)/;
53+
54+
const matchDetector = message.match(regexDetectorPattern);
55+
const matchSha = message.match(regexShaPattern);
56+
57+
if (matchDetector && matchSha) {
58+
const detector = matchDetector[1].trim();
59+
const secretSha = matchSha[1].trim();
60+
return { detector, secretSha };
61+
} else {
62+
throw new Error("No match found");
63+
}
64+
}
65+
66+
export function generateSecretName(
67+
currentFile: string,
68+
uriDiagnostic: any
69+
): string {
70+
return `${uriDiagnostic.detector} - ${currentFile}:l.${uriDiagnostic.startLine}`;
71+
}

src/lib/ggshield-api.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,32 @@ export function ignoreLastFound(
9494
}
9595
}
9696

97+
/**
98+
* Ignore one secret.
99+
*
100+
* Show error message on failure
101+
*/
102+
export function ignoreSecret(
103+
configuration: GGShieldConfiguration,
104+
secretSha: string,
105+
secretName: string
106+
): boolean {
107+
const proc = runGGShieldCommand(configuration, [
108+
"secret",
109+
"ignore",
110+
secretSha,
111+
"--name",
112+
secretName,
113+
]);
114+
if (proc.stderr || proc.error) {
115+
console.log(proc.stderr);
116+
return false;
117+
} else {
118+
console.log(proc.stdout);
119+
return true;
120+
}
121+
}
122+
97123
/**
98124
* Scan a file using ggshield CLI application
99125
*

src/lib/ggshield-results-parser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ Incident URL: ${incident.incident_url || "N/A"}
6262
Secret SHA: ${incident.ignore_sha}`,
6363
DiagnosticSeverity.Warning
6464
);
65+
66+
diagnostic.source = "gitguardian";
6567
diagnostics.push(diagnostic);
6668
});
6769
});

src/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import * as vscode from "vscode";
12
import { spawnSync } from "child_process";
3+
import path = require("path");
24

35
export async function isGitInstalled(): Promise<boolean> {
46
return new Promise((resolve) => {
@@ -11,3 +13,12 @@ export async function isGitInstalled(): Promise<boolean> {
1113
}
1214
});
1315
}
16+
17+
export function getCurrentFile(): string {
18+
const activeEditor = vscode.window.activeTextEditor;
19+
if (activeEditor) {
20+
return activeEditor.document.fileName;
21+
} else {
22+
return "";
23+
}
24+
}

0 commit comments

Comments
 (0)