@biffo/cli 0.223.2 → 0.224.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +33 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -965,10 +965,10 @@ var GitAdapter = class {
965
965
  * `token`, when given, authenticates the clone against a private repo by
966
966
  * embedding it in the URL's userinfo (`https://x-access-token:<token>@...`
967
967
  * — the standard scheme for a GitHub PAT/App token over HTTPS) before
968
- * handing it to `git clone`. The *rewritten* URL is only ever passed to
969
- * `execa`, never logged or included in a thrown error message — errors
970
- * reference the original `repoUrl` so a token can't leak into CLI output
971
- * or a crash report.
968
+ * handing it to `git clone`. The failure path runs the underlying error
969
+ * through `redactSecrets` before reporting it: `execa` echoes the argv it
970
+ * ran, so the tokenized URL *is* present in that message and interpolating
971
+ * it raw leaked the token into CLI output and CI logs (#1169).
972
972
  */
973
973
  async cloneToTemp(repoUrl, namePrefix, token) {
974
974
  const dir = mkdtempSync2(join6(tmpdir2(), `${namePrefix}-${randomUUID().slice(0, 8)}-`));
@@ -977,7 +977,7 @@ var GitAdapter = class {
977
977
  await execa2("git", ["clone", "--depth", "1", cloneUrl, dir]);
978
978
  } catch (err) {
979
979
  rmSync2(dir, { recursive: true, force: true });
980
- throw new Error(`Failed to clone ${repoUrl}: ${err.message}`);
980
+ throw gitFailure(`Failed to clone ${repoUrl}`, err, [token]);
981
981
  }
982
982
  const gitDir = join6(dir, ".git");
983
983
  if (existsSync6(gitDir)) rmSync2(gitDir, { recursive: true, force: true });
@@ -1000,7 +1000,7 @@ var GitAdapter = class {
1000
1000
  await execa2("git", ["clone", cloneUrl, dir]);
1001
1001
  } catch (err) {
1002
1002
  rmSync2(dir, { recursive: true, force: true });
1003
- throw new Error(`Failed to clone ${repoUrl}: ${err.message}`);
1003
+ throw gitFailure(`Failed to clone ${repoUrl}`, err, [token]);
1004
1004
  }
1005
1005
  return dir;
1006
1006
  }
@@ -1182,9 +1182,15 @@ var GitAdapter = class {
1182
1182
  /**
1183
1183
  * Push the current HEAD to `branch` on the remote. When `token` is given and
1184
1184
  * the remote is HTTPS, it's embedded in the push URL for auth (SSH/file
1185
- * remotes push with ambient credentials). The authenticated URL is never
1186
- * logged or included in a thrown error — a push failure references only the
1187
- * remote name, so a token can't leak into CLI output or a crash report.
1185
+ * remotes push with ambient credentials).
1186
+ *
1187
+ * The failure path reports git's own error, redacted (#1135). It previously
1188
+ * discarded it entirely — a defensible-looking choice, since the argv `execa`
1189
+ * echoes contains the tokenized URL, but it made **every** push failure in
1190
+ * the CLI indistinguishable: a rejected pre-push hook, an unresolvable
1191
+ * remote and a bad token all produced the same sentence. That is why #1040
1192
+ * ("`core upgrade --apply` aborts at the push step") survived two sessions of
1193
+ * diagnosis without its cause ever being established.
1188
1194
  */
1189
1195
  async push(cwd, branch, opts = {}) {
1190
1196
  const remote = opts.remote ?? "origin";
@@ -1196,8 +1202,8 @@ var GitAdapter = class {
1196
1202
  }
1197
1203
  try {
1198
1204
  await execa2("git", ["push", target, `HEAD:refs/heads/${branch}`], { cwd });
1199
- } catch {
1200
- throw new Error(`Failed to push branch '${branch}' to remote '${remote}'.`);
1205
+ } catch (err) {
1206
+ throw gitFailure(`Failed to push branch '${branch}' to remote '${remote}'`, err, [opts.token]);
1201
1207
  }
1202
1208
  await this.setUpstreamAfterPush(cwd, branch, remote);
1203
1209
  }
@@ -1263,6 +1269,22 @@ function injectToken(repoUrl, token) {
1263
1269
  parsed.password = token;
1264
1270
  return parsed.toString();
1265
1271
  }
1272
+ var REDACTED = "***";
1273
+ var MIN_REDACTABLE = 8;
1274
+ function redactSecrets(text, secrets) {
1275
+ let out = text;
1276
+ for (const secret of secrets) {
1277
+ if (secret === void 0 || secret.length < MIN_REDACTABLE) continue;
1278
+ for (const form of /* @__PURE__ */ new Set([secret, encodeURIComponent(secret)])) {
1279
+ out = out.split(form).join(REDACTED);
1280
+ }
1281
+ }
1282
+ return out;
1283
+ }
1284
+ function gitFailure(summary, err, secrets) {
1285
+ const detail = redactSecrets(err.message, secrets);
1286
+ return new Error(`${summary}: ${detail}`);
1287
+ }
1266
1288
 
1267
1289
  // src/adapters/source-control/github/index.ts
1268
1290
  import { execSync } from "child_process";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.223.2",
3
+ "version": "0.224.0",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",