git-watchtower 2.1.15 → 2.1.17

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.
@@ -576,6 +576,18 @@ async function checkCliAuth(cmd) {
576
576
  }
577
577
 
578
578
  // Bulk-fetch PR statuses for all branches (parsing delegated to src/git/pr.js)
579
+ //
580
+ // PR_LIST_LIMIT is the cap passed to `gh pr list` / `glab mr list`. The
581
+ // previous values (200 for gh, glab's default of 30) silently truncated
582
+ // on any active OSS repo — branches whose PR was outside the most recent
583
+ // N rendered with no inline indicator and never sorted as merged.
584
+ // 1000 covers virtually any real-world repo while still keeping the
585
+ // single API call fast (gh streams JSON, no pagination overhead). If
586
+ // your repo somehow exceeds 1000 active PRs, the truncation is back —
587
+ // but at that scale the inline indicator is the smallest of your UX
588
+ // concerns.
589
+ const PR_LIST_LIMIT = '1000';
590
+
579
591
  async function fetchAllPrStatuses() {
580
592
  if (!cachedEnv) return null;
581
593
  const { platform, hasGh, ghAuthed, hasGlab, glabAuthed } = cachedEnv;
@@ -584,7 +596,7 @@ async function fetchAllPrStatuses() {
584
596
  try {
585
597
  const { stdout } = await execCli('gh', [
586
598
  'pr', 'list', '--state', 'all',
587
- '--json', 'headRefName,number,title,state', '--limit', '200',
599
+ '--json', 'headRefName,number,title,state', '--limit', PR_LIST_LIMIT,
588
600
  ]);
589
601
  return parseGitHubPrList(JSON.parse(stdout));
590
602
  } catch (e) { /* gh error */ }
@@ -594,6 +606,7 @@ async function fetchAllPrStatuses() {
594
606
  try {
595
607
  const { stdout } = await execCli('glab', [
596
608
  'mr', 'list', '--state', 'all', '--output', 'json',
609
+ '--per-page', PR_LIST_LIMIT,
597
610
  ]);
598
611
  return parseGitLabMrList(JSON.parse(stdout));
599
612
  } catch (e) { /* glab error */ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "2.1.15",
3
+ "version": "2.1.17",
4
4
  "description": "Terminal-based Git branch monitor with activity sparklines and optional dev server with live reload",
5
5
  "main": "bin/git-watchtower.js",
6
6
  "bin": {
@@ -81,11 +81,28 @@ const LOCK_FILE = path.join(WATCHTOWER_DIR, 'web.lock');
81
81
  const SOCKET_PATH = path.join(WATCHTOWER_DIR, 'web.sock');
82
82
 
83
83
  /**
84
- * Ensure the ~/.watchtower directory exists.
84
+ * Ensure the ~/.watchtower directory exists, restricted to the owner.
85
+ *
86
+ * The directory hosts the lock file and the Unix-domain IPC socket;
87
+ * both should only be reachable by the user running git-watchtower.
88
+ * On Linux, connect() to a Unix socket requires read+execute on the
89
+ * containing directory, so 0o700 is sufficient defense-in-depth even
90
+ * when the socket file itself ends up world-readable due to umask.
91
+ *
92
+ * mkdirSync's mode argument only applies when the directory is being
93
+ * created — it has no effect on a pre-existing dir. We chmod after
94
+ * the create check so installs that ran before this fix get migrated
95
+ * to the tighter mode on next launch.
85
96
  */
86
97
  function ensureDir() {
87
98
  if (!fs.existsSync(WATCHTOWER_DIR)) {
88
- fs.mkdirSync(WATCHTOWER_DIR, { recursive: true });
99
+ fs.mkdirSync(WATCHTOWER_DIR, { recursive: true, mode: 0o700 });
100
+ }
101
+ try {
102
+ fs.chmodSync(WATCHTOWER_DIR, 0o700);
103
+ } catch (e) {
104
+ // chmod may fail on Windows or filesystems without POSIX perms; the
105
+ // permission semantics don't apply there anyway, so silently skip.
89
106
  }
90
107
  }
91
108
 
@@ -290,6 +307,20 @@ class Coordinator {
290
307
  });
291
308
 
292
309
  this.ipcServer.listen(this.socketPath, () => {
310
+ // Defense-in-depth: tighten the socket file's own perms after
311
+ // bind. Node's net.Server.listen() creates the socket file with
312
+ // perms derived from umask, which on shared workstations
313
+ // (umask 0022) yields 0o755 — world-readable. Linux ignores
314
+ // socket-file perms for connect() (the directory's perms are
315
+ // authoritative), but BSD honours them and other tooling may
316
+ // surface them in audit reports. Setting 0o600 explicitly
317
+ // matches the 0o700 directory perms set in ensureDir().
318
+ try {
319
+ fs.chmodSync(this.socketPath, 0o600);
320
+ } catch (e) {
321
+ // Non-POSIX filesystem or Windows — perm semantics don't
322
+ // apply, skip silently.
323
+ }
293
324
  resolve();
294
325
  });
295
326
  });
@@ -52,8 +52,16 @@ function lockFilePath(repoRoot) {
52
52
  }
53
53
 
54
54
  function ensureDir() {
55
+ // 0o700 keeps lock files (which contain pids and the cwd of running
56
+ // git-watchtower instances) unreadable to other local users. See
57
+ // src/server/coordinator.js for the rationale and Windows note.
55
58
  if (!fs.existsSync(WATCHTOWER_DIR)) {
56
- fs.mkdirSync(WATCHTOWER_DIR, { recursive: true });
59
+ fs.mkdirSync(WATCHTOWER_DIR, { recursive: true, mode: 0o700 });
60
+ }
61
+ try {
62
+ fs.chmodSync(WATCHTOWER_DIR, 0o700);
63
+ } catch (e) {
64
+ // chmod may fail on Windows / non-POSIX filesystems; skip silently.
57
65
  }
58
66
  }
59
67