From 057dbd877a9c28259b51e8f60b6887f030897881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Salom=C3=A9=20Voltz?= Date: Mon, 23 Sep 2024 15:35:58 +0200 Subject: [PATCH 1/4] fix(ggshield-configuration): Override configuration with settings --- src/extension.ts | 4 +-- src/lib/ggshield-configuration.ts | 20 +++-------- src/lib/ggshield-resolver.ts | 56 +++++++++---------------------- 3 files changed, 23 insertions(+), 57 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index d0e7f16..d1e6915 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,7 +7,7 @@ import { showAPIQuota, } from "./lib/ggshield-api"; import { - createDefaultConfiguration, + getConfiguration, GGShieldConfiguration, } from "./lib/ggshield-configuration"; import { parseGGShieldResults } from "./lib/ggshield-results-parser"; @@ -121,7 +121,7 @@ function registerQuotaViewCommands(view: GitGuardianQuotaWebviewProvider) { export function activate(context: ExtensionContext) { // Check if ggshield if available const outputChannel = window.createOutputChannel("GitGuardian"); - let configuration = createDefaultConfiguration(context); + let configuration = getConfiguration(context); let authStatus: boolean = false; const ggshieldResolver = new GGShieldResolver( outputChannel, diff --git a/src/lib/ggshield-configuration.ts b/src/lib/ggshield-configuration.ts index d03a02f..6fe3883 100644 --- a/src/lib/ggshield-configuration.ts +++ b/src/lib/ggshield-configuration.ts @@ -20,26 +20,16 @@ export class GGShieldConfiguration { * TODO: Check with Mathieu if this behaviour is expected * @returns {GGShieldConfiguration} from the extension settings */ -export function getSettingsConfiguration(): GGShieldConfiguration | undefined { +export function getConfiguration( + context: ExtensionContext +): GGShieldConfiguration { const config = workspace.getConfiguration("gitguardian"); const ggshieldPath: string | undefined = config.get("GGShieldPath"); const apiUrl: string | undefined = config.get("apiUrl"); - if (!ggshieldPath) { - return undefined; - } return new GGShieldConfiguration( - ggshieldPath, + ggshieldPath ? ggshieldPath : getBinaryAbsolutePath(os.platform(), os.arch(), context), apiUrl ? apiUrl : apiUrlDefault ); -} - -export function createDefaultConfiguration( - context: ExtensionContext -): GGShieldConfiguration { - return new GGShieldConfiguration( - getBinaryAbsolutePath(os.platform(), os.arch(), context), - "https://api.gitguardian.com/" - ); -} +} \ No newline at end of file diff --git a/src/lib/ggshield-resolver.ts b/src/lib/ggshield-resolver.ts index 197b450..206b7cf 100644 --- a/src/lib/ggshield-resolver.ts +++ b/src/lib/ggshield-resolver.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode"; import { - getSettingsConfiguration, + getConfiguration, GGShieldConfiguration, } from "./ggshield-configuration"; import { runGGShieldCommand } from "./ggshield-api"; @@ -32,44 +32,20 @@ export class GGShieldResolver { * @returns {Promise} A promise that resolves once the `ggshield` path is determined. */ async checkGGShieldConfiguration(): Promise { - let settingsConfiguration = getSettingsConfiguration(); - if (settingsConfiguration) { - try { - await this.useSettingsConfiguration(settingsConfiguration); - this.channel.appendLine( - `Using ggshield at: ${this.configuration.ggshieldPath}, to change this go to settings.` - ); - return; - } catch (error) { - this.channel.appendLine( - `Failed to use ggshield version from settings. - You can remove it from settings, and use the bundled version instead.` - ); - window.showErrorMessage( - `Failed to use ggshield version from settings.` - ); - throw error; - } - } else { - try { - await this.checkBundledGGShield(); - this.channel.appendLine( - `Using bundled ggshield at: ${this.configuration.ggshieldPath}, to change this go to settings.` - ); - return; - } catch (error) { - this.channel.appendLine( - `ggshield binary not found: this architecture is not supported ${ - (os.arch(), os.platform()) - }` - ); - window.showErrorMessage( - `ggshield binary not found: this architecture is not supported ${ - (os.arch(), os.platform()) - }` - ); - throw error; - } + try { + await this.testConfiguration(this.configuration); + this.channel.appendLine( + `Using ggshield at: ${this.configuration.ggshieldPath}, to change this go to settings.` + ); + return; + } catch (error) { + this.channel.appendLine( + `Failed to use ggshield version ${this.configuration.ggshieldPath}.` + ); + window.showErrorMessage( + `Failed to use ggshield.` + ); + throw error; } } @@ -78,7 +54,7 @@ export class GGShieldResolver { * * @returns {Promise} A promise that resolves if the configuration is valid. */ - async useSettingsConfiguration( + async testConfiguration( configuration: GGShieldConfiguration ): Promise { let proc = runGGShieldCommand(configuration, ["--version"]); From 94710217158825e15868bbd42759767dec15caa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Salom=C3=A9=20Voltz?= Date: Mon, 23 Sep 2024 15:43:26 +0200 Subject: [PATCH 2/4] feat(configuration): Add placehoder for api key --- package.json | 5 +++++ src/lib/ggshield-api.ts | 6 ++++-- src/lib/ggshield-configuration.ts | 8 ++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 29070f6..32528ab 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,11 @@ "type": "string", "default": "https://api.gitguardian.com", "markdownDescription": "You can override the value here for On Premise installations" + }, + "gitguardian.apiKey": { + "type": "string", + "default": "", + "markdownDescription": "Your API Key" } } }, diff --git a/src/lib/ggshield-api.ts b/src/lib/ggshield-api.ts index 8059039..c8987a6 100644 --- a/src/lib/ggshield-api.ts +++ b/src/lib/ggshield-api.ts @@ -21,7 +21,7 @@ export function runGGShieldCommand( configuration: GGShieldConfiguration, args: string[] ): SpawnSyncReturns { - const { ggshieldPath, apiUrl } = configuration; + const { ggshieldPath, apiUrl, apiKey } = configuration; let options: SpawnSyncOptionsWithStringEncoding = { cwd: os.tmpdir(), @@ -30,6 +30,8 @@ export function runGGShieldCommand( GITGUARDIAN_API_URL: apiUrl, // eslint-disable-next-line @typescript-eslint/naming-convention GG_USER_AGENT: "gitguardian-vscode", + //eslint-disable-next-line @typescript-eslint/naming-convention + GITGUARDIAN_API_KEY: apiKey, }, encoding: "utf-8", windowsHide: true, @@ -178,7 +180,7 @@ export async function loginGGShield( configuration: GGShieldConfiguration, outputChannel: any ): Promise { - const { ggshieldPath, apiUrl } = configuration; + const { ggshieldPath, apiUrl, apiKey } = configuration; let options: SpawnOptionsWithoutStdio = { cwd: os.tmpdir(), diff --git a/src/lib/ggshield-configuration.ts b/src/lib/ggshield-configuration.ts index 6fe3883..6506dba 100644 --- a/src/lib/ggshield-configuration.ts +++ b/src/lib/ggshield-configuration.ts @@ -7,10 +7,12 @@ const apiUrlDefault = "https://api.gitguardian.com/"; export class GGShieldConfiguration { ggshieldPath: string; apiUrl: string; + apiKey: string; - constructor(ggshieldPath: string = "", apiUrl: string = "") { + constructor(ggshieldPath: string = "", apiUrl: string = "", apiKey: string = "") { this.ggshieldPath = ggshieldPath; this.apiUrl = apiUrl; + this.apiKey = apiKey; } } @@ -27,9 +29,11 @@ export function getConfiguration( const ggshieldPath: string | undefined = config.get("GGShieldPath"); const apiUrl: string | undefined = config.get("apiUrl"); + const apiKey: string | undefined = config.get("apiKey"); return new GGShieldConfiguration( ggshieldPath ? ggshieldPath : getBinaryAbsolutePath(os.platform(), os.arch(), context), - apiUrl ? apiUrl : apiUrlDefault + apiUrl ? apiUrl : apiUrlDefault, + apiKey ? apiKey : "" ); } \ No newline at end of file From 8568061bf92d6762816cb529dfef6ea4176b036f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Salom=C3=A9=20Voltz?= Date: Mon, 23 Sep 2024 16:09:49 +0200 Subject: [PATCH 3/4] feat(ggshield-configuration): Use config list to retrieve token --- src/extension.ts | 3 +++ src/lib/ggshield-api.ts | 19 +++++++++++++++++++ src/lib/ggshield-configuration.ts | 9 +++++++++ 3 files changed, 31 insertions(+) diff --git a/src/extension.ts b/src/extension.ts index d1e6915..742e6ae 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,4 +1,5 @@ import { + ggshieldApiKey, ggshieldAuthStatus, ggshieldScanFile, ignoreLastFound, @@ -9,6 +10,7 @@ import { import { getConfiguration, GGShieldConfiguration, + setApiKey, } from "./lib/ggshield-configuration"; import { parseGGShieldResults } from "./lib/ggshield-results-parser"; import { @@ -233,6 +235,7 @@ export function activate(context: ExtensionContext) { authStatus = true; updateStatusBarItem(StatusBarStatus.ready, statusBar); commands.executeCommand('setContext', 'isAuthenticated', true); + setApiKey(configuration, ggshieldApiKey(configuration)); ggshieldViewProvider.refresh(); ggshieldQuotaViewProvider.refresh(); } else { diff --git a/src/lib/ggshield-api.ts b/src/lib/ggshield-api.ts index c8987a6..a845aaf 100644 --- a/src/lib/ggshield-api.ts +++ b/src/lib/ggshield-api.ts @@ -232,7 +232,26 @@ export function ggshieldAuthStatus( console.log(proc.stderr); return false; } else { + if (proc.stdout.includes("unhealthy")) { + return false; + } console.log(proc.stdout); return true; } } + +export function ggshieldApiKey( + configuration: GGShieldConfiguration, +): string | undefined { + const proc = runGGShieldCommand(configuration, ["config", "list"]); + if (proc.stderr || proc.error) { + console.log(proc.stderr); + return undefined; + } else { + console.log(proc.stdout); + const regexToken = /token: ([a-zA-Z0-9]+)/; + const matchToken = proc.stdout.match(regexToken); + + return matchToken ? matchToken[1].trim() : undefined; + } +} \ No newline at end of file diff --git a/src/lib/ggshield-configuration.ts b/src/lib/ggshield-configuration.ts index 6506dba..b37f3bc 100644 --- a/src/lib/ggshield-configuration.ts +++ b/src/lib/ggshield-configuration.ts @@ -36,4 +36,13 @@ export function getConfiguration( apiUrl ? apiUrl : apiUrlDefault, apiKey ? apiKey : "" ); +} + +export function setApiKey(configuration: GGShieldConfiguration, apiKey: string | undefined): void { + const config = workspace.getConfiguration("gitguardian"); + if (!apiKey) { + throw new Error("Missing API Key"); + } + configuration.apiKey = apiKey; + config.update("apiKey", apiKey); } \ No newline at end of file From c310eaf17ecd43869f27da0667c2b46d79121cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Salom=C3=A9=20Voltz?= Date: Tue, 24 Sep 2024 11:17:13 +0200 Subject: [PATCH 4/4] feat(ggshield-configuration): fetch API key from config list --- src/extension.ts | 8 ++++-- src/lib/ggshield-api.ts | 42 ++++++++++++++++++++++--------- src/lib/ggshield-configuration.ts | 5 ++-- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 742e6ae..72ab673 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -167,7 +167,10 @@ export function activate(context: ExtensionContext) { updateStatusBarItem(StatusBarStatus.unauthenticated, statusBar); } else { commands.executeCommand('setContext', 'isAuthenticated', true); - } + updateStatusBarItem(StatusBarStatus.ready, statusBar); + const ggshieldApi = ggshieldApiKey(configuration); + setApiKey(configuration, ggshieldApi); + } }) .then(async () => { // Check if git is installed @@ -235,7 +238,8 @@ export function activate(context: ExtensionContext) { authStatus = true; updateStatusBarItem(StatusBarStatus.ready, statusBar); commands.executeCommand('setContext', 'isAuthenticated', true); - setApiKey(configuration, ggshieldApiKey(configuration)); + const ggshieldApi = ggshieldApiKey(configuration); + setApiKey(configuration, ggshieldApi); ggshieldViewProvider.refresh(); ggshieldQuotaViewProvider.refresh(); } else { diff --git a/src/lib/ggshield-api.ts b/src/lib/ggshield-api.ts index a845aaf..87baabb 100644 --- a/src/lib/ggshield-api.ts +++ b/src/lib/ggshield-api.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { SpawnOptionsWithoutStdio, SpawnSyncOptionsWithStringEncoding, @@ -22,17 +23,25 @@ export function runGGShieldCommand( args: string[] ): SpawnSyncReturns { const { ggshieldPath, apiUrl, apiKey } = configuration; + let env: { + GITGUARDIAN_API_URL: string; + GG_USER_AGENT: string; + GITGUARDIAN_API_KEY?: string; // Note the ? to indicate this property is optional + } = { + GITGUARDIAN_API_URL: apiUrl, + GG_USER_AGENT: "gitguardian-vscode", + }; + + if (apiKey) { + env = { + ...env, + // eslint-disable-next-line @typescript-eslint/naming-convention + GITGUARDIAN_API_KEY: apiKey, + }; + } let options: SpawnSyncOptionsWithStringEncoding = { cwd: os.tmpdir(), - env: { - // eslint-disable-next-line @typescript-eslint/naming-convention - GITGUARDIAN_API_URL: apiUrl, - // eslint-disable-next-line @typescript-eslint/naming-convention - GG_USER_AGENT: "gitguardian-vscode", - //eslint-disable-next-line @typescript-eslint/naming-convention - GITGUARDIAN_API_KEY: apiKey, - }, encoding: "utf-8", windowsHide: true, }; @@ -249,9 +258,18 @@ export function ggshieldApiKey( return undefined; } else { console.log(proc.stdout); - const regexToken = /token: ([a-zA-Z0-9]+)/; - const matchToken = proc.stdout.match(regexToken); - - return matchToken ? matchToken[1].trim() : undefined; + const apiUrl = configuration.apiUrl; + const re = /api/; + + const regexInstanceSection = `\\[${apiUrl.replace(re, "dashboard")}\\]([\\s\\S]*?)(?=\\[|$)`; + const instanceSectionMatch = proc.stdout.match(regexInstanceSection); + + if (instanceSectionMatch) { + const instanceSection = instanceSectionMatch[0]; + const regexToken = /token:\s([a-zA-Z0-9]+)/; + const matchToken = instanceSection.match(regexToken); + + return matchToken ? matchToken[1].trim() : undefined; + } } } \ No newline at end of file diff --git a/src/lib/ggshield-configuration.ts b/src/lib/ggshield-configuration.ts index b37f3bc..bfd8022 100644 --- a/src/lib/ggshield-configuration.ts +++ b/src/lib/ggshield-configuration.ts @@ -1,5 +1,5 @@ import { getBinaryAbsolutePath } from "./ggshield-resolver-utils"; -import { ExtensionContext, workspace } from "vscode"; +import { ConfigurationTarget, ExtensionContext, workspace } from "vscode"; import * as os from "os"; const apiUrlDefault = "https://api.gitguardian.com/"; @@ -43,6 +43,7 @@ export function setApiKey(configuration: GGShieldConfiguration, apiKey: string | if (!apiKey) { throw new Error("Missing API Key"); } + configuration.apiKey = apiKey; - config.update("apiKey", apiKey); + config.update("apiKey", apiKey, ConfigurationTarget.Global); } \ No newline at end of file