aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-01 13:26:34 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-01 13:26:34 +0200
commit8f5037a4321633dd96f2f320869aac1f96ed4c01 (patch)
tree883f87ce1eadf8baaf38ee0ae033cff8f411aa16 /packages/meshbay-hub/src/meshbay_hub
parentb1878ab982ab72571915e7fbe2c1b558ea31f838 (diff)
downloadmeshbay-8f5037a4321633dd96f2f320869aac1f96ed4c01.tar.gz
fix(client): the audit export never hands a spreadsheet a formula
A cell starting with = + - @ (or a tab or carriage return) gets a leading apostrophe; the export carries text members chose (F-29). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/csv.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/node-page.js7
2 files changed, 16 insertions, 5 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/csv.js b/packages/meshbay-hub/src/meshbay_hub/static/csv.js
new file mode 100644
index 0000000..310bdca
--- /dev/null
+++ b/packages/meshbay-hub/src/meshbay_hub/static/csv.js
@@ -0,0 +1,14 @@
+/**
+ * One CSV cell, quoted when it has to be, and never a formula.
+ *
+ * A spreadsheet runs a cell that starts with `=`, `+`, `-` or `@` (or a tab or a
+ * carriage return in front of one) as a formula. The audit export carries text
+ * a member chose — a refused blob's kind, a file name — so such a cell is given
+ * a leading apostrophe, which spreadsheets read as "this is text", and which is
+ * what other exports do.
+ */
+export function csvCell(value) {
+ let s = value == null ? '' : String(value);
+ if (/^[=+\-@\t\r]/.test(s)) s = `'${s}`;
+ return /[",\n\r]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
+}
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js
index f940934..41e9567 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js
@@ -1,3 +1,4 @@
+import { csvCell } from './csv.js';
import {
html, useState, useEffect, useCallback, useRef,
} from './vendor/htm-preact.js';
@@ -585,17 +586,13 @@ export function NodePage({ groups, token, username }) {
const cols = ['timestamp', 'event', 'user', 'user_id', 'ip',
'group', 'group_id', 'detail'];
- const esc = (v) => {
- const s = v == null ? '' : String(v);
- return /[",\n\r]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
- };
const lines = [cols.join(',')];
for (const e of rows) {
lines.push([
new Date(e.timestamp * 1000).toISOString(),
e.event, e.username || '', e.user_id || '', e.ip || '',
e.group_name || '', e.group_id || '', e.detail || '',
- ].map(esc).join(','));
+ ].map(csvCell).join(','));
}
const csv = lines.join('\r\n') + '\r\n';
const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');