diffx-cli 0.7.1 → 0.8.0

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/dist/cli.mjs +24 -5
  2. package/package.json +2 -1
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { parseArgs } from "node:util";
3
3
  import { fileURLToPath } from "node:url";
4
- import { basename, dirname, extname, join, resolve } from "node:path";
4
+ import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
5
5
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import getPort from "get-port";
7
7
  import { execSync } from "node:child_process";
@@ -10,6 +10,21 @@ import { readFile } from "node:fs/promises";
10
10
  import { Hono } from "hono";
11
11
  import { serve } from "@hono/node-server";
12
12
  import { homedir } from "node:os";
13
+ //#region src/path.ts
14
+ function decodeAndNormalize(p) {
15
+ let decoded = p;
16
+ try {
17
+ decoded = decodeURIComponent(p);
18
+ } catch {}
19
+ return decoded.replace(/\\/g, "/");
20
+ }
21
+ function isSafePath(relativePath, baseDir) {
22
+ const normalized = decodeAndNormalize(relativePath);
23
+ if (normalized.includes("..") || normalized.includes("\0") || isAbsolute(normalized)) return false;
24
+ const resolved = resolve(baseDir, normalized);
25
+ return resolved.startsWith(resolve(baseDir) + "/") || resolved === resolve(baseDir);
26
+ }
27
+ //#endregion
13
28
  //#region src/git.ts
14
29
  function isBinaryFile(absolutePath) {
15
30
  try {
@@ -23,8 +38,10 @@ function isBinaryFile(absolutePath) {
23
38
  }
24
39
  function getFileContent(filePath, version) {
25
40
  const root = getRepoRoot();
41
+ if (!isSafePath(filePath, root)) return null;
42
+ const resolved = resolve(root, filePath);
26
43
  if (version === "new") try {
27
- return readFileSync(join(root, filePath));
44
+ return readFileSync(resolved);
28
45
  } catch {
29
46
  return null;
30
47
  }
@@ -333,7 +350,9 @@ function createApp(clientDir, customDiffArgs, commentStore) {
333
350
  app.get("/*", async (c) => {
334
351
  let filePath = c.req.path;
335
352
  if (filePath === "/") filePath = "/index.html";
336
- const fullPath = join(clientDir, filePath);
353
+ const relativePath = filePath.slice(1);
354
+ if (!isSafePath(relativePath, clientDir)) return c.text("Forbidden", 403);
355
+ const fullPath = resolve(clientDir, relativePath);
337
356
  try {
338
357
  const content = await readFile(fullPath);
339
358
  const contentType = MIME_TYPES[extname(fullPath)] || "application/octet-stream";
@@ -382,7 +401,7 @@ if (values.help) {
382
401
  Usage: diffx [options] [-- <git diff args>]
383
402
 
384
403
  Options:
385
- -p, --port <port> Port to run the server on (default: 3433)
404
+ -p, --port <port> Port to run the server on (default: random available port)
386
405
  --no-open Don't open the browser automatically
387
406
  -v, --version Show version number
388
407
  -h, --help Show this help message
@@ -405,7 +424,7 @@ if (!isGitRepo()) {
405
424
  console.error("Error: not inside a git repository");
406
425
  process.exit(1);
407
426
  }
408
- const port = await getPort({ port: values.port ? parseInt(values.port, 10) : 3433 });
427
+ const port = await getPort(values.port ? { port: parseInt(values.port, 10) } : void 0);
409
428
  const clientDir = resolve(dirname(fileURLToPath(import.meta.url)), "client");
410
429
  const { existsSync } = await import("node:fs");
411
430
  const { port: actualPort } = await startServer({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffx-cli",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Local code review tool for git diffs with a GitHub PR-like web UI",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,6 +33,7 @@
33
33
  "open": "^11.0.0"
34
34
  },
35
35
  "devDependencies": {
36
+ "@changesets/changelog-git": "^0.2.1",
36
37
  "@changesets/cli": "^2.30.0",
37
38
  "@pierre/diffs": "^1.1.11",
38
39
  "@types/react": "^19.1.2",