git-watchtower 1.10.17 → 1.10.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.
@@ -368,6 +368,14 @@ const cliArgs = parseCliArgs(process.argv.slice(2), {
368
368
  onHelp: (v) => { console.log(getHelpText(v)); process.exit(0); },
369
369
  });
370
370
 
371
+ if (cliArgs.errors.length > 0) {
372
+ for (const err of cliArgs.errors) {
373
+ console.error(`Error: ${err}`);
374
+ }
375
+ console.error('\nRun git-watchtower --help for usage information.');
376
+ process.exit(1);
377
+ }
378
+
371
379
  // Configuration - these will be set after config is loaded
372
380
  let SERVER_MODE = 'static'; // 'static' | 'command' | 'none'
373
381
  let NO_SERVER = false; // Derived from SERVER_MODE === 'none'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "1.10.17",
3
+ "version": "1.10.19",
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": {
package/src/cli/args.js CHANGED
@@ -22,6 +22,7 @@ const { version: PACKAGE_VERSION } = require('../../package.json');
22
22
  * @property {boolean} casino - Enable casino mode
23
23
  * @property {boolean} web - Enable web dashboard mode
24
24
  * @property {number|null} webPort - Web dashboard port override
25
+ * @property {string[]} errors - Validation errors encountered during parsing
25
26
  */
26
27
 
27
28
  /**
@@ -55,6 +56,8 @@ function parseArgs(argv, options = {}) {
55
56
  // Actions
56
57
  init: false,
57
58
  casino: false,
59
+ // Parsing errors
60
+ errors: [],
58
61
  };
59
62
 
60
63
  for (let i = 0; i < args.length; i++) {
@@ -63,21 +66,33 @@ function parseArgs(argv, options = {}) {
63
66
  const mode = args[i + 1];
64
67
  if (['static', 'command', 'none'].includes(mode)) {
65
68
  result.mode = mode;
69
+ } else {
70
+ result.errors.push(`Invalid value for ${args[i]}: "${mode || ''}" (expected: static, command, none)`);
66
71
  }
67
72
  i++;
68
73
  } else if (args[i] === '--port' || args[i] === '-p') {
69
74
  const portValue = parseInt(args[i + 1], 10);
70
75
  if (!isNaN(portValue) && portValue > 0 && portValue < 65536) {
71
76
  result.port = portValue;
77
+ } else {
78
+ result.errors.push(`Invalid value for ${args[i]}: "${args[i + 1] || ''}" (expected: port number 1-65535)`);
72
79
  }
73
80
  i++;
74
81
  } else if (args[i] === '--no-server' || args[i] === '-n') {
75
82
  result.noServer = true;
76
83
  } else if (args[i] === '--static-dir') {
77
- result.staticDir = args[i + 1];
84
+ if (args[i + 1] && !args[i + 1].startsWith('-')) {
85
+ result.staticDir = args[i + 1];
86
+ } else {
87
+ result.errors.push(`Missing value for ${args[i]}`);
88
+ }
78
89
  i++;
79
90
  } else if (args[i] === '--command' || args[i] === '-c') {
80
- result.command = args[i + 1];
91
+ if (args[i + 1] !== undefined) {
92
+ result.command = args[i + 1];
93
+ } else {
94
+ result.errors.push(`Missing value for ${args[i]}`);
95
+ }
81
96
  i++;
82
97
  } else if (args[i] === '--restart-on-switch') {
83
98
  result.restartOnSwitch = true;
@@ -86,7 +101,11 @@ function parseArgs(argv, options = {}) {
86
101
  }
87
102
  // Git settings
88
103
  else if (args[i] === '--remote' || args[i] === '-r') {
89
- result.remote = args[i + 1];
104
+ if (args[i + 1] && !args[i + 1].startsWith('-')) {
105
+ result.remote = args[i + 1];
106
+ } else {
107
+ result.errors.push(`Missing value for ${args[i]}`);
108
+ }
90
109
  i++;
91
110
  } else if (args[i] === '--auto-pull') {
92
111
  result.autoPull = true;
@@ -96,6 +115,8 @@ function parseArgs(argv, options = {}) {
96
115
  const interval = parseInt(args[i + 1], 10);
97
116
  if (!isNaN(interval) && interval > 0) {
98
117
  result.pollInterval = interval;
118
+ } else {
119
+ result.errors.push(`Invalid value for ${args[i]}: "${args[i + 1] || ''}" (expected: positive integer in ms)`);
99
120
  }
100
121
  i++;
101
122
  }
@@ -108,6 +129,8 @@ function parseArgs(argv, options = {}) {
108
129
  const count = parseInt(args[i + 1], 10);
109
130
  if (!isNaN(count) && count > 0) {
110
131
  result.visibleBranches = count;
132
+ } else {
133
+ result.errors.push(`Invalid value for ${args[i]}: "${args[i + 1] || ''}" (expected: positive integer)`);
111
134
  }
112
135
  i++;
113
136
  } else if (args[i] === '--casino') {
@@ -120,6 +143,8 @@ function parseArgs(argv, options = {}) {
120
143
  const webPortValue = parseInt(args[i + 1], 10);
121
144
  if (!isNaN(webPortValue) && webPortValue > 0 && webPortValue < 65536) {
122
145
  result.webPort = webPortValue;
146
+ } else {
147
+ result.errors.push(`Invalid value for ${args[i]}: "${args[i + 1] || ''}" (expected: port number 1-65535)`);
123
148
  }
124
149
  i++;
125
150
  }
@@ -135,6 +160,10 @@ function parseArgs(argv, options = {}) {
135
160
  options.onHelp(PACKAGE_VERSION);
136
161
  }
137
162
  }
163
+ // Unknown flag
164
+ else if (args[i].startsWith('-')) {
165
+ result.errors.push(`Unknown option: ${args[i]}`);
166
+ }
138
167
  }
139
168
  return result;
140
169
  }
@@ -26,6 +26,13 @@ const crypto = require('crypto');
26
26
  */
27
27
  const WATCHTOWER_DIR = path.join(os.homedir(), '.watchtower');
28
28
 
29
+ /**
30
+ * Maximum IPC receive buffer size (1 MiB). Connections that exceed
31
+ * this without a complete newline-delimited message are dropped to
32
+ * prevent unbounded memory growth from malformed or malicious peers.
33
+ */
34
+ const MAX_IPC_BUFFER = 1024 * 1024;
35
+
29
36
  /**
30
37
  * Lock file path
31
38
  */
@@ -268,6 +275,10 @@ class Coordinator {
268
275
 
269
276
  socket.on('data', (data) => {
270
277
  buffer += data.toString();
278
+ if (buffer.length > MAX_IPC_BUFFER) {
279
+ socket.destroy();
280
+ return;
281
+ }
271
282
  let newlineIdx;
272
283
  while ((newlineIdx = buffer.indexOf('\n')) !== -1) {
273
284
  const line = buffer.slice(0, newlineIdx);
@@ -413,6 +424,10 @@ class Worker {
413
424
 
414
425
  this.socket.on('data', (data) => {
415
426
  this._buffer += data.toString();
427
+ if (this._buffer.length > MAX_IPC_BUFFER) {
428
+ this.socket.destroy();
429
+ return;
430
+ }
416
431
  let idx;
417
432
  while ((idx = this._buffer.indexOf('\n')) !== -1) {
418
433
  const line = this._buffer.slice(0, idx);