Showing the changes to apps/server/src/workspace/WorkspaceFileSystem.test.ts (1 of 20 files). Show all
Unified · Split
| @@ -14,6 +14,7 @@ import * as VcsProcess from "../vcs/VcsProcess.ts"; · 13 unmodified lines | ||
| 14 | 14 | import * as WorkspaceEntries from "./WorkspaceEntries.ts"; |
| 15 | 15 | import * as WorkspaceFileSystem from "./WorkspaceFileSystem.ts"; |
| 16 | 16 | import * as WorkspacePaths from "./WorkspacePaths.ts"; |
| 17 | +import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; | |
| 17 | 18 | |
| 18 | 19 | const ProjectLayer = WorkspaceFileSystem.layer.pipe( |
| 19 | 20 | Layer.provide(WorkspacePaths.layer), |
| @@ -138,34 +139,36 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (i · 118 unmodified lines | ||
| 138 | 139 | }), |
| 139 | 140 | ); |
| 140 | 141 | |
| 141 | − it.effect("rejects symlinks that resolve outside the workspace root", () => | |
| 142 | − Effect.gen(function* () { | |
| 143 | − const workspaceFileSystem = yield* WorkspaceFileSystem.WorkspaceFileSystem; | |
| 144 | − const fileSystem = yield* FileSystem.FileSystem; | |
| 145 | − const path = yield* Path.Path; | |
| 146 | − const cwd = yield* makeTempDir; | |
| 147 | − const outsideDir = yield* makeTempDir; | |
| 148 | − yield* writeTextFile(outsideDir, "secret.txt", "outside\n"); | |
| 149 | − yield* fileSystem.symlink( | |
| 150 | − path.join(outsideDir, "secret.txt"), | |
| 151 | − path.join(cwd, "linked-secret.txt"), | |
| 152 | − ); | |
| 153 | − | |
| 154 | − const error = yield* workspaceFileSystem | |
| 155 | − .readFile({ cwd, relativePath: "linked-secret.txt" }) | |
| 156 | − .pipe(Effect.flip); | |
| 157 | − const resolvedWorkspaceRoot = yield* fileSystem.realPath(cwd); | |
| 158 | − const resolvedPath = yield* fileSystem.realPath(path.join(outsideDir, "secret.txt")); | |
| 159 | − | |
| 160 | − expect(error).toBeInstanceOf(WorkspaceFileSystem.WorkspaceFilePathEscapeError); | |
| 161 | − expect(error).toMatchObject({ | |
| 162 | − workspaceRoot: cwd, | |
| 163 | − relativePath: "linked-secret.txt", | |
| 164 | − resolvedWorkspaceRoot, | |
| 165 | − resolvedPath, | |
| 166 | − }); | |
| 167 | − expect("cause" in error).toBe(false); | |
| 168 | − }), | |
| 142 | + it.effect.skipIf(!symlinksSupported)( | |
| 143 | + "rejects symlinks that resolve outside the workspace root", | |
| 144 | + () => | |
| 145 | + Effect.gen(function* () { | |
| 146 | + const workspaceFileSystem = yield* WorkspaceFileSystem.WorkspaceFileSystem; | |
| 147 | + const fileSystem = yield* FileSystem.FileSystem; | |
| 148 | + const path = yield* Path.Path; | |
| 149 | + const cwd = yield* makeTempDir; | |
| 150 | + const outsideDir = yield* makeTempDir; | |
| 151 | + yield* writeTextFile(outsideDir, "secret.txt", "outside\n"); | |
| 152 | + yield* fileSystem.symlink( | |
| 153 | + path.join(outsideDir, "secret.txt"), | |
| 154 | + path.join(cwd, "linked-secret.txt"), | |
| 155 | + ); | |
| 156 | + | |
| 157 | + const error = yield* workspaceFileSystem | |
| 158 | + .readFile({ cwd, relativePath: "linked-secret.txt" }) | |
| 159 | + .pipe(Effect.flip); | |
| 160 | + const resolvedWorkspaceRoot = yield* fileSystem.realPath(cwd); | |
| 161 | + const resolvedPath = yield* fileSystem.realPath(path.join(outsideDir, "secret.txt")); | |
| 162 | + | |
| 163 | + expect(error).toBeInstanceOf(WorkspaceFileSystem.WorkspaceFilePathEscapeError); | |
| 164 | + expect(error).toMatchObject({ | |
| 165 | + workspaceRoot: cwd, | |
| 166 | + relativePath: "linked-secret.txt", | |
| 167 | + resolvedWorkspaceRoot, | |
| 168 | + resolvedPath, | |
| 169 | + }); | |
| 170 | + expect("cause" in error).toBe(false); | |
| 171 | + }), | |
| 169 | 172 | ); |
| 170 | 173 | |
| 171 | 174 | it.effect("rejects directories without manufacturing an I/O cause", () => |