소스 검색

fix: make studio truth paths cross-platform

fanghanjun 6 달 전
부모
커밋
828d445fcb
1개의 변경된 파일과 6개의 추가작업 그리고 8개의 파일을 삭제
  1. 6 8
      packages/studio/src/api/server.ts

+ 6 - 8
packages/studio/src/api/server.ts

@@ -42,7 +42,7 @@ import {
   type LogEntry,
 } from "@actalk/inkos-core";
 import { access, readFile, readdir, writeFile } from "node:fs/promises";
-import { join } from "node:path";
+import { isAbsolute, join, relative, resolve } from "node:path";
 import { isSafeBookId } from "./safety.js";
 import { ApiError } from "./errors.js";
 import { buildStudioBookConfig } from "./book-create.js";
@@ -973,7 +973,7 @@ export function createStudioServer(initialConfig: ProjectConfig, root: string) {
    */
   function resolveTruthFilePath(bookDir: string, file: string): string | null {
     // Reject absolute paths, traversal, null bytes outright.
-    if (!file || file.includes("\0") || file.startsWith("/") || file.includes("..")) {
+    if (!file || file.includes("\0") || isAbsolute(file) || file.includes("..")) {
       return null;
     }
 
@@ -988,12 +988,10 @@ export function createStudioServer(initialConfig: ProjectConfig, root: string) {
 
     if (!allowed) return null;
 
-    const storyDir = join(bookDir, "story");
-    const resolved = join(storyDir, file);
-    // Path safety: resolved absolute path must sit inside storyDir. `join`
-    // collapses any `..` segments, so compare against the storyDir prefix.
-    const storyPrefix = storyDir.endsWith("/") ? storyDir : storyDir + "/";
-    if (!resolved.startsWith(storyPrefix) && resolved !== storyDir) {
+    const storyDir = resolve(bookDir, "story");
+    const resolved = resolve(storyDir, file);
+    const relativePath = relative(storyDir, resolved);
+    if (relativePath === "" || relativePath.startsWith("..") || isAbsolute(relativePath)) {
       return null;
     }
     return resolved;