git-watchtower 2.3.4 → 2.3.6
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/bin/git-watchtower.js +11 -5
- package/package.json +1 -1
- package/src/server/web-ui/js.js +1 -1
- package/src/ui/ansi.js +82 -8
package/bin/git-watchtower.js
CHANGED
|
@@ -859,7 +859,7 @@ let verySlowFetchWarningShown = false;
|
|
|
859
859
|
let pollIntervalId = null;
|
|
860
860
|
|
|
861
861
|
// ANSI escape codes and box drawing imported from src/ui/ansi.js
|
|
862
|
-
const { ansi, box, truncate, sparkline: uiSparkline, visibleLength, stripAnsi, padRight, padLeft, getMaxBranchesForScreen: calcMaxBranches, drawBox: renderBox, clearArea: renderClearArea } = require('../src/ui/ansi');
|
|
862
|
+
const { ansi, box, truncate, sparkline: uiSparkline, visibleLength, stripAnsi, sanitizeForRender, padRight, padLeft, getMaxBranchesForScreen: calcMaxBranches, drawBox: renderBox, clearArea: renderClearArea } = require('../src/ui/ansi');
|
|
863
863
|
|
|
864
864
|
// Error detection utilities imported from src/utils/errors.js
|
|
865
865
|
const { isAuthError, isMergeConflict, isNetworkError } = require('../src/utils/errors');
|
|
@@ -1111,10 +1111,16 @@ function getCasinoMessage(type) {
|
|
|
1111
1111
|
function addLog(message, type = 'info') {
|
|
1112
1112
|
const icons = { info: '○', success: '✓', warning: '●', error: '✗', update: '⟳' };
|
|
1113
1113
|
const colors = { info: 'white', success: 'green', warning: 'yellow', error: 'red', update: 'cyan' };
|
|
1114
|
-
// Collapse any whitespace (newlines, tabs, CRs) into a single space
|
|
1115
|
-
//
|
|
1116
|
-
//
|
|
1117
|
-
|
|
1114
|
+
// Collapse any whitespace (newlines, tabs, CRs) into a single space, then
|
|
1115
|
+
// strip dangerous escape sequences (cursor moves, screen clears, OSC,
|
|
1116
|
+
// bell, etc.). git stderr and remote git output regularly contain ANSI
|
|
1117
|
+
// colour AND raw control codes; without sanitising, a colourised
|
|
1118
|
+
// `git fetch` error or a malicious commit subject can corrupt the
|
|
1119
|
+
// rendered box. SGR colour codes survive — sanitizeForRender preserves
|
|
1120
|
+
// them so any styling we want stays.
|
|
1121
|
+
const safeMessage = sanitizeForRender(
|
|
1122
|
+
String(message == null ? '' : message).replace(/\s+/g, ' ').trim()
|
|
1123
|
+
);
|
|
1118
1124
|
const entry = {
|
|
1119
1125
|
message: safeMessage, type,
|
|
1120
1126
|
timestamp: new Date().toLocaleTimeString(),
|
package/package.json
CHANGED
package/src/server/web-ui/js.js
CHANGED
|
@@ -300,7 +300,7 @@ function getDashboardJs() {
|
|
|
300
300
|
const dot = document.getElementById('connection-dot');
|
|
301
301
|
const badge = document.getElementById('status-badge');
|
|
302
302
|
if (ui.connected) {
|
|
303
|
-
dot.className = 'connection-dot
|
|
303
|
+
dot.className = 'connection-dot connected';
|
|
304
304
|
badge.className = 'badge badge-online';
|
|
305
305
|
badge.textContent = 'live';
|
|
306
306
|
} else {
|
package/src/ui/ansi.js
CHANGED
|
@@ -282,14 +282,79 @@ const indicators = {
|
|
|
282
282
|
* Helper functions for working with ANSI codes
|
|
283
283
|
*/
|
|
284
284
|
|
|
285
|
+
// Match any ECMA-48 CSI sequence: ESC [ <params> <intermediates> <final byte>.
|
|
286
|
+
// final byte is in 0x40-0x7E. Covers SGR (m), cursor movement (A-H), erase
|
|
287
|
+
// (J/K), scroll, mode set/reset (h/l), and everything else terminals
|
|
288
|
+
// recognise via CSI.
|
|
289
|
+
// eslint-disable-next-line no-control-regex
|
|
290
|
+
const CSI_RE = /\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g;
|
|
291
|
+
|
|
292
|
+
// Match CSI sequences that are NOT SGR (final byte 'm' / 0x6D). Used by the
|
|
293
|
+
// render-safe sanitiser to drop dangerous controls while preserving colour.
|
|
294
|
+
// eslint-disable-next-line no-control-regex
|
|
295
|
+
const NON_SGR_CSI_RE = /\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x6c\x6e-\x7e]/g;
|
|
296
|
+
|
|
297
|
+
// Match OSC sequences: ESC ] ... terminator (BEL or ESC \). These set
|
|
298
|
+
// terminal title, hyperlinks, etc. — all undesirable in untrusted input.
|
|
299
|
+
// eslint-disable-next-line no-control-regex
|
|
300
|
+
const OSC_RE = /\x1b\][\s\S]*?(?:\x07|\x1b\\)/g;
|
|
301
|
+
|
|
302
|
+
// Match other 2-byte ESC sequences (Fe codes 0x40-0x5F) excluding CSI ([)
|
|
303
|
+
// and OSC (]) which are handled separately above.
|
|
304
|
+
// eslint-disable-next-line no-control-regex
|
|
305
|
+
const ESC_RE = /\x1b[@-Z\\-_]/g;
|
|
306
|
+
|
|
307
|
+
// Match dangerous C0 control characters. Whitespace (\t, \n, \r) and ESC
|
|
308
|
+
// (\x1b) are excluded — ESC is consumed by the escape-sequence regexes
|
|
309
|
+
// above, and SGR codes preserved by sanitizeForRender contain it. Bell,
|
|
310
|
+
// BS, VT, FF, SO, SI, DEL, etc. all get stripped.
|
|
311
|
+
// eslint-disable-next-line no-control-regex
|
|
312
|
+
const C0_RE = /[\x00-\x08\x0b\x0c\x0e-\x1a\x1c-\x1f\x7f]/g;
|
|
313
|
+
|
|
285
314
|
/**
|
|
286
|
-
* Strip ANSI
|
|
315
|
+
* Strip ALL ANSI escape sequences and dangerous C0 control characters.
|
|
316
|
+
* Use this for measuring display width or for fully sanitising untrusted
|
|
317
|
+
* input that won't be styled (e.g. JSON pushed to the web dashboard).
|
|
318
|
+
*
|
|
319
|
+
* Catches CSI (cursor moves, screen clears, SGR colour), OSC (terminal
|
|
320
|
+
* title), other 2-byte ESC sequences, and dangerous C0 controls (bell,
|
|
321
|
+
* backspace, etc). Preserves whitespace (\t, \n, \r).
|
|
322
|
+
*
|
|
287
323
|
* @param {string} str - String potentially containing ANSI codes
|
|
288
324
|
* @returns {string} String with ANSI codes removed
|
|
289
325
|
*/
|
|
290
326
|
function stripAnsi(str) {
|
|
291
|
-
|
|
292
|
-
|
|
327
|
+
return String(str)
|
|
328
|
+
.replace(OSC_RE, '')
|
|
329
|
+
.replace(CSI_RE, '')
|
|
330
|
+
.replace(ESC_RE, '')
|
|
331
|
+
.replace(C0_RE, '')
|
|
332
|
+
// Final pass: any stray ESC bytes left over from malformed sequences.
|
|
333
|
+
// Display surfaces that ask for "no escapes" should never see one.
|
|
334
|
+
// eslint-disable-next-line no-control-regex
|
|
335
|
+
.replace(/\x1b/g, '');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Sanitise a string for safe terminal rendering: strip dangerous escape
|
|
340
|
+
* sequences (cursor moves, screen clears, OSC terminal-title, bell, etc.)
|
|
341
|
+
* while PRESERVING SGR colour/style codes so legitimate styling we built
|
|
342
|
+
* ourselves still renders.
|
|
343
|
+
*
|
|
344
|
+
* Use this on any string that came from outside (commit subjects, branch
|
|
345
|
+
* names, git stderr, PR titles) before it flows to the renderer. A
|
|
346
|
+
* malicious commit subject containing `\x1b[2J` would otherwise clear the
|
|
347
|
+
* screen on every render.
|
|
348
|
+
*
|
|
349
|
+
* @param {string} str - Untrusted string that will be written to a TTY
|
|
350
|
+
* @returns {string} String with dangerous escapes removed, SGR preserved
|
|
351
|
+
*/
|
|
352
|
+
function sanitizeForRender(str) {
|
|
353
|
+
return String(str)
|
|
354
|
+
.replace(OSC_RE, '')
|
|
355
|
+
.replace(NON_SGR_CSI_RE, '')
|
|
356
|
+
.replace(ESC_RE, '')
|
|
357
|
+
.replace(C0_RE, '');
|
|
293
358
|
}
|
|
294
359
|
|
|
295
360
|
/**
|
|
@@ -302,19 +367,27 @@ function visibleLength(str) {
|
|
|
302
367
|
}
|
|
303
368
|
|
|
304
369
|
/**
|
|
305
|
-
* Truncate a string to a maximum visible length, preserving
|
|
306
|
-
*
|
|
370
|
+
* Truncate a string to a maximum visible length, preserving SGR colour
|
|
371
|
+
* codes but stripping any other escape sequences (cursor movement, screen
|
|
372
|
+
* clears, OSC, bell, etc.). This guarantees that even short, non-truncated
|
|
373
|
+
* strings cannot leak terminal-corrupting escapes to the renderer.
|
|
374
|
+
*
|
|
375
|
+
* @param {string} str - String to truncate (may contain SGR + dangerous escapes)
|
|
307
376
|
* @param {number} maxLen - Maximum visible length
|
|
308
377
|
* @param {string} [suffix='…'] - Suffix to append if truncated
|
|
309
378
|
* @returns {string} Truncated string
|
|
310
379
|
*/
|
|
311
380
|
function truncate(str, maxLen, suffix = '…') {
|
|
312
|
-
|
|
381
|
+
// Strip dangerous escapes up front so the fast-path can never return a
|
|
382
|
+
// raw input with cursor/screen-control sequences in it. SGR survives.
|
|
383
|
+
const safeStr = sanitizeForRender(str);
|
|
384
|
+
const visible = stripAnsi(safeStr);
|
|
313
385
|
if (visible.length <= maxLen) {
|
|
314
|
-
return
|
|
386
|
+
return safeStr;
|
|
315
387
|
}
|
|
316
388
|
|
|
317
|
-
//
|
|
389
|
+
// Long-string path: drop ALL escapes (including SGR), truncate, append
|
|
390
|
+
// ellipsis + reset. Existing behaviour, kept for layout determinism.
|
|
318
391
|
const truncated = visible.slice(0, maxLen - suffix.length);
|
|
319
392
|
return truncated + suffix + ansi.reset;
|
|
320
393
|
}
|
|
@@ -497,6 +570,7 @@ module.exports = {
|
|
|
497
570
|
generateSparkline,
|
|
498
571
|
indicators,
|
|
499
572
|
stripAnsi,
|
|
573
|
+
sanitizeForRender,
|
|
500
574
|
visibleLength,
|
|
501
575
|
truncate,
|
|
502
576
|
pad,
|