@kaitranntt/ccs 7.78.0-dev.5 → 7.78.0-dev.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"codex-routes.d.ts","sourceRoot":"","sources":["../../../src/web-server/routes/codex-routes.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA6ExB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"codex-routes.d.ts","sourceRoot":"","sources":["../../../src/web-server/routes/codex-routes.ts"],"names":[],"mappings":"AAYA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAqFxB,eAAe,MAAM,CAAC"}
@@ -1,8 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const express_1 = require("express");
4
+ const auth_middleware_1 = require("../middleware/auth-middleware");
4
5
  const codex_dashboard_service_1 = require("../services/codex-dashboard-service");
5
6
  const router = (0, express_1.Router)();
7
+ const CODEX_CONFIG_ACCESS_ERROR = 'Codex configuration endpoints require localhost access when dashboard auth is disabled.';
8
+ router.use('/config', (req, res, next) => {
9
+ if ((0, auth_middleware_1.requireLocalAccessWhenAuthDisabled)(req, res, CODEX_CONFIG_ACCESS_ERROR)) {
10
+ next();
11
+ }
12
+ });
6
13
  router.get('/diagnostics', async (_req, res) => {
7
14
  try {
8
15
  res.json(await (0, codex_dashboard_service_1.getCodexDashboardDiagnostics)());
@@ -1 +1 @@
1
- {"version":3,"file":"codex-routes.js","sourceRoot":"","sources":["../../../src/web-server/routes/codex-routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codex-routes.js","sourceRoot":"","sources":["../../../src/web-server/routes/codex-routes.ts"],"names":[],"mappings":"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"}
@@ -151,6 +151,8 @@ const DEFAULT_DRAG_STEPS = 5;
151
151
  const MAX_POINTER_ACTIONS = 25;
152
152
  const SESSION_START_SETTLE_WINDOW_MS = 250;
153
153
  const MAX_ARTIFACT_FILE_BYTES = 5 * 1024 * 1024;
154
+ const MAX_LOCAL_TRANSFER_FILE_BYTES = 10 * 1024 * 1024;
155
+ const MAX_LOCAL_TRANSFER_FILES = 10;
154
156
  const SAFE_ARTIFACT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
155
157
  const SESSION_CANCELED_ERROR_CODE = 'SESSION_CANCELED';
156
158
 
@@ -177,6 +179,37 @@ const recentDownloads = [];
177
179
  const interceptSessionsByPageId = new Map();
178
180
  let browserDownloadSession = null;
179
181
  let sessionDownloadDir = '';
182
+ const SENSITIVE_LOCAL_PATH_SEGMENTS = new Set([
183
+ '.ssh',
184
+ '.gnupg',
185
+ '.aws',
186
+ '.azure',
187
+ '.kube',
188
+ '.docker',
189
+ '.npmrc',
190
+ '.netrc',
191
+ '.pypirc',
192
+ '.config',
193
+ '.claude',
194
+ '.ccs',
195
+ ]);
196
+ const SENSITIVE_LOCAL_FILE_NAMES = new Set([
197
+ '.env',
198
+ 'id_rsa',
199
+ 'id_dsa',
200
+ 'id_ecdsa',
201
+ 'id_ed25519',
202
+ 'known_hosts',
203
+ 'authorized_keys',
204
+ 'credentials',
205
+ 'credentials.json',
206
+ 'config.json',
207
+ 'settings.json',
208
+ 'history',
209
+ '.bash_history',
210
+ '.zsh_history',
211
+ '.fish_history',
212
+ ]);
180
213
  const MAX_RECENT_REQUESTS = 100;
181
214
  const MAX_RECENT_DOWNLOADS = 100;
182
215
  const FETCH_FAIL_ERROR_REASON = 'Failed';
@@ -1438,10 +1471,105 @@ function getSessionDownloadPath() {
1438
1471
  return sessionDownloadDir;
1439
1472
  }
1440
1473
 
1474
+ function splitConfiguredPathRoots(value) {
1475
+ return String(value || '')
1476
+ .split(path.delimiter)
1477
+ .map((entry) => entry.trim())
1478
+ .filter(Boolean)
1479
+ .map((entry) => path.resolve(entry));
1480
+ }
1481
+
1482
+ function getDownloadSafeRoots() {
1483
+ return [
1484
+ getSessionDownloadPath(),
1485
+ ...splitConfiguredPathRoots(process.env.CCS_BROWSER_DOWNLOAD_ROOTS),
1486
+ ];
1487
+ }
1488
+
1489
+ function getUploadSafeRoots() {
1490
+ return [
1491
+ getSessionDownloadPath(),
1492
+ ...splitConfiguredPathRoots(process.env.CCS_BROWSER_UPLOAD_ROOTS),
1493
+ ];
1494
+ }
1495
+
1496
+ function getNearestExistingAncestor(candidatePath) {
1497
+ let currentPath = candidatePath;
1498
+ while (!fs.existsSync(currentPath)) {
1499
+ const parentPath = path.dirname(currentPath);
1500
+ if (parentPath === currentPath) {
1501
+ return currentPath;
1502
+ }
1503
+ currentPath = parentPath;
1504
+ }
1505
+ return currentPath;
1506
+ }
1507
+
1508
+ function resolveExistingRoot(rootPath) {
1509
+ fs.mkdirSync(rootPath, { recursive: true });
1510
+ return fs.realpathSync(rootPath);
1511
+ }
1512
+
1513
+ function resolvePathWithRealAncestor(candidatePath) {
1514
+ const resolvedPath = path.resolve(candidatePath);
1515
+ const ancestorPath = getNearestExistingAncestor(resolvedPath);
1516
+ const realAncestorPath = fs.realpathSync(ancestorPath);
1517
+ const relativeSuffix = path.relative(ancestorPath, resolvedPath);
1518
+ return relativeSuffix ? path.resolve(realAncestorPath, relativeSuffix) : realAncestorPath;
1519
+ }
1520
+
1521
+ function isPathInsideRoot(candidatePath, rootPath) {
1522
+ const relativePath = path.relative(rootPath, candidatePath);
1523
+ return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
1524
+ }
1525
+
1526
+ function findContainingRoot(candidatePath, rootPaths) {
1527
+ return rootPaths.find((rootPath) => isPathInsideRoot(candidatePath, rootPath)) || '';
1528
+ }
1529
+
1530
+ function getLocalPathSegments(candidatePath, rootPath) {
1531
+ const rootSegments = path.resolve(rootPath).split(path.sep).filter(Boolean);
1532
+ const relativePath = path.relative(rootPath, candidatePath);
1533
+ const relativeSegments = relativePath.split(path.sep).filter(Boolean);
1534
+ return [...rootSegments, ...relativeSegments];
1535
+ }
1536
+
1537
+ function assertNoSensitiveLocalPathSegments(candidatePath, rootPath, label) {
1538
+ const segments = getLocalPathSegments(candidatePath, rootPath);
1539
+ for (const segment of segments) {
1540
+ const normalizedSegment = segment.toLowerCase();
1541
+ if (normalizedSegment.startsWith('.') || SENSITIVE_LOCAL_PATH_SEGMENTS.has(normalizedSegment)) {
1542
+ throw new Error(`${label} cannot include hidden or sensitive path segment: ${segment}`);
1543
+ }
1544
+ }
1545
+
1546
+ const fileName = path.basename(candidatePath).toLowerCase();
1547
+ if (SENSITIVE_LOCAL_FILE_NAMES.has(fileName)) {
1548
+ throw new Error(
1549
+ `${label} cannot reference sensitive file name: ${path.basename(candidatePath)}`
1550
+ );
1551
+ }
1552
+ }
1553
+
1441
1554
  function ensureWritableDirectory(downloadPath) {
1442
- fs.mkdirSync(downloadPath, { recursive: true });
1443
- fs.accessSync(downloadPath, fs.constants.W_OK);
1444
- return downloadPath;
1555
+ const resolvedPath = path.resolve(downloadPath);
1556
+ const candidatePath = resolvePathWithRealAncestor(resolvedPath);
1557
+ const safeRoots = getDownloadSafeRoots().map(resolveExistingRoot);
1558
+ const containingRoot = findContainingRoot(candidatePath, safeRoots);
1559
+ if (!containingRoot) {
1560
+ throw new Error(
1561
+ 'downloadPath must be inside the browser session download directory or a CCS_BROWSER_DOWNLOAD_ROOTS entry'
1562
+ );
1563
+ }
1564
+ assertNoSensitiveLocalPathSegments(candidatePath, containingRoot, 'downloadPath');
1565
+
1566
+ fs.mkdirSync(resolvedPath, { recursive: true });
1567
+ const realDownloadPath = fs.realpathSync(resolvedPath);
1568
+ if (!isPathInsideRoot(realDownloadPath, containingRoot)) {
1569
+ throw new Error('downloadPath cannot traverse outside the allowed download root');
1570
+ }
1571
+ fs.accessSync(realDownloadPath, fs.constants.W_OK);
1572
+ return realDownloadPath;
1445
1573
  }
1446
1574
 
1447
1575
  function pushRecentDownload(entry) {
@@ -2418,16 +2546,35 @@ function buildFileInputHandleExpression(selector, nth, frameSelector, pierceShad
2418
2546
  }
2419
2547
 
2420
2548
  function validateLocalFiles(files) {
2549
+ if (files.length > MAX_LOCAL_TRANSFER_FILES) {
2550
+ throw new Error(`files exceeds maximum of ${MAX_LOCAL_TRANSFER_FILES}`);
2551
+ }
2552
+
2553
+ const safeRoots = getUploadSafeRoots().map(resolveExistingRoot);
2421
2554
  return files.map((filePath) => {
2422
2555
  const resolvedPath = path.resolve(filePath);
2423
2556
  if (!fs.existsSync(resolvedPath)) {
2424
2557
  throw new Error(`file does not exist: ${resolvedPath}`);
2425
2558
  }
2426
- const stat = fs.statSync(resolvedPath);
2559
+ const realFilePath = fs.realpathSync(resolvedPath);
2560
+ const containingRoot = findContainingRoot(realFilePath, safeRoots);
2561
+ if (!containingRoot) {
2562
+ throw new Error(
2563
+ 'file must be inside the browser session download directory or a CCS_BROWSER_UPLOAD_ROOTS entry'
2564
+ );
2565
+ }
2566
+ assertNoSensitiveLocalPathSegments(realFilePath, containingRoot, 'file');
2567
+
2568
+ const stat = fs.statSync(realFilePath);
2427
2569
  if (!stat.isFile()) {
2428
- throw new Error(`file is not a regular file: ${resolvedPath}`);
2570
+ throw new Error(`file is not a regular file: ${realFilePath}`);
2571
+ }
2572
+ if (stat.size > MAX_LOCAL_TRANSFER_FILE_BYTES) {
2573
+ throw new Error(
2574
+ `file exceeds maximum size of ${MAX_LOCAL_TRANSFER_FILE_BYTES} bytes: ${realFilePath}`
2575
+ );
2429
2576
  }
2430
- return resolvedPath;
2577
+ return realFilePath;
2431
2578
  });
2432
2579
  }
2433
2580
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "7.78.0-dev.5",
3
+ "version": "7.78.0-dev.7",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more",
5
5
  "keywords": [
6
6
  "cli",