swarmpath-claudecode-bridge 1.2.0 → 1.2.1

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.
Files changed (2) hide show
  1. package/index.mjs +16 -2
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -151,6 +151,7 @@ const cfg = loadConfig();
151
151
  const SERVER = getArg('server') || cfg.server || process.env.SWARMPATH_SERVER;
152
152
  let TOKEN = getArg('token') || process.env.SWARMPATH_TOKEN;
153
153
  let CWD = getArg('cwd') || process.cwd();
154
+ const ROOT_CWD = resolve(CWD); // Sandbox root — cd cannot escape this directory
154
155
 
155
156
  if (!SERVER) {
156
157
  console.error('❌ 未配置服务器。请先运行: npx swarmpath-claudecode-bridge --setup');
@@ -186,8 +187,10 @@ const terminalSessions = new Map();
186
187
  const HEARTBEAT_MS = 30_000;
187
188
  const RECONNECT_MS = 5_000;
188
189
  const MAX_RECONNECT_MS = 60_000;
190
+ const MAX_RECONNECT_ATTEMPTS = 5; // Exit after 5 failed reconnects
189
191
  const TOKEN_REFRESH_MS = 12 * 60 * 1000; // Refresh token every 12 min (before 15-min expiry)
190
192
  let reconnectDelay = RECONNECT_MS;
193
+ let reconnectAttempts = 0;
191
194
 
192
195
  // ---------------------------------------------------------------------------
193
196
  // Token auto-refresh
@@ -217,6 +220,7 @@ function connect() {
217
220
  ws.on('open', () => {
218
221
  console.log('✅ Connected to SwarmPath server');
219
222
  reconnectDelay = RECONNECT_MS;
223
+ reconnectAttempts = 0;
220
224
 
221
225
  clearInterval(heartbeatTimer);
222
226
  heartbeatTimer = setInterval(() => {
@@ -267,7 +271,12 @@ function connect() {
267
271
 
268
272
  function scheduleReconnect() {
269
273
  if (reconnectTimer) return;
270
- console.log(`⏳ Reconnecting in ${reconnectDelay / 1000}s ...`);
274
+ reconnectAttempts++;
275
+ if (reconnectAttempts > MAX_RECONNECT_ATTEMPTS) {
276
+ console.log(`\n❌ 连续 ${MAX_RECONNECT_ATTEMPTS} 次重连失败,自动退出。`);
277
+ process.exit(1);
278
+ }
279
+ console.log(`⏳ Reconnecting in ${reconnectDelay / 1000}s ... (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
271
280
  reconnectTimer = setTimeout(() => {
272
281
  reconnectTimer = null;
273
282
  reconnectDelay = Math.min(reconnectDelay * 1.5, MAX_RECONNECT_MS);
@@ -533,6 +542,10 @@ function handleFileRequest(msg) {
533
542
  if (action === 'cd') {
534
543
  const target = msg.path || '..';
535
544
  const newCwd = resolve(CWD, target);
545
+ // Security: only allow navigating into subdirectories of ROOT_CWD
546
+ if (!newCwd.startsWith(ROOT_CWD + '/') && newCwd !== ROOT_CWD) {
547
+ throw new Error(`Access denied: cannot navigate above root directory (${ROOT_CWD})`);
548
+ }
536
549
  const st = statSync(newCwd);
537
550
  if (!st.isDirectory()) throw new Error('Not a directory');
538
551
  CWD = newCwd;
@@ -547,7 +560,7 @@ function handleFileRequest(msg) {
547
560
  const relPath = msg.path;
548
561
  if (!relPath) throw new Error('path required');
549
562
  const fullPath = resolve(CWD, relPath);
550
- if (!fullPath.startsWith(resolve(CWD) + '/') && fullPath !== resolve(CWD)) throw new Error('Access denied');
563
+ if (!fullPath.startsWith(ROOT_CWD + '/') && fullPath !== ROOT_CWD) throw new Error('Access denied: outside sandbox root');
551
564
  const ext = extname(fullPath).toLowerCase();
552
565
  const mime = MIME_MAP[ext] || 'application/octet-stream';
553
566
  const isText = mime.startsWith('text/') || mime === 'application/json' || mime === 'text/markdown';
@@ -623,5 +636,6 @@ process.on('SIGTERM', shutdown);
623
636
  // ---------------------------------------------------------------------------
624
637
  console.log('🌐 SwarmPath Claude Code Bridge');
625
638
  console.log(`📁 Working directory: ${CWD}`);
639
+ console.log(`🔒 Sandbox root: ${ROOT_CWD} (cd cannot escape)`);
626
640
  startTokenRefresh();
627
641
  connect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swarmpath-claudecode-bridge",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Connect local Claude Code to SwarmPath Chat — control your Mac from your phone",
5
5
  "type": "module",
6
6
  "bin": {