termbeam 1.19.1 → 1.19.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termbeam",
3
- "version": "1.19.1",
3
+ "version": "1.19.3",
4
4
  "description": "Beam your terminal to any device — mobile-optimized web terminal with multi-session support",
5
5
  "main": "src/server/index.js",
6
6
  "bin": {
@@ -32,10 +32,12 @@ function createPreviewProxy() {
32
32
  .json({ error: 'Invalid port: must be an integer between 1 and 65535' });
33
33
  }
34
34
 
35
- // Strip /preview/:port prefix, keep the rest (or default to /)
36
- // Express 5 *path returns an array of segments join them back
37
- const segments = req.params.path;
38
- const forwardPath = segments ? `/${[].concat(segments).join('/')}` : '/';
35
+ // Derive forward path from req.url to preserve trailing slashes.
36
+ // Express *path params lose trailing slashes, causing redirect loops
37
+ // with servers that enforce them (e.g. mkdocs: /TermBeam → /TermBeam/).
38
+ const portPrefix = `/${req.params.port}`;
39
+ const urlWithoutQuery = req.url.split('?')[0];
40
+ const forwardPath = urlWithoutQuery.slice(portPrefix.length) || '/';
39
41
  const search = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
40
42
 
41
43
  const fwdHeaders = { ...req.headers, host: `127.0.0.1:${port}` };
@@ -10,6 +10,15 @@ const log = require('../utils/logger');
10
10
  const rateLimit = require('express-rate-limit');
11
11
 
12
12
  const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');
13
+
14
+ // Resolve a user-provided path relative to rootDir and verify it stays within bounds.
15
+ // Returns the resolved path or null if it escapes rootDir.
16
+ function safePath(rootDir, userPath) {
17
+ const resolved = path.resolve(rootDir, userPath);
18
+ if (!resolved.startsWith(rootDir + path.sep) && resolved !== rootDir) return null;
19
+ return resolved;
20
+ }
21
+
13
22
  const uploadedFiles = new Map(); // id -> filepath
14
23
 
15
24
  const IMAGE_SIGNATURES = [
@@ -644,7 +653,10 @@ function setupRoutes(app, { auth, sessions, config, state, pushManager }) {
644
653
  }
645
654
 
646
655
  const rootDir = path.resolve(sessions.getSessionCwd(req.params.id));
647
- const dir = path.resolve(rootDir, req.query.dir || '.');
656
+ const dir = safePath(rootDir, req.query.dir || '.');
657
+ if (!dir) {
658
+ return res.status(403).json({ error: 'Path is outside session directory' });
659
+ }
648
660
 
649
661
  const MAX_ENTRIES = 1000;
650
662
  try {
@@ -798,7 +810,10 @@ function setupRoutes(app, { auth, sessions, config, state, pushManager }) {
798
810
  }
799
811
 
800
812
  const rootDir = path.resolve(sessions.getSessionCwd(req.params.id));
801
- const filePath = path.resolve(rootDir, file);
813
+ const filePath = safePath(rootDir, file);
814
+ if (!filePath) {
815
+ return res.status(403).json({ error: 'Path is outside session directory' });
816
+ }
802
817
 
803
818
  try {
804
819
  if (fs.lstatSync(filePath).isSymbolicLink()) {
@@ -829,7 +844,10 @@ function setupRoutes(app, { auth, sessions, config, state, pushManager }) {
829
844
  }
830
845
 
831
846
  const rootDir = path.resolve(sessions.getSessionCwd(req.params.id));
832
- const filePath = path.resolve(rootDir, file);
847
+ const filePath = safePath(rootDir, file);
848
+ if (!filePath) {
849
+ return res.status(403).json({ error: 'Path is outside session directory' });
850
+ }
833
851
 
834
852
  try {
835
853
  if (fs.lstatSync(filePath).isSymbolicLink()) {
@@ -860,7 +878,10 @@ function setupRoutes(app, { auth, sessions, config, state, pushManager }) {
860
878
  }
861
879
 
862
880
  const rootDir = path.resolve(sessions.getSessionCwd(req.params.id));
863
- const filePath = path.resolve(rootDir, file);
881
+ const filePath = safePath(rootDir, file);
882
+ if (!filePath) {
883
+ return res.status(403).json({ error: 'Path is outside session directory' });
884
+ }
864
885
 
865
886
  try {
866
887
  if (fs.lstatSync(filePath).isSymbolicLink()) {