@bli-cockpit/cli 0.2.18 → 0.2.19

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.
package/dist/autostart.js CHANGED
@@ -353,8 +353,16 @@ function renderWindowsRegistrationScript(options) {
353
353
  `$taskName = ${powershellLiteral(options.taskName)}`,
354
354
  `$syncScriptPath = ${powershellLiteral(options.scriptPath)}`,
355
355
  "$powershellPath = Join-Path $PSHOME 'powershell.exe'",
356
- "$actionArgs = '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"' + $syncScriptPath + '\"'",
357
- "$taskCommand = '\"' + $powershellPath + '\" ' + $actionArgs",
356
+ // Windows PowerShell 5.1 passes a native argument's embedded quotes to
357
+ // CommandLineToArgvW unescaped, so a /TR value with bare quotes falls out
358
+ // of the quoted region at the first space inside a quoted path:
359
+ // `C:\Users\Brandon Chiem\...` reached schtasks as a stray `Chiem\...`
360
+ // argument and /Create exited 0x80004005 on every CLI version (BLI-2598).
361
+ // Escaping the embedded quotes as \" keeps the whole /TR value one
362
+ // argument; Task Scheduler still normalizes what it stores (BLI-2541),
363
+ // which the read-back validator already compares path-wise.
364
+ "$actionArgs = '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \\\"' + $syncScriptPath + '\\\"'",
365
+ "$taskCommand = '\\\"' + $powershellPath + '\\\" ' + $actionArgs",
358
366
  `& schtasks.exe /Create /TN $taskName /TR $taskCommand /SC MINUTE /MO ${options.intervalMinutes} /IT /RL LIMITED /F | Out-Null`,
359
367
  "if ($LASTEXITCODE -ne 0) { throw \"schtasks /Create exited $LASTEXITCODE\" }",
360
368
  "$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew",
@@ -15,7 +15,7 @@ export async function runCockpitCli(argv, io) {
15
15
  }
16
16
 
17
17
  if (command === "--version" || command === "-V" || command === "version") {
18
- writeLine(io?.stdout ?? process.stdout, "0.2.18");
18
+ writeLine(io?.stdout ?? process.stdout, "0.2.19");
19
19
  return 0;
20
20
  }
21
21
 
@@ -27,9 +27,24 @@ import { redactSecretLikeContent } from "@bli-cockpit/telemetry-core";
27
27
  export const HEALTH_DETAIL_MAX_CHARS = 600;
28
28
  const WINDOWS_ABSOLUTE_PATH = /[A-Za-z]:\\[^\s"';]+/gu;
29
29
  const UNC_PATH = /\\\\[^\s"';]+/gu;
30
+ // A quoted Windows path may contain spaces; the quotes bound it, so the whole
31
+ // quoted region is the path. Unquoted, the space-blind pattern above stops at
32
+ // the first space — which is how `C:\Users\Brandon Chiem\...` masked to
33
+ // `[path] Chiem\...` and an operator surname reached the server (BLI-2599).
34
+ const QUOTED_WINDOWS_PATH = /"[A-Za-z]:\\[^"]*"/gu;
35
+ const SINGLE_QUOTED_WINDOWS_PATH = /'[A-Za-z]:\\[^']*'/gu;
36
+ // A backslash-joined fragment of three or more segments is path material even
37
+ // without a drive anchor: schtasks echoes a severed path tail as its own token
38
+ // (`Chiem\.local\state\...`, the BLI-2598 receipt). Over-matching here is the
39
+ // safe direction — this boundary exists to keep local identifiers on the
40
+ // machine, not to keep messages pretty.
41
+ const BACKSLASH_PATH_FRAGMENT = /[^\s"';:,()]+(?:\\[^\s"';:,()]+){2,}/gu;
30
42
  // An absolute POSIX path of at least two segments. One segment ("/tmp") carries
31
43
  // nothing identifying and masking it would make messages harder to read.
32
44
  const POSIX_ABSOLUTE_PATH = /\/[\w.@-]+(?:\/[\w.@ -]+)+/gu;
45
+ // The tail a space split off a masked path: `[path] Chiem\...` collapses back
46
+ // into the placeholder it was severed from.
47
+ const PATH_PLACEHOLDER_RUN = /\[path\](?:\s+\[path\])+/gu;
33
48
  export function redactedHealthDetail(message) {
34
49
  const collapsed = message.replace(/\s+/gu, " ").trim();
35
50
  if (!collapsed)
@@ -43,20 +58,28 @@ export function redactedHealthDetail(message) {
43
58
  : masked;
44
59
  }
45
60
  /**
46
- * Paths first, because a path usually contains the account name; whatever
47
- * mentions of the host or account survive on their own are masked after.
61
+ * Identifiers first, so the account name is caught even when it sits inside a
62
+ * path the path patterns would only partially cover (BLI-2599: the path
63
+ * pattern stopped at the space in `Brandon Chiem` and the surname survived).
64
+ * Then quoted paths before unquoted ones, because the quotes are what bound a
65
+ * space-containing path; then the drive-anchored and fragment patterns; then
66
+ * adjacent placeholders collapse so a severed path reads as one `[path]`.
48
67
  */
49
68
  export function maskLocalIdentifiers(value) {
50
- let text = value
51
- .replace(UNC_PATH, "[path]")
52
- .replace(WINDOWS_ABSOLUTE_PATH, "[path]")
53
- .replace(POSIX_ABSOLUTE_PATH, "[path]");
69
+ let text = value;
54
70
  for (const [identifier, placeholder] of localIdentifiers()) {
55
71
  if (!identifier)
56
72
  continue;
57
73
  text = text.replace(literalPattern(identifier), placeholder);
58
74
  }
59
- return text;
75
+ return text
76
+ .replace(QUOTED_WINDOWS_PATH, "[path]")
77
+ .replace(SINGLE_QUOTED_WINDOWS_PATH, "[path]")
78
+ .replace(UNC_PATH, "[path]")
79
+ .replace(WINDOWS_ABSOLUTE_PATH, "[path]")
80
+ .replace(BACKSLASH_PATH_FRAGMENT, "[path]")
81
+ .replace(POSIX_ABSOLUTE_PATH, "[path]")
82
+ .replace(PATH_PLACEHOLDER_RUN, "[path]");
60
83
  }
61
84
  function localIdentifiers() {
62
85
  const identifiers = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/cli",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {