From 2adf19e7bc0bac18cce1f8868a89922661d8189f Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 17 Sep 2026 09:44:49 -0500 Subject: [PATCH] Keep the token out of the debug output --debug prints the whole options object, the token included, and it is the first thing anyone turns on when a run misbehaves in CI. A build log is not a private place to keep a credential that can write to the repository, so the value is replaced with a marker. That a token was resolved is what the output is useful for; which one it is is not. The filter beside it compared each [key, value] entry against the string "debug" and so never matched, which is why --debug reported itself. It is the same expression and now destructures the key. Co-Authored-By: Claude Opus 5 --- lib/src/Gren.js | 13 +++++++++---- test/Gren.membership.spec.js | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/src/Gren.js b/lib/src/Gren.js index 69286b4c..092d9495 100644 --- a/lib/src/Gren.js +++ b/lib/src/Gren.js @@ -2300,10 +2300,15 @@ class Gren { .toLowerCase() .replace(/\w/, (a) => a.toUpperCase()); const outputs = Object.entries(options) - .filter((option) => option !== "debug") - .map( - ([key, value]) => `${chalk.yellow(camelcaseToSpaces(key))}: ${value.toString() || "empty"}`, - ); + .filter(([key]) => key !== "debug") + .map(([key, value]) => { + // --debug is the first thing anyone turns on in CI, and its output is the whole + // options object. A build log is not a private place to keep a credential that can + // write to the repository, so report that the token is set and not what it is. + const shown = key === "token" ? "" : value.toString() || "empty"; + + return `${chalk.yellow(camelcaseToSpaces(key))}: ${shown}`; + }); process.stdout.write("\n" + chalk.blue("Options: \n") + outputs.join("\n") + "\n"); } diff --git a/test/Gren.membership.spec.js b/test/Gren.membership.spec.js index 83526103..67eb5d87 100644 --- a/test/Gren.membership.spec.js +++ b/test/Gren.membership.spec.js @@ -765,6 +765,27 @@ describe("Gren release membership", () => { assert.throws(() => createGren({ pullRequestRepos: ["just-a-name"] }), /owner\/repo/); }); + it("Should not print the token in the debug output", () => { + // --debug is the first thing anyone turns on in CI, and a build log is not a private + // place to keep a credential that can write to the repository. + const written = []; + const { write } = process.stdout; + + process.stdout.write = (chunk) => written.push(String(chunk)); + + try { + createGren({ debug: true, token: "ghp_averysecrettoken" }); + } finally { + process.stdout.write = write; + } + + const output = written.join(""); + + assert.notInclude(output, "ghp_averysecrettoken", "The token is not in the output"); + assert.include(output, "Token:", "The option is still reported"); + assert.include(output, "Repo: current", "The other options are still reported"); + }); + it("Should reject commitNotes keys that are not commit SHAs", () => { assert.throws(() => createGren({ commitNotes: { abc: {} } }), /at least 7 characters/); });