From 890cb407bf14f2a153fb2acaa636cbf46ee65db3 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sat, 11 Apr 2026 20:15:42 +0200 Subject: [PATCH 1/3] ci: set explicit contents: read permissions on workflows Addresses CodeQL alerts #1, #3, #9 (actions/missing-workflow-permissions). Both workflows only need read access to the repo contents, so the minimal permissions block is set at the workflow level. --- .github/workflows/license_npm.yml | 3 +++ .github/workflows/npm.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/license_npm.yml b/.github/workflows/license_npm.yml index 91c7fb4..daa97a5 100644 --- a/.github/workflows/license_npm.yml +++ b/.github/workflows/license_npm.yml @@ -13,6 +13,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} +permissions: + contents: read + jobs: license-check: runs-on: ubuntu-latest diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index d126c62..a09c445 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -7,6 +7,9 @@ on: - master pull_request: +permissions: + contents: read + jobs: lint: runs-on: ubuntu-latest From e9d07a49df17cfd41c40a43ca5daaf7da07255ec Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sat, 11 Apr 2026 20:15:49 +0200 Subject: [PATCH 2/3] fix: use exact-match check for git SSH rewrite config value The installers were using .includes('https://github.com/') to check the output of `git config --get url.git@github.com:.insteadOf`. That output is either empty or exactly the value we set ourselves, so exact match is both more correct and silences CodeQL's js/incomplete-url-substring-sanitization rule. Addresses CodeQL alerts #5, #6, #7, #8 across atl, discord, esq, and n8n installers. --- lib/installers/atl.js | 2 +- lib/installers/discord.js | 3 +-- lib/installers/esq.js | 3 +-- lib/installers/n8n.js | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/installers/atl.js b/lib/installers/atl.js index 84b90b7..dfe46fb 100644 --- a/lib/installers/atl.js +++ b/lib/installers/atl.js @@ -85,7 +85,7 @@ const configurePrivateRepoAccess = () => { stdio: 'pipe', }).trim(); - if (!gitConfig.includes('https://github.com/')) { + if (gitConfig !== 'https://github.com/') { console.log(chalk.gray('Configuring git to use SSH for GitHub...')); execSync('git config --global url."git@github.com:".insteadOf "https://github.com/"', { stdio: 'pipe', diff --git a/lib/installers/discord.js b/lib/installers/discord.js index 71f9695..5a97a1a 100644 --- a/lib/installers/discord.js +++ b/lib/installers/discord.js @@ -84,8 +84,7 @@ const configurePrivateRepoAccess = () => { } ); - const needsConfig = - gitResult.status !== 0 || !gitResult.stdout.trim().includes('https://github.com/'); + const needsConfig = gitResult.status !== 0 || gitResult.stdout.trim() !== 'https://github.com/'; if (needsConfig) { console.log(chalk.gray('Configuring git to use SSH for GitHub...')); diff --git a/lib/installers/esq.js b/lib/installers/esq.js index 649995e..6105280 100644 --- a/lib/installers/esq.js +++ b/lib/installers/esq.js @@ -95,8 +95,7 @@ const configurePrivateRepoAccess = () => { } ); - const needsConfig = - gitResult.status !== 0 || !gitResult.stdout.trim().includes('https://github.com/'); + const needsConfig = gitResult.status !== 0 || gitResult.stdout.trim() !== 'https://github.com/'; if (needsConfig) { console.log(chalk.gray('Configuring git to use SSH for GitHub...')); diff --git a/lib/installers/n8n.js b/lib/installers/n8n.js index 5486100..75b5a0f 100644 --- a/lib/installers/n8n.js +++ b/lib/installers/n8n.js @@ -97,7 +97,7 @@ const configurePrivateRepoAccess = () => { stdio: 'pipe', }).trim(); - if (!gitConfig.includes('https://github.com/')) { + if (gitConfig !== 'https://github.com/') { console.log(chalk.gray('Configuring git to use SSH for GitHub...')); execSync('git config --global url."git@github.com:".insteadOf "https://github.com/"', { stdio: 'pipe', From 91ed87bfc447505ee3f0c4f25dd1b93959446e18 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sat, 11 Apr 2026 20:15:55 +0200 Subject: [PATCH 3/3] fix(grafanactl): redact secrets from runConfig error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runConfig threw errors containing the full args joined — for `config set contexts.X.grafana.password ` that embedded the plaintext password in error.message, which was then logged by the outer catch handler. Add a redactSensitiveArgs helper that replaces any value whose preceding key matches .token / .password / .secret / .apikey with before building the error message. Addresses CodeQL alert #4 (js/clear-text-logging, error severity). --- lib/installers/grafanactl.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/installers/grafanactl.js b/lib/installers/grafanactl.js index 237110f..6a924d0 100644 --- a/lib/installers/grafanactl.js +++ b/lib/installers/grafanactl.js @@ -6,13 +6,30 @@ import path from 'path'; import { execSync, spawnSync } from 'child_process'; import { commandExists, getPlatformInfo } from '../utils/platform.js'; +// Keys whose following arg is a secret (e.g. contexts.X.grafana.password). +const SENSITIVE_KEY_PATTERN = /\.(token|password|secret|api[_-]?key)$/i; + +/** + * Redact values in args that follow a sensitive key so secrets don't leak + * into error messages or logs. + */ +const redactSensitiveArgs = (args) => + args.map((arg, i) => { + const prev = args[i - 1]; + if (typeof prev === 'string' && SENSITIVE_KEY_PATTERN.test(prev)) { + return ''; + } + return arg; + }); + /** * Run a grafanactl config command, throwing on failure */ const runConfig = (binary, args) => { const result = spawnSync(binary, args, { stdio: 'pipe', encoding: 'utf8' }); if (result.status !== 0) { - throw new Error(`grafanactl ${args.join(' ')} failed: ${result.stderr || 'unknown error'}`); + const safeArgs = redactSensitiveArgs(args); + throw new Error(`grafanactl ${safeArgs.join(' ')} failed: ${result.stderr || 'unknown error'}`); } return result; };